:root {
  color-scheme: light;
  /* 全站普通字号 = 最小字号，对应「全部选择」这类按钮的字；所有加粗、加大都在它之上。 */
  --font-base: 14px;
  /* 字体照 LibTV（2026-10-12，用户：「所有的字体都和libtv保持一致」）：字体栈、Inter 都在 fonts.css，每张页面最先加载它；这里别再写死字体。 */
  font-family: var(--font-text);
  font-size: var(--font-base);
  /* ---- 语义化变量：以后改主题只动这一段 ---- */
  /* 全站只有一个强调色：青蓝（照 liblib.tv 的品牌色 #09caf5，用户 2026-09-15 定的；原来是紫）。
     主按钮、选中态、焦点、链接都用它；浅底上 #09caf5 对比度不够，浅色主题用深一档的 #05a3c5。 */
  --accent: #0099e6;             /* 交互强调色：主按钮、选中态、焦点（鲜艳的青蓝，用户 2026-09-15 定的） */
  --accent-strong: #0088d1;
  --accent-2: #2f7cff;           /* 渐变的另一端 */
  --accent-soft: rgba(0, 153, 230, .14);
  --accent-text: #0088d1;
  --gradient-primary: linear-gradient(90deg, #0099e6 0%, #1a8cf0 55%, #2f7cff 100%);
  /* 积分那枚闪电一直是原来的紫（用户 2026-09-15：「积分 icon 保持之前的颜色不变」），不跟强调色走。 */
  --credits-icon-color: #5b48f0;
  --radius-sm: 8px;              /* 按钮、输入框 */
  --radius-md: 10px;             /* 列表行、小块 */
  --radius-lg: 14px;             /* 卡片、面板 */
  --radius-xl: 22px;             /* 弹窗、大面板 */
  --control-height: 36px;
  --shadow-1: 0 6px 18px rgba(20, 30, 26, .14);
  --shadow-2: 0 24px 70px rgba(20, 30, 26, .22);
  --glass-bg: rgba(255, 255, 255, .72);
  --glass-line: rgba(0, 0, 0, .08);
  --bg: #f4f5f5;
  /* 侧边栏的底：和页面分得开、又不抢（用户 2026-09-21）。浅色主题往深走一档，深色主题往亮走一档。 */
  --sidebar-bg: #eff1f0;
  /* 侧栏导航每一行的字和图标（用户 2026-10-12 嫌侧栏「看起来很暗」，照 LibTV 一律最亮那一档）：浅色主题就是正文色，深色主题纯白 */
  --sidebar-text: var(--text);
  --surface: #ffffff;
  /* 滑轨页签的背景槽，全站七条共用这一档。原来是半透明的黑（把身下那层压暗一档），
     压在页面底 --bg 上和页面几乎同色，换成了实心浅灰：比面板 / 页面 / 弹窗三种底都深一截。
     **不许各处自己写死**，要调就调这里。 */
  --tab-rail-strong: #e4e9e7;
  --line: #d7dcda;
  --line-strong: #b8c0bd;
  --text: #18201d;
  --muted: #66706c;
  /* **--green 这个名字留着是因为一百多处引用，它现在就是强调色（青蓝）**，不再是绿：选中态、勾选描边、页签、
     勾选框都走它（用户 2026-09-15：「用了大量的绿色，我很不喜欢」）。真正的「成功 / 已完成」用下面的 --success。 */
  --green: var(--accent);
  --green-soft: var(--accent-soft);
  --green-selected: #c5e6ef;   /* 选中的分集行：比 --green-soft 深一档，压在面板底上看得出来 */
  --success: #1f9d55;
  --success-soft: #e3f5ea;
  --orange: #98601a;
  --orange-soft: #fff1dc;
  /* Minimo3D 的入口色（用户 2026-09-25：「Minimo3D的入口给一个别的颜色」）：琥珀。**只给 Minimo3D 的入口用**
     （侧栏那颗、首页那张卡、页头的标），页面里面的按钮、选中态照旧走全站的青蓝，不另起一套。 */
  --model3d-color: #d97706;
  --model3d-soft: rgba(217, 119, 6, .14);
  --model3d-text: #b45309;
  --red: #9f3535;
  --red-soft: #fbe7e7;
  --blue: #2a5f92;
  --blue-soft: #e7f0fa;
  /* 排队中：运行中那个蓝和行底色对半调出来的，比运行中浅一档。 */
  --blue-faint: #ecf1f6;
  --gray-soft: #ecefee;
  --input-bg: #ffffff;
  /* 控制台里能填的框：比所在面板 / 卡片沉一层，浅色下就是白底压在浅灰卡片上。 */
  --console-input-bg: #ffffff;
  --console-input-line: #8f9a95;
  --surface-alt: #fafbfb;
  --surface-soft: #f0f2f1;
  --surface-muted: var(--surface-soft);
  --surface-hover: #f8faf9;
  --command-neutral-bg: #e4e9e7;
  --command-generate-bg: #cdeef6;
  --command-generate-text: #05677c;
  /* 生成类按钮（剧本、分集、正文、提示词、图片、视频）：浅色下黑底白字，深色下反过来（用户 2026-09-12）。
     和「掏钱 / 落点主按钮」同一个 #202124。选中的页签、控制台主按钮这些不是生成的，照旧用上面那档生成绿。 */
  --command-create-bg: #202124;
  --command-create-text: #ffffff;
  --control-hover: #f8f9f9;
  --control-hover-border: #76827d;
  --primary-bg: var(--accent-strong);
  --primary-hover: var(--accent);
  --on-primary: #ffffff;
  --header-bg: #202724;
  --header-border: #cf8a32;
  --header-muted: #dce2df;
  --header-control-border: #74807b;
  --queue-running-border: #73a6d7;
  --queue-running-text: #cde5fa;
  --queue-pausing-border: #e3aa5d;
  --queue-pausing-text: #ffe2b9;
  --progress-track: #45504c;
  --progress-value: #52a77f;
  --readonly-text: #46504c;
  --focus-ring: rgba(111, 92, 255, .16);
  --stats-bg: #edf0ef;
  --warning-border: #e7c38d;
  --warning-text: #684212;
  --cell-line: #e3e6e5;
  --table-header-bg: #f0f2f1;
  --table-header-text: #515c58;
  --resizer: #aeb7b3;
  --reference-missing-border: #8d9893;
  --reference-missing-text: #59645f;
  --image-index-bg: rgba(24, 32, 29, .82);
  --preview-border: #75807c;
  --preview-bg: #202724;
  --preview-shadow: rgba(20, 30, 26, .32);
  --conflict-text: #59635f;
  --type-bg: #edf0ef;
  --type-text: #46504c;
  --generate-border: #6fb9cc;
  --dialog-shadow: rgba(20, 30, 26, .25);
  --dialog-backdrop: rgba(15, 22, 19, .52);
  /* --toast-error stays dark: the service banner still uses it. */
  --toast-error: #7f2929;
  --toast-bg: #ffffff;
  --toast-text: #18201d;
  --toast-border: #e8e8e8;
  /* 报错那条照 New API 的配法：底色里掺 16% 的红、描边掺 35%、字就是那个红。红取它两套主题里的值；
     三个派生色写在 :root 上，深色只换 --toast-danger、底色和描边，跟着重算。 */
  --toast-danger: var(--danger);
  --toast-error-bg: color-mix(in oklch, var(--toast-danger) 16%, var(--toast-bg));
  --toast-error-text: var(--toast-danger);
  --toast-error-border: color-mix(in oklch, var(--toast-danger) 35%, var(--toast-border));
  letter-spacing: 0;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --accent: #00c2ff;
  --accent-strong: #00b0ec;
  --accent-2: #2f7cff;
  --accent-soft: rgba(0, 194, 255, .2);
  --accent-text: #3ad1ff;
  --gradient-primary: linear-gradient(90deg, #00c2ff 0%, #1aa0ff 55%, #2f7cff 100%);
  --credits-icon-color: #7b6cff;
  --shadow-1: 0 6px 18px rgba(0, 0, 0, .28);
  --shadow-2: 0 24px 70px rgba(0, 0, 0, .4);
  --glass-bg: rgba(20, 22, 30, .62);
  --glass-line: rgba(255, 255, 255, .08);
  --bg: #141414;
  --sidebar-bg: #181818;
  --sidebar-text: #ffffff;
  /* 深色四档（用户 2026-09-12 按参考图定的）：页面 #141414 → 面板 #212121 → 条目 / 输入框 #2a2a2a → 悬浮 #303030。
     靠底色分层，描边退成 #2e2e2e 那种若隐若现的一道。原来面板是 #161616，只比页面亮 2/255，
     层次全靠 #474747 的亮描边硬撑，看着又闷又花。 */
  --surface: #212121;
  /* 深色下的「浅灰」是比页面 #141414 亮出来的灰。**两套主题是朝相反方向离开页面底色的**：
     浅色主题的页面本来就接近白，槽要比它深一点才看得见；深色主题反过来，要比它亮。
     槽压在条目 #2a2a2a 上也得看得见，所以比悬浮那档再亮一截。 */
  --tab-rail-strong: #404040;
  --line: #2e2e2e;
  --line-strong: #3d3d3d;
  --text: #f2f2f2;
  --muted: #b3b3b3;
  --green: var(--accent);
  --green-soft: var(--accent-soft);
  --green-selected: #0f4453;
  --success: #2ecc71;
  --success-soft: #16382a;
  --orange: #f0bd72;
  --orange-soft: #3a2c19;
  --model3d-color: #fbbf24;
  --model3d-soft: rgba(251, 191, 36, .16);
  --model3d-text: #fcd34d;
  --red: #ff9f9f;
  --red-soft: #3b2224;
  --blue: #91c9ff;
  --blue-soft: #1c3042;
  --blue-faint: #1e2830;
  --gray-soft: #2a2a2a;
  --input-bg: #2a2a2a;
  /* 深色下 --input-bg 和 --surface-soft 是同一个 #2a2a2a，框贴在卡片上就消失了：控制台的框再沉一档。 */
  --console-input-bg: #181818;
  --console-input-line: #6f6f6f;
  --surface-alt: #1c1c1c;
  --surface-soft: #2a2a2a;
  --surface-hover: #303030;
  /* 普通按钮要比条目 #2a2a2a 亮，不然贴在条目上消失。 */
  --command-neutral-bg: #333333;
  --command-generate-bg: #0d4f60;
  --command-generate-text: #c9f1fb;
  --command-create-bg: #ffffff;
  --command-create-text: #202124;
  --control-hover: #303030;
  --control-hover-border: #7a7a7a;
  --primary-bg: var(--accent-strong);
  --primary-hover: var(--accent);
  --on-primary: #ffffff;
  --header-bg: #141414;
  --header-border: #d59643;
  --header-muted: #d5ded9;
  --header-control-border: #2e2e2e;
  --queue-running-border: #6fa5d5;
  --queue-running-text: #cce5fb;
  --queue-pausing-border: #dba45c;
  --queue-pausing-text: #ffe0b5;
  --progress-track: #333333;
  --progress-value: #58bd8c;
  --readonly-text: #cfcfcf;
  --focus-ring: rgba(124, 108, 255, .22);
  --stats-bg: #1c1c1c;
  --warning-border: #6b522f;
  --warning-text: #ffdda5;
  --cell-line: #2e2e2e;
  --table-header-bg: #2a2a2a;
  --table-header-text: #cfcfcf;
  --resizer: #3d3d3d;
  --reference-missing-border: #8a8a8a;
  --reference-missing-text: #c4c4c4;
  --image-index-bg: rgba(5, 8, 7, .84);
  --preview-border: #2e2e2e;
  --preview-bg: #212121;
  --preview-shadow: rgba(0, 0, 0, .48);
  --conflict-text: #c9c9c9;
  --type-bg: #2a2a2a;
  --type-text: #d6d6d6;
  --generate-border: #2f8ea6;
  --dialog-shadow: rgba(0, 0, 0, .5);
  --dialog-backdrop: rgba(3, 6, 5, .72);
  --toast-error: #7f3033;
  /* Lifted a step above --surface so a bubble reads as floating, not as page. */
  --toast-bg: #2a2a2a;
  --toast-text: #f2f2f2;
  --toast-border: rgba(255, 255, 255, .1);
}

* { box-sizing: border-box; }

/* 根上原来有一条 scrollbar-gutter: stable（给整页滚动条预留 15px）。可这张页面的根从来不滚——body 是 overflow: hidden，
   该滚的都在各自的框里滚——那 15px 就成了整页右边一条永远空着的缝，平时和页面底色一样看不出来；顶部横幅有了颜色之后，
   它铺不到那条缝里，右端就露出一截（用户 2026-09-20：「顶部的横幅，在右侧没有完全贴边」）。预留位里只画得进画布的底色，
   任何元素都伸不进去，所以只能撤掉预留。框里自己的那几条 scrollbar-gutter（资产库、时间线）不受影响。 */

body {
  margin: 0;
  min-width: 320px;
  background: var(--bg);
  color: var(--text);
}

button, input, select, textarea { font: inherit; letter-spacing: 0; }
small, sub, sup { font-size: var(--font-base); }

button, .button-link {
  min-height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface);
  color: var(--text);
  padding: 7px 12px;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

/* 按下：往下沉 1 像素再缩到 .98（照 New API，用户 2026-09-28；原来是缩到 .96）。悬浮、聚焦的样子见文件末尾「按钮与弹窗」那一块。
   用独立的 translate / scale 属性，不碰元素自己的 transform——原来的 transform: scale 会盖掉「用 transform 居中」的元素，一按就跳。
   **按下效果只给独立的操作按钮**（用户 2026-09-10 定的），**点名列入，不是点名排除**（2026-10-07 改的）：只认下面这串——
   没类名的按钮（Button 组件的次要款）、主按钮、危险、图标、生成、链接、弹窗底栏的按钮，和「按钮与弹窗」那一块形状那串对齐，
   再加几颗自己起了类名的独立按钮。行、卡片、菜单项、下拉选项、页签、导航一律不在里面（点了动一下像整块面板在抖）。
   原来是「所有 button 都缩，再一个个点名排除」：排除名单攒到 38 个还在漏，邀请 / 设置 / 控制台另有一条整页都缩、不看排除名单的旧规则，
   设置页的行、控制台的页签都跟着缩（用户 2026-10-07：「这种栏位的按钮也会导致缩放」）。**别改回点名排除**；
   新的独立按钮用 Button 组件或这里的类名，自己起类名的加进这一串。 */
button:disabled { cursor: not-allowed; opacity: .5; }
:is(button:where(:not([class]), [class=""], [class="hidden"]), .button-link, .primary-command, .danger-command, .icon-command, .generate-command,
.model-test-button, .asset-generate, .cost-book-command, .subtab-pick-command, .prompt-command, .storyboard-delete-command, .provider-model-add,
.provider-card-remove, .metrics-refresh, .episode-manage, .studio-shot-manage, .ticket-close, .shot-action-command, .one-click-command,
.dialog-footer button, button.primary, button.danger, .cloud-account-button:not(.hover-only), .theme-toggle-button, .lang-current, .notify-button,
.nav-menu-button, .console-menu-button, .toast-close, .urgent-banner-close, .urgent-banner-action, .login-dialog-close, .billing-close, .image-viewer-close,
.image-viewer-pick, .captcha-image, .auth-submit, .auth-code-button, .auth-dialog-primary, .auth-dialog-ghost, .auth-eye, .secret-eye, .home-cta,
.home-hero-nav, .home-hero-arrow, .home-row-arrow, .buy-action, .buy-history-button, .buy-redeem-button, .billing-buy, .billing-pill, .billing-banner-cta, .orders-range, .view-button,
.console-copy-button, .console-detail-back, .notice-image-clear, .metrics-day-clear,
.provider-model-tier-remove, .ticket-new-button, .ticket-submit-button, .notify-read-all, .settings-copy, .asset-delete, .asset-drawer-close,
.asset-drawer-header-button, .asset-stale-regenerate, .rename-pen, .header-copy, .new-work-file-clear, .assistant-send, .assistant-action,
.assistant-tool, .assistant-change-dismiss, .assistant-history-manage, .assistant-history-remove, .assistant-questions-submit, .breakdown-look-redo,
.breakdown-stale-redo, .episode-redo-command, .episode-history-button, .episode-shot-type-save, .script-back, .script-beat-open,
.script-episode-edit, .script-history-restore, .script-regen, .script-send, .studio-icon-button, .studio-play-all, .studio-board-view,
.studio-shot-remove, .m3d-dock-btn, .m3d-drop-clear, .m3d-tool, .jump-command):not([role="tab"], [role="menuitem"], [role="option"]):active:not(:disabled) {
  translate: 0 1px;
  scale: .98;
}

.primary-command {
  border-color: var(--primary-bg);
  background: var(--primary-bg);
  color: var(--on-primary);
  font-weight: 650;
}

.primary-command:hover:not(:disabled) { border-color: var(--primary-hover); background: var(--primary-hover); }
.danger-command { border-color: var(--red); color: var(--red); font-weight: 650; }
/* 带 i 的按钮：文字和记号在一条基线上。 */
button:has(> .info-dot) { display: inline-flex; align-items: center; justify-content: center; }
.danger-command:hover:not(:disabled) { border-color: var(--red); background: var(--red-soft); }

.app-header {
  position: sticky;
  top: 0;
  /* 层级：顶栏 80 < 资产库遮罩 85 < 资产库抽屉 90 < 横幅提示 95。抽屉打开时顶栏整个被盖住。 */
  z-index: 80;
  display: grid;
  grid-template-areas: "brand modes tools";
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
  align-items: center;
  gap: 20px;
  padding: 12px 24px;
  /* 顶栏不再自成一块：和正文同底色，也不用那条分隔线压边。 */
  background: var(--bg);
  color: var(--on-primary);
}

.header-brand { grid-area: brand; display: flex; min-width: 0; align-items: center; gap: 14px; }
.header-brand .pick-project { flex: none; }
.header-brand .queue-badge { display: none; }
.app-header h1 { margin: 0; font-size: 26px; line-height: 1.15; font-weight: 700; letter-spacing: .5px; }

.queue-badge {
  border: 1px solid var(--header-control-border);
  border-radius: 4px;
  padding: 3px 7px;
  color: var(--header-muted);
  font-size: var(--font-base);
}

.queue-badge.running { border-color: var(--queue-running-border); color: var(--queue-running-text); }
.queue-badge.pausing { border-color: var(--queue-pausing-border); color: var(--queue-pausing-text); }
.queue-badge.failed { border-color: var(--red); color: var(--red); }

.mode-switch {
  grid-area: modes;
  display: grid;
  width: 640px;
  max-width: 100%;
  justify-self: center;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--header-control-border);
  border-radius: 999px;
  background: rgba(0, 0, 0, .28);
}
.mode-switch .mode-card {
  --mode-accent: #68d4a1;
  --mode-accent-soft: rgba(73, 195, 139, .12);
  display: grid;
  min-width: 0;
  height: 60px;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto 4px;
  row-gap: 4px;
  align-items: center;
  border: 0;
  border-radius: 999px;
  padding: 6px 10px;
  background: transparent;
  color: var(--header-muted);
  text-align: left;
}
.mode-switch .mode-card:not(.active):hover:not(:disabled) { border: 0; background: rgba(255, 255, 255, .09); }
.mode-switch .mode-card.active {
  border: 0;
  background: rgba(104, 212, 161, .24);
}
.mode-switch .mode-card:focus-visible { outline: 2px solid var(--mode-accent); outline-offset: 2px; }
/* 图标在左、文字在右，整对居中。文字自己那一层收省略号——直接在 flex 容器上收，
   收的是整对，图标会先被挤掉。 */
.mode-card-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  overflow: hidden;
  color: #ffffff;
  font-size: 14px;
  font-weight: 680;
  text-align: center;
  white-space: nowrap;
}
.mode-card-label { overflow: hidden; text-overflow: ellipsis; }
/* 四个图标是白色带透明通道的 PNG，当**遮罩**用而不是直接当图片：形状取 alpha，
   颜色取 currentColor，所以选中 / 未选中跟着字色走，将来换主题也不用另出一版图。 */
.mode-card-icon {
  flex: none;
  width: 18px;
  height: 18px;
  background-color: currentColor;
}
.mode-card-icon[data-icon="script"] { -webkit-mask: url("/static/icon-mode-script.png") center / contain no-repeat; mask: url("/static/icon-mode-script.png") center / contain no-repeat; }
.mode-card-icon[data-icon="storyboard"] { -webkit-mask: url("/static/icon-mode-storyboard.png") center / contain no-repeat; mask: url("/static/icon-mode-storyboard.png") center / contain no-repeat; }
.mode-card-icon[data-icon="image"] { -webkit-mask: url("/static/icon-mode-image.png") center / contain no-repeat; mask: url("/static/icon-mode-image.png") center / contain no-repeat; }
.mode-card-icon[data-icon="video"] { -webkit-mask: url("/static/icon-mode-video.png") center / contain no-repeat; mask: url("/static/icon-mode-video.png") center / contain no-repeat; }
/* 进度条：横向圆角胶囊。已完成部分走青绿渐变（青蓝 → 薄荷绿），未完成是深灰蓝，
   两段之间不描边、自然衔接。**不加 overflow: hidden**——那会把下面那层外发光一起
   裁掉；填充自己是全圆角的，不靠裁剪也不会露出方角。 */
.mode-card-progress {
  height: 4px;
  grid-column: 1 / -1;
  border-radius: 999px;
  background: #242b35;
}
.mode-card-progress > span {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #20b8d0 0%, #55ddb5 100%);
  /* 很轻的一层，深色底上有点层次就够，不要做成霓虹。 */
  box-shadow: 0 0 6px rgba(56, 200, 190, .32);
  /* 淡出是收尾动画的第二段：补到 100% 之后淡掉，再无过渡地归零。 */
  transition: width .2s ease, opacity .28s ease;
}

/* 右上角那一排的间距只有这一个数（各档屏宽在「屏幕适配」里改它），每一颗自己不加外边距。
   用户 2026-10-08 先要「从账户按钮往左，每个按钮的间距都要保持一致」，接着「距离可以再缩短一倍」：18 → 9，窄屏几档跟着减半（5 / 4 / 3）。 */
.header-tools { grid-area: tools; align-self: start; display: flex; min-width: 0; align-items: center; justify-content: flex-end; gap: 9px; }
/* 顶栏账户按钮就是一颗积分药丸：闪电加数值，点开才是账号详情。
   连接状态不再用小圆点，断线时整颗变红——一个元素只说一件事。 */
.cloud-account-menu-wrap { position: relative; flex: none; }

/* 2026-10-07 方案一「头像积分胶囊」（用户从四个方案里选的，演示站在 Assets\账户按钮方案-1007）：闪电积分 + 右侧一枚 26px 小头像，
   和右上角那一排同一身（34 高、描边、半透明底）。会员不再整颗铺渐变，头像外一圈品牌渐变环 + 右下角一颗钻石。 */
.cloud-account-button {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 34px;
  min-height: 34px;
  border: 1px solid var(--header-control-border);
  border-radius: 12px;
  background: rgba(0, 0, 0, .28);
  color: var(--on-primary);
  padding: 0 4px 0 11px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
}
.account-button-avatar { position: relative; flex: none; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: rgba(255, 255, 255, .1); }
:root:not([data-theme="dark"]) .account-button-avatar { background: var(--surface-soft); }
.account-button-face {
  position: relative;
  z-index: 1;
  width: 14px;
  height: 14px;
  background: currentColor;
  opacity: .85;
  -webkit-mask: var(--icon-account) no-repeat center / contain;
  mask: var(--icon-account) no-repeat center / contain;
}
/* 会员：头像底换成品牌渐变，再压一圈按钮底色，露出来的 2px 就是渐变环；右下角的钻石垫一圈按钮底色，像嵌在环上。 */
.cloud-account-button.member .account-button-avatar { background: linear-gradient(135deg, #19c3ff 0%, #2f7cff 55%, #8b5cf6 100%); }
.cloud-account-button.member .account-button-avatar::before { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: #1c1f24; }
:root:not([data-theme="dark"]) .cloud-account-button.member .account-button-avatar::before { background: var(--surface-soft); }
.account-button-badge { position: absolute; right: -3px; bottom: -3px; z-index: 2; display: none; place-items: center; width: 13px; height: 13px; border-radius: 50%; background: #1c1f24; }
:root:not([data-theme="dark"]) .account-button-badge { background: var(--surface); }
.cloud-account-button.member .account-button-badge { display: grid; }
.account-button-diamond { width: 10px; height: 10px; background: url("/static/icon-tier-member.png") no-repeat center / contain; }

/* 悬浮和平时一个样子（全站按钮规矩）。原来悬浮换绿边、提亮底色，是老绿色主题留下的。 */
.cloud-account-button:hover:not(:disabled) {
  border-color: var(--header-control-border);
  background: rgba(0, 0, 0, .28);
}

.cloud-account-button.offline { border-color: #d97b7b; color: #ffb0b0; }
.cloud-account-button.loading { color: var(--header-muted); cursor: progress; }
/* 登录之后这颗不可点（用户 2026-09-13）：面板只靠悬浮出，鼠标是普通箭头、按下也不缩（顶部 button:active 的排除名单里有 .hover-only）。 */
.cloud-account-button.hover-only { cursor: default; }


.cloud-account-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  /* 2026-09-22 整体缩了一档（用户：「太大了，整体缩小一点」）：348 → 300 宽，里面的头像、积分卡、行高、字号一起按比例收，排版没动。 */
  width: min(300px, calc(100vw - 24px));
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .4);
  font-family: inherit;
  font-size: var(--font-base);
  font-weight: 500;
  line-height: 1.5;
  /* 开合过渡（用户 2026-09-13）：淡入 + 从上方 6px 落下来；open 由 setCloudAccountMenuOpen 挂，时长和 ACCOUNT_MENU_FADE_MS 一致。 */
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .18s ease, transform .18s cubic-bezier(.22, .61, .36, 1);
}
.cloud-account-menu.open { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .cloud-account-menu { transition: none; } }

/* 悬浮打开：按钮和面板之间隔着 8px 的缝，鼠标穿过去会先离开再进入。盖一块透明的桥，
   鼠标走在缝上也算还在面板里。 */
.cloud-account-menu::before {
  content: "";
  position: absolute;
  top: -10px;
  right: 0;
  left: 0;
  height: 10px;
}

.cloud-account-menu-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  align-items: center;
  gap: 11px;
  height: 42px;
  padding: 0 12px;
  border-radius: 6px;
  color: var(--muted);
}
.cloud-account-menu-row:hover { background: var(--surface-muted); color: var(--text); }
.cloud-account-menu-row strong {
  width: 100%;
  min-width: 0;
  justify-self: start;
  overflow: hidden;
  color: var(--text);
  font: inherit;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ---- 账户菜单（方案一「会员卡」，用户 2026-10-05 选的；演示站在 Assets\账户面板方案-1005）----
   头部一整行点进「设置」：圆头像 + 账号名 + 右箭头；下面一张卡，上半是会员状态（钻石 +「普通用户 / 创作会员」，
   右边普通用户一颗「开通会员」、会员写有效期，都点进会员中心），下半是积分余额和「积分明细」；会员整张换成深色会员卡。
   再下面几行入口各隔一道线，最后是退出。原来账号行和积分合成一块浅底（会员整块青色渐变）、「开通会员 / 会员中心」另起一行，
   他嫌那一块「很奇怪」（2026-10-05），给了三个方案选的这一个。颜色全走主题变量，浅色主题下一样成立。 */
.cloud-account-menu-profile,
.cloud-account-menu-link {
  width: 100%;
  min-height: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.cloud-account-menu-profile {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 14px;
  align-items: center;
  gap: 10px;
  padding: 2px 6px 10px;
  border-radius: 10px;
}
/* 两行入口悬浮时铺一层浅底，和侧栏入口同一个做法。 */
.cloud-account-menu-link:hover:not(:disabled) { border: 0; background: var(--surface-muted); color: var(--text); }
/* 头部账号那一行不要悬浮样式（用户 2026-09-10 原话）：悬浮和平时一个样子，显式写平，
   免得被全局按钮规则带出底色。 */
.cloud-account-menu-profile:hover:not(:disabled) { border: 0; background: none; color: var(--text); }
.cloud-account-menu-avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-soft);
  color: var(--text);
}
.cloud-account-menu-avatar .cloud-account-menu-icon-account { width: 18px; height: 18px; -webkit-mask-size: 18px 18px; mask-size: 18px 18px; opacity: .9; }
.cloud-account-menu-identity-text { display: grid; gap: 2px; min-width: 0; }
.cloud-account-menu-name { display: flex; align-items: center; gap: 2px; min-width: 0; }
.cloud-account-menu-name strong { min-width: 0; overflow: hidden; color: var(--text); font-size: 15px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
.cloud-account-menu-chevron {
  flex: none;
  width: 14px;
  height: 14px;
  background: currentColor;
  opacity: .55;
  -webkit-mask: var(--icon-chevron-right) no-repeat center / contain;
  mask: var(--icon-chevron-right) no-repeat center / contain;
}
.cloud-account-menu-profile > .cloud-account-menu-chevron,
.cloud-account-menu-link > .cloud-account-menu-chevron { color: var(--muted); opacity: 1; }

/* 会员卡：普通用户一张素卡（--surface-soft 加一圈 --line），会员整张换成深色会员卡（深浅两种主题都是深底），
   右上一抹青光、左下一抹蓝光，描一圈淡青边。卡里只有两层：上半会员状态、下半积分，别再往里套色块
   （用户 2026-10-05：「积分余额不需要单独的背景色块」）。 */
.cloud-account-menu-card {
  margin: 0 0 6px;
  padding: 12px 14px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface-soft);
}
/* 卡下面紧跟入口行；充值记录被功能开关摘掉时，别让一道分隔线贴着卡底。 */
.cloud-account-menu-card + .cloud-account-menu-divider { display: none; }
.cloud-account-menu-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; height: 26px; }
.cloud-account-menu-tier { display: inline-flex; align-items: center; gap: 6px; min-width: 0; color: var(--text); font-size: 13px; font-weight: 600; line-height: 1; white-space: nowrap; }
/* 进会员中心的那颗：普通用户是一颗渐变小药丸「开通会员」（和套餐订阅那几颗掏钱按钮同一块渐变）；会员换成「有效期至 …」加右箭头的一句字。
   悬浮和平时同一个样子；按下不缩（面板里的入口，在顶部 button:active 那串排除名单里）。 */
.cloud-account-menu-billing,
.cloud-account-menu .cloud-account-menu-billing:hover:not(:disabled) {
  display: inline-flex; flex: none; align-items: center; gap: 2px; height: 26px; min-height: 0; margin: 0; padding: 0 12px;
  border: 0; border-radius: 999px; background: var(--gradient-primary); color: #ffffff;
  font: inherit; font-size: 12.5px; font-weight: 600; line-height: 26px; white-space: nowrap; cursor: pointer;
}
.cloud-account-menu-billing .cloud-account-menu-chevron { display: none; }
.cloud-account-menu-card.member .cloud-account-menu-billing,
.cloud-account-menu .cloud-account-menu-card.member .cloud-account-menu-billing:hover:not(:disabled) {
  padding: 0; background: none; color: rgba(255, 255, 255, .7); font-size: 12px; font-weight: 400; font-variant-numeric: tabular-nums;
}
.cloud-account-menu-card.member .cloud-account-menu-billing .cloud-account-menu-chevron { display: block; width: 12px; height: 12px; }
.cloud-account-menu-card-body { display: flex; align-items: flex-end; justify-content: space-between; gap: 8px; margin-top: 12px; }
.cloud-account-menu-credits-text { display: grid; min-width: 0; }
.cloud-account-menu-credits-text small { color: var(--muted); font-size: 12px; line-height: 1.4; }
.cloud-account-menu-credits-value { display: flex; align-items: center; gap: 6px; margin-top: 2px; }
/* 闪电照旧是紫的（用户 2026-09-13：「无论是普通用户，还是会员用户，积分的 icon 都要显示原来的那个颜色」），别在这儿覆盖 --credits-icon-color。 */
.cloud-account-menu-credits-value .cloud-account-menu-icon-credits { width: 20px; height: 20px; }
/* 余额原来写死 #fff，浅色主题下白底白字看不见；改走 --text。 */
.cloud-account-menu-balance {
  color: var(--text);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.cloud-account-menu-card.member {
  border-color: rgba(0, 194, 255, .36);
  background:
    radial-gradient(120% 130% at 100% 0%, rgba(0, 194, 255, .36), transparent 56%),
    radial-gradient(90% 120% at 0% 100%, rgba(47, 124, 255, .26), transparent 62%),
    linear-gradient(135deg, #11253c 0%, #0a1522 100%);
}
.cloud-account-menu-card.member .cloud-account-menu-tier,
.cloud-account-menu-card.member .cloud-account-menu-balance { color: #ffffff; }
.cloud-account-menu-card.member .cloud-account-menu-credits-text small { color: rgba(255, 255, 255, .66); }
/* 会员卡两种主题都是深底：闪电用深色主题那一档紫（浅色主题那档 #5b48f0 压在深底上发暗），还是原来那个紫。 */
.cloud-account-menu-card.member .cloud-account-menu-icon-credits { background: #7b6cff; }

/* 几行入口：图标 / 名字 / 右箭头，一行 36px（用户 2026-09-22：「都不需要副标题，一行就够了」；原来名字下面还有一句说明、一行 54）。 */
.cloud-account-menu-link {
  grid-template-columns: 14px minmax(0, 1fr) 14px;
  gap: 10px;
  height: 36px;
  padding: 0 10px;
  border-radius: 8px;
}
/* 图标和字一样大（用户 2026-09-22：「左侧的 icon 要和字体的尺寸要一样」）：14px，格子也收到 14。 */
.cloud-account-menu-link .cloud-account-menu-icon {
  width: 14px;
  height: 14px;
  background: currentColor;
  opacity: .9;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
/* 和侧栏一个做法：画出来的高度 = 14px 字的墨迹 13px，各张按自己的墨迹占比换算（orders 铺满、tickets .93、console .88）。
   盒子 16 放得下最高的那张；格子还是 14，多出的 2px 两边各出 1，不动文字的位置。 */
/* top 原来是 1px（图标墨迹正压在文字墨迹中心上，看着偏低；用户 2026-09-22：「充值记录 我的工单 控制台 退出 这些按钮的 icon 都要上移一点」）。
   按截图像素量：皇冠那行（他满意的）图标墨迹在文字墨迹上方 0.7px，这几行收到 top: 0 之后各在上方 0.5～1.0px，一个关系。 */
.cloud-account-menu-link .cloud-account-menu-icon { position: relative; top: 0; width: 18px; height: 18px; margin: 0 -2px; -webkit-mask-size: auto 14px; mask-size: auto 14px; }
.cloud-account-menu-icon-crown { -webkit-mask-image: var(--icon-crown); mask-image: var(--icon-crown); }
/* 各张的墨迹占比（截图量的）：皇冠 .85、工单 .93、控制台 .88；订单铺满，用上面的 14。选择器要带 .cloud-account-menu-link，
   不然打不过上面那条两级的。 */
/* 皇冠的视觉重心在下半截，按墨迹居中看着偏低，往上提 1px（用户 2026-09-22）。 */
.cloud-account-menu-link .cloud-account-menu-icon-crown { top: 0; -webkit-mask-size: auto 16.5px; mask-size: auto 16.5px; }
.cloud-account-menu-link .cloud-account-menu-icon-tickets { -webkit-mask-size: auto 15px; mask-size: auto 15px; }
.cloud-account-menu-link .cloud-account-menu-icon-console { -webkit-mask-size: auto 16px; mask-size: auto 16px; }
/* 充值记录和侧栏「我的订单」同一枚单据图标。 */
.cloud-account-menu-icon-orders { -webkit-mask-image: url("/static/icon-orders.png"); mask-image: url("/static/icon-orders.png"); }
/* 我的工单：原来侧栏那一枚。 */
.cloud-account-menu-icon-tickets { -webkit-mask-image: url("/static/icon-tickets.png?v=2"); mask-image: url("/static/icon-tickets.png?v=2"); }
/* 控制台：原来侧栏底部那一枚（2026-09-22 入口挪进面板）。 */
.cloud-account-menu-icon-console { -webkit-mask-image: url("/static/icon-console.png"); mask-image: url("/static/icon-console.png"); }
/* 设置：原来侧栏那一枚齿轮（2026-10-07 入口挪进面板，「退出」上方）。 */
.cloud-account-menu-icon-settings { -webkit-mask-image: url("/static/icon-settings.png"); mask-image: url("/static/icon-settings.png"); }
.cloud-account-menu-link-text { display: grid; min-width: 0; }
/* 四行入口的标题不加粗（用户 2026-09-22），图标和它同 14px。 */
.cloud-account-menu-link-text > span { overflow: hidden; color: var(--text); font-size: 14px; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; }
.cloud-account-menu-icon {
  flex: none;
  width: 18px;
  height: 18px;
  color: inherit;
  opacity: .85;
}
/* 卡右下角的「积分明细」：一句字加右箭头（原来是一颗小药丸，卡里再套一颗药丸显得挤）。
   悬浮和平时同一个样子；会员卡上换成半透明白字。 */
.cloud-account-menu-action,
.cloud-account-menu .cloud-account-menu-action,
.cloud-account-menu .cloud-account-menu-action:hover:not(:disabled) { display: inline-flex; flex: none; align-items: center; gap: 2px; height: 26px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; color: var(--muted); font: inherit; font-size: 12.5px; line-height: 26px; cursor: pointer; white-space: nowrap; }
.cloud-account-menu-action .cloud-account-menu-chevron { width: 12px; height: 12px; }
.cloud-account-menu-card.member .cloud-account-menu-action,
.cloud-account-menu .cloud-account-menu-card.member .cloud-account-menu-action:hover:not(:disabled) { color: rgba(255, 255, 255, .74); }
.cloud-account-menu-icon-account,
.cloud-account-menu-icon-logout {
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: 18px 18px;
  mask-size: 18px 18px;
}
.cloud-account-menu-icon-account {
  -webkit-mask-image: var(--icon-account);
  mask-image: var(--icon-account);
}
.cloud-account-menu-icon-credits {
  width: 18px;
  height: 18px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
  opacity: 1;
}
.cloud-account-menu-icon-logout {
  -webkit-mask-image: var(--icon-logout);
  mask-image: var(--icon-logout);
}
.cloud-account-menu-divider { height: 1px; margin: 4px 6px; background: var(--line); }
/* 入口被功能开关关掉之后，它上下的两道线会挨在一起：网关是把按钮整颗摘掉（两道线成了相邻兄弟），
   页面开着时管理员改开关则是给按钮挂 .hidden（线隔着一颗看不见的按钮）。两种都只留一道。 */
.cloud-account-menu-divider + .cloud-account-menu-divider,
.cloud-account-menu-row.hidden + .cloud-account-menu-divider { display: none; }
/* 和上面四行入口同一套格子（14px 图标格、10 间距、左右 10 内边距、14px / 400 的字）：
   原来格子 20、内边距 12、字重 500，图标比上面几行偏右 5px、字偏右 8px、看着也比别的行粗（用户 2026-09-22）。 */
.cloud-account-menu-logout {
  grid-template-columns: 14px minmax(0, 1fr);
  gap: 10px;
  width: 100%;
  height: 36px;
  min-height: 36px;
  padding: 0 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 400;
  text-align: left;
}
/* 退出那张按 20 画出来 17px 高（占比 .85），比旁边的字高一截；按墨迹收到 15。盒子和上面几行一样 18、两边各出 2。 */
/* 退出那一枚同上，top 1px → 0（量过：收完在文字墨迹上方 0.7px，和皇冠一样）。 */
.cloud-account-menu-logout .cloud-account-menu-icon-logout { position: relative; top: 0; width: 18px; height: 18px; margin: 0 -2px; -webkit-mask-size: auto 15px; mask-size: auto 15px; }
.cloud-account-menu-logout:hover:not(:disabled) { border: 0; background: var(--surface-muted); color: var(--text); }

/* ==================== 通知中心 ==================== */
/* 顶栏铃铛拉出右侧抽屉；侧边栏「通知」是同一份列表的整页版本。
   两处共用 .notify-tabs / .notify-list / .notify-item 这套，只是外壳不同。 */

/* ---- 顶栏铃铛 ---- */
/* 和账户按钮同一套外观，只是方的。未读时点亮并在右上角挂个红角标。 */

.notify-button {
  position: relative;
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  min-height: 34px;
  padding: 0;
  border: 1px solid var(--header-control-border);
  border-radius: 12px;
  background: rgba(0, 0, 0, .28);
  color: var(--header-muted);
}
/* 悬浮和平时一个样子；未读时铃铛字色提亮，靠右上角的红角标提醒，不再描一圈绿边（参考图里没有）。 */
.notify-button:hover:not(:disabled) { border-color: var(--header-control-border); background: rgba(0, 0, 0, .28); }
.notify-button.has-unread { color: var(--on-primary); }

/* ---- 主题切换（顶栏铃铛左边）----
   外观和铃铛一套：方形圆角、描边、半透明底；悬浮不变，按下缩小（它是独立的操作按钮）。
   图标两层：外面一圈太阳（用户给的 PNG 当遮罩）会转，中间的「半实心圆」不转——
   太阳是圆的、光线 45° 一格，转一格看着就是光线挪了一格；中间那半边是深色的标记，
   得一直是右半边实心，跟着转就歪了。 */
.theme-toggle-button {
  position: relative;
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  min-height: 34px;
  padding: 0;
  border: 1px solid var(--header-control-border);
  border-radius: 12px;
  background: rgba(0, 0, 0, .28);
  color: var(--header-muted);
}
.theme-toggle-button:hover:not(:disabled) { border-color: var(--header-control-border); background: rgba(0, 0, 0, .28); }
.theme-toggle-icon { position: relative; display: block; width: 20px; height: 20px; }
.theme-toggle-sun {
  position: absolute;
  inset: 0;
  background: currentColor;
  -webkit-mask: url("/static/icon-theme-sun.png") no-repeat center / contain;
  mask: url("/static/icon-theme-sun.png") no-repeat center / contain;
  transform: rotate(0deg);
  transition: transform .45s cubic-bezier(.22, .61, .36, 1);
}
/* 中间的圆：图里圆环内径是整枚的 32%，填到 34% 压住一点环的内沿，不留缝。深色时右半边实心。 */
.theme-toggle-core {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 34%;
  height: 34%;
  border-radius: 50%;
  background: linear-gradient(90deg, transparent 50%, currentColor 50%);
  transform: translate(-50%, -50%);
  transition: opacity .3s ease;
}
/* 浅色时中间空心。 */
:root:not([data-theme="dark"]) .theme-toggle-core { opacity: 0; }
/* 转角跟着主题走，不跟着点击次数累加（用户 2026-09-10 更正：「点一下顺时针，点一下逆时针，
   不是一直一个方向」）：深色 0°，浅色逆时针一格光线；切回深色就顺时针转回来。
   首帧脚本在样式生效前就定好了 data-theme，刷新时不会先转一下。 */
:root:not([data-theme="dark"]) .theme-toggle-sun { transform: rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .theme-toggle-sun, .theme-toggle-core { transition: none; } }
/* 浅色主题：顶栏跟着页面是浅底，原来的「半透明黑底 + 白字」压上去是一块灰、字看不清。
   换成卡片底、描边色、正文色。会员那颗是渐变、断线那颗是红色，都不动。 */
:root:not([data-theme="dark"]) .theme-toggle-button,
:root:not([data-theme="dark"]) .theme-toggle-button:hover:not(:disabled),
:root:not([data-theme="dark"]) .notify-button,
:root:not([data-theme="dark"]) .notify-button:hover:not(:disabled),
:root:not([data-theme="dark"]) .lang-switch .lang-current,
:root:not([data-theme="dark"]) .lang-switch .lang-current:hover,
:root:not([data-theme="dark"]) .cloud-account-button:not(.offline),
:root:not([data-theme="dark"]) .cloud-account-button:not(.offline):hover:not(:disabled) {
  border-color: var(--line);
  background: var(--surface);
  color: var(--text);
}

/* 顶栏的「套餐订阅」（2026-10-07 从侧栏挪上来，放在积分 / 账户按钮左边）：和铃铛、积分同一身——34 高、描边、半透明底，
   悬浮不变；一枚强调色的购物车 + 四个字（原来是皇冠，用户 2026-10-08 换成侧栏「套餐订阅」原来那枚购物车）。
   它是导航，按下不缩（顶部 button:active 的排除名单里有它）。全站 button 有 min-height: 36px，这里自己写 34，不然撑高一截。 */
.header-billing-button {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 34px;
  min-height: 34px;
  padding: 0 14px 0 11px;
  border: 1px solid var(--header-control-border);
  border-radius: 12px;
  background: rgba(0, 0, 0, .28);
  color: var(--on-primary);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}
.header-billing-button:hover:not(:disabled) { border-color: var(--header-control-border); background: rgba(0, 0, 0, .28); }
/* 购物车那张图四周留白多、笔画细，按皇冠原来的 16 画出来比旁边的铃铛小一圈、显淡；画到 18（和铃铛一样大），
   多出来的 2px 用 -1px 外边距吃掉，按钮宽度和字的位置一个像素不变。 */
.header-billing-icon {
  flex: none;
  width: 18px;
  height: 18px;
  margin: -1px;
  background: var(--accent);
  -webkit-mask: url("/static/icon-cart.png") no-repeat center / contain;
  mask: url("/static/icon-cart.png") no-repeat center / contain;
}
:root:not([data-theme="dark"]) .header-billing-button,
:root:not([data-theme="dark"]) .header-billing-button:hover:not(:disabled) { border-color: var(--line); background: var(--surface); color: var(--text); }

/* 铃铛：用户给的图标（白色透明 PNG），当遮罩用，颜色跟着按钮字色走。
   原来是用 CSS 边框拼出来的钟身和钟摆。侧栏「消息通知」用同一枚。 */
.notify-icon {
  width: 18px;
  height: 18px;
  background: currentColor;
  -webkit-mask: url("/static/icon-notify.png") no-repeat center / contain;
  mask: url("/static/icon-notify.png") no-repeat center / contain;
}

.notify-badge {
  position: absolute;
  top: -5px;
  right: -6px;
  min-width: 16px;
  padding: 0 4px;
  border-radius: 9px;
  background: #d05a5a;
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* 侧边栏那一项的未读小圆点。收起态只剩图标时也还看得见。 */
.sidebar-dot {
  width: 7px;
  height: 7px;
  margin-left: auto;
  border-radius: 50%;
  background: #d05a5a;
}

/* ---- 右侧抽屉 ---- */

.notify-scrim {
  position: fixed;
  inset: 72px 0 0;
  z-index: 70;
  background: rgba(0, 0, 0, .38);
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
  transition: opacity .2s ease, visibility 0s linear .24s;
}
.notify-scrim.open { visibility: visible; opacity: 1; pointer-events: auto; transition-delay: 0s; }

.notify-drawer {
  position: fixed;
  top: 72px;
  right: 0;
  bottom: 0;
  z-index: 75;
  display: flex;
  flex-direction: column;
  width: min(400px, 92vw);
  border-left: 1px solid var(--line);
  background: var(--surface);
  box-shadow: -18px 0 44px rgba(0, 0, 0, .34);
  transform: translateX(100%);
  visibility: hidden;
  pointer-events: none;
  transition: transform .24s cubic-bezier(.22, .61, .36, 1), visibility 0s linear .24s;
}
.notify-drawer.open {
  transform: translateX(0);
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}
/* 有人把系统动画关掉了就别硬推。 */
@media (prefers-reduced-motion: reduce) {
  .notify-drawer,
  .notify-scrim { transition: none; }
}

.notify-drawer-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  padding: 18px 20px 12px;
}
.notify-drawer-head h3 { margin: 0; font-size: 18px; font-weight: 600; }
.notify-drawer-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.notify-drawer-tools button { min-height: 28px; padding: 3px 10px; font-size: var(--font-base); }

/* 底部「查看全部」：整条都能点，和上面的列表用一条分割线隔开。 */
.notify-drawer-foot {
  flex: none;
  width: 100%;
  min-height: 52px;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  background: var(--surface-alt);
  color: var(--text);
  font-weight: 600;
}
.notify-drawer-foot:hover { background: var(--surface-hover); }

/* ---- 页签：全部 / 未读 ---- */

.notify-tabs {
  display: flex;
  align-items: center;
  gap: 24px;
  flex: none;
  padding: 0 20px;
  border-bottom: 1px solid var(--line);
}
.notify-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 2px;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--muted);
  font-size: 15px;
}
.notify-tab:hover { color: var(--text); background: none; }
.notify-tab.active { color: var(--text); border-bottom-color: var(--text); font-weight: 600; }
.notify-count {
  min-width: 22px;
  padding: 1px 6px;
  border-radius: 6px;
  background: var(--surface-soft);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.notify-tab.active .notify-count { background: var(--green-soft); color: var(--green); }
.notify-read-all {
  flex: none;
  min-height: 30px;
  margin-left: auto;
  padding: 4px 10px;
  white-space: nowrap;
}

/* ---- 列表 ---- */

.notify-list { flex: 1; min-height: 0; overflow: auto; }
.notify-empty { margin: 0; padding: 40px 16px; color: var(--muted); font-size: var(--font-base); text-align: center; }

.notify-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 14px 20px;
  border: 0;
  border-bottom: 1px solid var(--cell-line);
  border-radius: 0;
  background: none;
  text-align: left;
}
.notify-item:hover { background: var(--surface-hover); }
/* 整页那边的消息只是内容，不是按钮：鼠标不变手型，也不跟着按下缩一下。
   悬浮那点底色留着——它是「这一行」的标识，不是「可以点」的暗示。 */
.notify-item-static { cursor: default; }
/* 从抽屉点进整页时，落在刚点的那一条上短暂高亮一下，扫一眼就知道是哪条。 */
.notify-item-focus { background: var(--surface-hover); }
.notify-item:last-child { border-bottom: 0; }
/* 未读左侧一条绿杠，比整行变色轻，扫一眼就能分出来。 */
.notify-item.unread { box-shadow: inset 3px 0 0 var(--green); }

/* 圆形图标底盘。三种图形都用 CSS 画，不额外引图标文件。 */
.notify-item-icon {
  position: relative;
  flex: none;
  width: 34px;
  height: 34px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--surface-soft);
  color: var(--muted);
}
/* 通知条目的图标一律用顶栏那枚铃铛（icon-notify.png），不再按种类画对勾 / 气泡 / 手绘铃铛
   （用户 2026-09-12：「通知的 icon 就使用通知栏的铃铛 icon，保持一致」）。种类靠标签区分。 */
.notify-item-icon::before {
  content: "";
  position: absolute;
  inset: 8px;
  background: currentColor;
  -webkit-mask: url("/static/icon-notify.png") no-repeat center / contain;
  mask: url("/static/icon-notify.png") no-repeat center / contain;
}

.notify-item-main { display: grid; gap: 6px; min-width: 0; }
.notify-item-title {
  color: var(--text);
  font-size: var(--font-base);
  line-height: 1.5;
  /* 标题最多两行，再长就省略——一条通知不该把整屏占了。 */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.notify-item-detail {
  color: var(--muted);
  font-size: var(--font-base);
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* 抽屉先给正文摘要，点进左侧通知页后再看完整内容。 */
.notify-drawer .notify-item-detail {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.notify-item-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.notify-item-time { color: var(--muted); font-size: var(--font-base); font-variant-numeric: tabular-nums; }

/* 类型标签：绿色是"办完了"，蓝色是"有人回你了"，灰色是中性。 */
.notify-tag {
  padding: 1px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.notify-tag-green { background: var(--green-soft); color: var(--green); }
.notify-tag-blue { background: var(--blue-soft); color: var(--blue); }
.notify-tag-gray { background: var(--gray-soft); color: var(--muted); }

/* ---- 整页版 ---- */

.notify-page-head { display: flex; align-items: flex-start; gap: 16px; }
.notify-page-head > div { min-width: 0; }
.notify-page-head button { flex: none; margin-left: auto; min-height: 32px; padding: 4px 12px; }
/* 页签挪到框外面当工具栏用，所以不要那条贯穿的下边框，间距按订单页的工具栏来。 */
.notify-page-tabs { padding: 0; border-bottom: 0; margin-bottom: 18px; }
/* 列表容器和订单表用同一套外框：14px 圆角、同色边框和底。 */
.notify-page-wrap {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  overflow: hidden;
}
.notify-page-list { max-height: none; }
.notify-page-list .notify-item-detail { display: block; overflow: visible; }
.cloud-account-button strong { color: inherit; font-weight: inherit; font-variant-numeric: tabular-nums; }

.service-connection {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--header-control-border);
  border-radius: 4px;
  padding: 3px 8px;
  color: var(--header-muted);
  font-size: var(--font-base);
  white-space: nowrap;
}
.service-connection::before { content: ""; width: 7px; min-width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.service-connection.online { border-color: #4f9877; color: #8fe4bb; }
.service-connection.offline { border-color: #d97b7b; color: #ff9f9f; }
.service-connection.starting { border-color: #dba45c; color: #ffd8a4; }
.service-connection.offline::before,
.service-connection.starting::before { animation: service-pulse 1.1s ease-in-out infinite; }

@keyframes service-pulse { 50% { opacity: .25; } }

.header-progress { width: min(360px, 38vw); color: var(--header-muted); font-size: var(--font-base); }
.header-progress > span { display: block; margin-bottom: 5px; text-align: right; }
.progress-track { height: 5px; overflow: hidden; background: var(--progress-track); }
.progress-track span { display: block; width: 0; height: 100%; background: var(--progress-value); transition: width .2s ease; }


main { width: 100%; }
.mode-view { width: 100%; }

.control-band {
  display: flex;
  /* 放不下就换行。不换行的话窄屏会把整页顶出一条横向滚动条，
     而横向滚动会把左边的表头也一起推走，比换行难用得多。 */
  flex-wrap: wrap;
  align-items: end;
  gap: 10px;
  padding: 14px 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.provider-band { align-items: end; flex-wrap: wrap; background: var(--surface-alt); }
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > span, .prompt-label { color: var(--muted); font-size: var(--font-base); font-weight: 650; }
.field.grow { flex: 1; }
.project-path-field { flex: 1 1 260px; max-width: 600px; }
.base-url-field { flex: 1 1 260px; }
.key-field { flex: 1 1 220px; }
.model-field { flex: 0 1 210px; }
.compact-field { width: 130px; }
/* 供应商、模型两个下拉框放宽（2026-10-04 用户：「供应商和模型的下拉框长度要增加」）：模型名「gpt-image-2.5-sunburst」
   在原来 170 的框里只露出一半，「GPT备用线路」在 130 的框里也贴着箭头。两个数是量过的：再宽，1707 宽（2560 @1.5）
   那一行右边的「删除 / 停止生成 / 全部生成」就挤到第二行去了。 */
.image-provider-field { width: 160px; }
.image-model-field { width: 250px; }
.ratio-field { width: 105px; }
.resolution-field { width: 95px; }
.pixel-size-field { width: 130px; }
.provider-mode-field { width: 155px; }
/* ③ 积分标：贴在“开始生成”里，闪电加数值。
   花多少积分写在按下去的那个键上，比单独占一栏更难忽略。 */
.cost-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 8px;
  padding: 1px 7px 1px 5px;
  border-radius: 999px;
  /* 压在按钮自身颜色上，深浅按钮都能压出一块底。 */
  background: rgba(0, 0, 0, .26);
  font-size: var(--font-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.55;
}

.cost-chip::before {
  content: "";
  flex: none;
  width: 11px;
  height: 11px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}

button:disabled .cost-chip { opacity: .55; }

/* 跳转类按钮（去生成图片、去分镜生成、去写正文……）文字后面挂一枚跳转图标（用户 2026-09-12）。
   图标是 mask，颜色跟着按钮字色走，深浅主题不用各配一张。 */
.jump-icon {
  display: inline-block;
  flex: none;
  width: 13px;
  height: 13px;
  margin-left: 5px;
  vertical-align: -1px;
  background: currentColor;
  -webkit-mask: url("/static/icon-jump.png") no-repeat center / contain;
  mask: url("/static/icon-jump.png") no-repeat center / contain;
}
/* 分镜行尾那颗「去生成图片」是带 gap 的 flex，图标再叠 margin 就比资产卡上那颗离字远一截（用户 2026-09-12），收掉。 */
.shot-action-command .jump-icon { margin-left: 0; }
/* 全站的「×」和「✓」都用图标，不再打字符（用户 2026-09-12：「所有的 X，包括关闭按钮等，都用 ICON」「所有的勾都用…」）。
   同样是 mask + currentColor：颜色跟着所在按钮 / 文字走。默认尺寸给正文里的勾，
   各处关闭按钮按自己的大小在下面单独收。 */
.close-icon,
.check-icon {
  display: inline-block;
  flex: none;
  width: 13px;
  height: 13px;
  vertical-align: -2px;
  background: currentColor;
}
.close-icon {
  -webkit-mask: url("/static/icon-close.png") no-repeat center / contain;
  mask: url("/static/icon-close.png") no-repeat center / contain;
}
.check-icon {
  -webkit-mask: url("/static/icon-check.png") no-repeat center / contain;
  mask: url("/static/icon-check.png") no-repeat center / contain;
}
/* 加号（用户 2026-09-12 给的图：实心圆、中间镂空的加号），各处按自己的大小收。 */
.plus-icon {
  display: inline-block;
  flex: none;
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  background: currentColor;
  -webkit-mask: url("/static/icon-plus.png") no-repeat center / contain;
  mask: url("/static/icon-plus.png") no-repeat center / contain;
}
.work-new-plus .plus-icon { width: 36px; height: 36px; }
.notice-image-drop-plus .plus-icon { width: 22px; height: 22px; }
.ticket-new-plus .plus-icon { width: 16px; height: 16px; }
.reference-plus .plus-icon { width: 22px; height: 22px; vertical-align: middle; }
.episode-add-plus .plus-icon { width: 18px; height: 18px; }
.assistant-history-item.new .plus-icon { width: 14px; height: 14px; margin-right: 2px; }
/* 勾在文字前面的那几处（「✓ 1200 字」「✓ 已写入设定」「✓ 已提交」）留一点间距。 */
.script-episode-status .check-icon,
.assistant-applied .check-icon,
.assistant-questions-done .check-icon { margin-right: 4px; }
/* 各处关闭按钮上的 × 按按钮大小收尺寸。 */
.icon-command .close-icon { width: 16px; height: 16px; }
.toast-close .close-icon { width: 12px; height: 12px; }
.new-work-file-clear .close-icon { width: 12px; height: 12px; }
.episode-remove .close-icon,
.notice-image-clear .close-icon { width: 10px; height: 10px; }
.pay-dialog .pay-close .close-icon { width: 14px; height: 14px; }
/* 资产提示词弹窗里的「重新生成提示词」：靠左，和「复制描述」「保存」同一行（用户 2026-09-12）；生成类同一身。
   图片页每条的「详情」2026-09-24 起也开这个弹窗，原来那个任务详情弹窗撤了。 */
.asset-prompt-dialog #assetPromptRegenerateButton { display: inline-flex; align-items: center; margin-right: auto; }
.asset-prompt-dialog #assetPromptRegenerateButton,
.asset-prompt-dialog #assetPromptRegenerateButton:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-create-bg);
  color: var(--command-create-text);
}
/* 生成类按钮上的积分价签（用户 2026-09-12）：白底按钮上价签是黑底、闪电和数字是白的；浅色主题按钮是黑的，价签反过来。
   颜色直接取按钮那两档对调，按钮换色价签跟着换；闪电本来就跟字色走（--credits-icon-color: currentColor）。
   别的按钮上的价签照旧是压一层半透明黑。 */
#mainView #workflowView :is(.one-click-command, .prompt-command, .shot-action-command.is-prompt, .asset-foot button, .asset-prompt-generate, .asset-placeholder .asset-generate) .cost-chip,
#mainView #assetDrawer .asset-generate .cost-chip,
#imageView :is(#startButton, .action-cell .generate-button) .cost-chip,
#videoView :is(#studioPrompt, #studioGenerate) .cost-chip,
:is(.generate-command, .script-settings-foot button, .assistant-action, .assistant-chip, .asset-prompt-dialog #assetPromptRegenerateButton) .cost-chip {
  background: var(--command-create-text);
  color: var(--command-create-bg);
}

/* 顶栏余额用同一套写法，读者一眼就知道是同一种单位。 */
.balance-chip { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }
.balance-chip.credits::before {
  content: "";
  flex: none;
  width: 15px;
  height: 15px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.pixel-size-field input {
  background: var(--surface-soft);
  color: var(--readonly-text);
  font-variant-numeric: tabular-nums;
}

.workflow-source-band { flex-wrap: wrap; }
/* Path inputs across the three modes share one width: they grow to 600px —
   enough for a full project path — and shrink instead of wrapping below that. */
/* 项目名允许收缩到内容以下（min-width: 0），否则输入框的固有宽度会顶出换行。 */
.workflow-name-field { flex: 0 1 170px; min-width: 0; }
.workflow-provider-band { align-items: end; }
.workflow-provider-band .base-url-field {
  flex: 0 1 320px;
  width: 320px;
  max-width: 320px;
}
.workflow-provider-band .key-field {
  flex: 0 1 320px;
  width: 320px;
  max-width: 320px;
}
#imageView .provider-band .base-url-field,
#imageView .provider-band .key-field {
  flex: 0 1 320px;
  width: 320px;
  max-width: 320px;
}

.workflow-capability-band {
  display: flex;
  min-height: 34px;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding: 7px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.workflow-capability-band > strong { font-size: var(--font-base); }
.workflow-capability-band .skill-status { display: inline-flex; align-items: center; gap: 6px; }
.workflow-capability-band .skill-status::before {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  content: "";
}


.workflow-results { background: var(--surface); }
.skill-status { color: var(--muted); font-size: var(--font-base); white-space: nowrap; }
.skill-status.ready { color: var(--success); }
.skill-status.missing { color: var(--red); }
/* 生成结果表和图片、视频的任务表用同一套外观：同样的容器、行高、首列缩进、
   字重和可拖动的列分隔线，三个模式的表格看起来是同一张表。 */
.workflow-file-table { width: 100%; min-width: 900px; border-collapse: collapse; table-layout: fixed; }
/* 表头的上边界由阶段栏底部那条实线负责：粘性表头在合并边框下不画自己的上框线。 */
.workflow-file-table tbody tr:not(.empty-row) td { height: 81px; }
.workflow-file-table th:first-child, .workflow-file-table td:first-child { padding-left: 24px; }
.workflow-file-table th:nth-child(1) { width: 90px; }
.workflow-file-table th:nth-child(2) { width: 130px; }
.workflow-file-table th:nth-child(3) { width: 170px; }
.workflow-file-table th:nth-child(4) { width: 100px; }
.workflow-file-table th:nth-child(5) { width: 34%; }
.workflow-file-table th:nth-child(6) { width: auto; }
.workflow-file-table th:nth-child(7) { width: 150px; }
.workflow-file-generated-at { white-space: nowrap; font-variant-numeric: tabular-nums; }
.workflow-file-path { font-weight: 650; }
.workflow-file-note { display: block; margin-top: 2px; color: var(--muted); font-size: var(--font-base); overflow: hidden; text-overflow: ellipsis; }
.workflow-file-action-cell { overflow: hidden; }
.workflow-file-actions { min-width: 126px; }
.workflow-file-generated { background: var(--success-soft); color: var(--success); }
.workflow-file-validating, .workflow-file-correcting { background: var(--blue-soft); color: var(--blue); }
.workflow-file-skipped { background: var(--gray-soft); color: var(--conflict-text); }
.workflow-file-warning { background: var(--orange-soft); color: var(--orange); }

input, select, textarea {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--input-bg);
  color: var(--text);
  outline: none;
}

input, select { height: 36px; padding: 7px 9px; }
textarea { padding: 11px; line-height: 1.65; resize: vertical; }
/* 鼠标点过的勾选框不留方块高亮，键盘走查时仍然看得见焦点。 */
input[type="checkbox"]:focus { outline: none; box-shadow: none; }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--green); outline-offset: 1px; }

/* 图片生成第一行：类型按钮（角色 / 场景 / 道具 / 分镜）靠左，搜索靠右。
   原来这儿是「全部 / 已完成 / 待处理 …」那条统计栏，撤了（用户原话「不需要了」）。 */
.image-type-bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 12px 24px;
  background: var(--stats-bg);
  border-bottom: 1px solid var(--line);
}
/* 类型那四颗（角色 / 场景 / 道具 / 分镜）2026-09-20 起是全站那份滑轨页签（用户：「也改成滑轨按钮」）：槽色、滑块、选中字色、
   按下缩小全走「滑轨页签」共用块，这里只留排布上的差别——带图标所以是 inline-flex，按字定宽、不换行
   （滑块只按 offsetLeft / offsetWidth 横着摆，换行成两排它就对不上了）。
   原来是四颗独立的大按钮：没选中中性实底、选中生成色实底，各画各的背景。
   **排布的那几条写在共用块的末尾**，不写在这儿：共用块在文件后面、优先级一样，写在这儿的 padding / font-size 会被它盖回去。 */
.image-type-icon { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.image-type-bar .search-field { margin-left: auto; }

.warning-band {
  padding: 10px 24px;
  background: var(--orange-soft);
  border-bottom: 1px solid var(--warning-border);
  color: var(--warning-text);
  white-space: pre-line;
}
/* 警告不再在顶部压一整块日志：具体原因看任务的状态徽标和详情。
   三个模块都留着这块区域和它的文本，只是不显示。 */
#workflowWarningBand, #warningBand, #videoWarningBand { display: none; }

.task-section { background: var(--surface); }
/* 表格上方那条工具栏（搜索 / 类型 / 选集 / 状态）撤了：搜索上了第一行，类型成了按钮，
   选集和状态并进参数那一行。 */
.filter-group, .command-group { display: flex; flex-wrap: wrap; align-items: end; gap: 8px; }
/* 队列操作按钮统一放在每个模式第一行的末尾，用一条竖线与设置项隔开。 */
.band-commands {
  display: flex;
  align-items: center;
  gap: 8px;
  /* 父级设置栏的 gap 是 10px；竖线左右各留 10px，视觉上才居中。 */
  padding-left: 10px;
  /* The group is only as tall as the buttons, so the divider matches them. */
  border-left: 1px solid var(--line-strong);
}
.search-field { width: min(340px, 30vw); }
.filter-field { width: 125px; }
.scope-filter-field { width: 170px; }

.table-wrap { width: 100%; overflow: auto; }
/* 列宽由 app.js 按容器宽度算好并写成行内 min-width，这里不再兜一个固定的最小宽度，否则窄屏上 JS 跑之前就先出一条横向滚动条。 */
#taskTable { width: 100%; min-width: 0; border-collapse: collapse; table-layout: fixed; }
th, td { padding: 10px 9px; border-bottom: 1px solid var(--cell-line); text-align: left; vertical-align: middle; }
th { position: sticky; top: 0; z-index: 1; background: var(--table-header-bg); color: var(--table-header-text); font-size: var(--font-base); font-weight: 700; }
/* JS 接管之前第一帧的列宽，和 app.js 的 DEFAULT_COLUMN_WIDTHS 一个比例（用户 2026-09-12 定的分配）。 */
#taskTable th:nth-child(1) { width: 60px; }
#taskTable th:nth-child(2) { width: 80px; }
#taskTable th:nth-child(3) { width: 80px; }
#taskTable th:nth-child(4) { width: 308px; }
#taskTable th:nth-child(5) { width: 978px; }
#taskTable th:nth-child(6) { width: 200px; }
#taskTable th:nth-child(7) { width: 96px; }
#taskTable th:nth-child(8) { width: 74px; }
#taskTable th:nth-child(9) { width: 230px; }
/* 操作列（用户 2026-09-12）：按钮靠右留 16px 照旧不动（margin-left: auto 顶到右边），「操作」两个字和按钮
   的左边对齐。三颗按钮加价签实测 200，加左内边距 9、右留边 16 是 225，再留 5 给三位数的价签——
   原来的 190 是按「约 170」估的，列真落到 190 时 .action-cell 的 overflow: hidden 会把「生成」的价签裁掉。
   这一列不挂拖动线，不能拖。 */
#taskTable th.action-column, #taskTable td.action-cell { text-align: right; padding-right: 16px; }
#taskTable td.action-cell .row-actions { margin-left: auto; }
/* 「操作」两个字和按钮组左对齐：文字放进一个和按钮组同宽的盒子（宽度由 app.js 按最宽那一行的按钮组实测后写进
   --action-row-width），盒子和按钮一样靠右、右边距一样，列被撑多宽都对得齐。 */
#taskTable th.action-column .action-column-label { display: block; width: var(--action-row-width, auto); margin-left: auto; text-align: left; }
#taskTable th:nth-child(5), .reference-cell { padding-left: 36px; }
.column-resizer {
  position: absolute;
  top: 0;
  left: -5px;
  z-index: 3;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  touch-action: none;
  outline: none;
}
.column-resizer::after {
  content: "";
  position: absolute;
  top: 6px;
  left: 4px;
  bottom: 6px;
  width: 1px;
  background: var(--resizer);
  opacity: .55;
}
.column-resizer:hover::after,
.column-resizer:focus-visible::after,
.column-resizer.active::after { width: 2px; background: var(--green); opacity: 1; }
body.column-resizing, body.column-resizing * { cursor: col-resize !important; user-select: none !important; }
tbody tr:not(.empty-row):hover { background: var(--surface-hover); }
tbody tr.current { background: var(--blue-soft); }
td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.check-column { padding-right: 4px; padding-left: 24px; }
.check-column input {
  width: 16px;
  height: 16px;
  margin: 0;
  vertical-align: middle;
  /* middle 对的是小写字母的中线，量出来比旁边正文的行中线低 2px，一眼看得出不在一条线上（用户 2026-09-15）。抬回去。 */
  position: relative;
  top: -2px;
  cursor: pointer;
}
.check-column input:disabled { cursor: not-allowed; }
/* 已完成又没单独勾过的任务：框里画一个小方块，全选会跳过它。 */
.check-column input.completed-mark {
  position: relative;
  width: 16px;
  height: 16px;
  min-height: 0;
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  background: var(--input-bg);
}
.check-column input.completed-mark::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 1px;
  background: var(--green);
}
.task-name { font-weight: 650; }
.task-subtitle { display: block; margin-top: 2px; color: var(--muted); font-size: var(--font-base); overflow: hidden; text-overflow: ellipsis; }
/* 角色、场景、道具行没有参考图缩略图，会比分镜行矮；统一按分镜行的高度。 */
#taskTable tbody tr:not(.empty-row) td { height: 81px; }
.reference-cell { overflow: hidden; white-space: normal; }
.reference-strip { display: flex; flex-wrap: nowrap; align-items: flex-start; gap: 4px; }
.reference-item {
  position: relative;
  display: grid;
  width: 70px;
  min-width: 70px;
  justify-items: center;
  align-content: start;
  gap: 4px;
}
/* 「停止生成」里那个 i：一个记号，颜色跟着按钮走——写死灰色的话，
   放进红色的危险按钮里就是格格不入的一圈。 */
.info-dot {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: 10px;
  font-style: italic;
  font-weight: 600;
  line-height: 1;
  opacity: .75;
  user-select: none;
}

/* 跟着鼠标走的小提示。固定挂在元素下方的做法试过，不是用户要的——
   他要的是光标旁边那一条。位置由 JS 每次 mousemove 更新。 */
.hover-tip {
  position: fixed;
  /* 顶层里没有 z-index 之争，这个数只给不支持 popover 的浏览器兜底。 */
  z-index: 60;
  /* popover 的浏览器默认样式会写死 inset / margin / 边框 / 内距 / 底色，
     位置是靠 transform 摆的，这几条不清掉提示框会跑到屏幕正中。 */
  inset: auto;
  margin: 0;
  border: 0;
  overflow: visible;
  top: 0;
  left: 0;
  max-width: 280px;
  padding: 5px 10px;
  border-radius: 6px;
  /* 底色跟站内其它浮层（画风、比例那两个）一个口径：浮层要比它压着的面板再亮一档才像浮着。 */
  background: var(--surface-hover);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .42);
  color: #f2f2f2;
  font-size: var(--font-base);
  line-height: 1.5;
  white-space: normal;
  overflow-wrap: anywhere;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
}
:root:not([data-theme="dark"]) .hover-tip {
  background: #ffffff;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .18);
  color: var(--text);
}
/* 提示里要标出来的那半句。一段说明里只该有一个重点，多标几处等于没标。 */
.hover-tip-accent { color: var(--orange); font-weight: 600; }
/* 提示里的单价：前面一枚积分图标，让人一眼知道单位是积分不是钱。 */
.hover-tip-credits {
  display: inline-flex; align-items: center; gap: 3px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.hover-tip-credits::before {
  content: ""; flex: none; width: 11px; height: 11px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.hover-tip.visible { visibility: visible; opacity: 1; }
.reference-slot {
  position: relative;
  width: 42px;
  min-width: 42px;
  height: 42px;
  min-height: 42px;
  overflow: hidden;
  padding: 0;
  border-radius: 4px;
  background: var(--surface-hover);
}
.reference-slot.missing { border: 1px dashed var(--reference-missing-border); color: var(--reference-missing-text); }
/* 取消选中的位：留着位置方便重新指定，但不再算缺图。 */
.reference-slot.dropped { border-style: dotted; background: var(--surface-soft); color: var(--muted); }
/* 打横杠的两种情况：手动取消选中（dropped），以及资产卡被删了（removed）——
   后者分镜表还引用着这个名字，但资产已经不存在，缩略图自然也是空的。 */
.reference-slot.dropped + .reference-label,
.reference-item:has(.dropped) .reference-label,
.reference-label.removed { text-decoration: line-through; }

.reference-slot.removed { border-style: dotted; color: var(--muted); }
.reference-slot.ready { border: 1px solid var(--line-strong); }
.reference-slot:hover:not(:disabled) { border-color: var(--green); background: var(--green-soft); }
.reference-slot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.reference-plus { display: grid; place-items: center; height: 40px; }
.reference-index {
  position: absolute;
  right: 2px;
  bottom: 2px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 3px;
  background: var(--image-index-bg);
  color: var(--on-primary);
  font-size: var(--font-base);
  line-height: 15px;
  text-align: center;
}
.reference-label {
  display: block;
  width: 100%;
  min-height: 14px;
  overflow: hidden;
  color: var(--muted);
  font-size: var(--font-base);
  line-height: 14px;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.output-cell { padding-top: 6px; padding-bottom: 6px; }
.output-content { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 8px; }
.output-filename { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.output-media { display: flex; flex: 0 0 auto; align-items: center; gap: 6px; }
.output-thumbnail {
  display: block;
  width: 46px;
  min-width: 46px;
  height: 46px;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--surface-soft);
}
.output-thumbnail:hover, .output-thumbnail:focus-visible { border-color: var(--green); outline: 2px solid var(--focus-ring); }
.output-thumbnail img { display: block; width: 100%; height: 100%; object-fit: cover; }
.output-download-button {
  width: 46px;
  min-width: 46px;
  height: 46px;
  padding: 0 4px;
  border-color: var(--generate-border);
  color: var(--green);
  line-height: 1.2;
}
.output-download-button:hover:not(:disabled) { border-color: var(--green); background: var(--green-soft); }
/* 悬浮预览的尺寸上限只写这一处：资产卡片（.image-preview-layer）和图片生成
   （.reference-preview）放大出来必须一样大，各写各的迟早会走偏。 */
.image-preview-layer {
  max-width: min(720px, calc(100vw - 24px));
  max-height: calc(100vh - 24px);
}

.image-preview-layer img {
  max-width: min(708px, calc(100vw - 36px));
  max-height: min(708px, calc(100vh - 36px));
}
/* 图片生成页那套悬浮预览（.reference-preview）撤了（用户 2026-09-13）：缩略图改成点开查看器，见 .image-viewer。 */
/* 点击缩略图弹出的菜单，分镜生成和视频生成共用一个。 */
.thumbnail-menu {
  position: fixed;
  z-index: 120;
  min-width: 96px;
  padding: 4px;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface);
  box-shadow: 0 10px 28px var(--dialog-shadow);
}
.thumbnail-menu-item {
  display: block;
  width: 100%;
  min-height: 30px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--text);
  font-size: var(--font-base);
  text-align: left;
}
.thumbnail-menu-item:hover { background: var(--surface-hover); }

/* 空位上没有图可选。置灰后连悬浮底色一并收掉，别看着还能点。 */
.thumbnail-menu-item:disabled {
  color: var(--muted);
  opacity: 0.5;
  cursor: not-allowed;
}
.thumbnail-menu-item:disabled:hover { background: transparent; }

/* 定价页：单项计价按功能分四区，不铺满整屏——一行拉到两千像素宽，
   眼睛要横扫半天才对得上标签和输入框。四列固定，窄屏降到两列。 */
/* 模型页顶上「通用计价」那几块（剧本创作 / 分镜生成 / 角色智配与兜底）。
   排布沿用原来定价页的规矩：**默认一列，够宽了才两列**——一行五列最少要 694px，
   宽度不够时两列会把毛利挤到卡片外、把名字挤成两行。1500 = 694 × 2 + 间距 + 页边距。 */
#consoleModelCommon {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px 14px;
  margin: 0 0 12px;
}
#consoleModelCommon:empty { display: none; }
@media (min-width: 1500px) {
  #consoleModelCommon { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 撑满自己那一格，两列才等宽；单列时还是按 .pricing-section 的 840 限宽。 */
  #consoleModelCommon .pricing-section { max-width: none; }
}

/* 卡片里的价格表。卡片只有三百像素宽，五列塞不下，藏掉的是**毛利**那一格。
   原来藏的是「≈ 售价」和毛利，剩下 项目 / 积分 / 成本——运营看到积分旁边一个「元」的
   框，理所当然当它是售价，填了积分它不动、填了它积分也不动（那是成本，本来就不联动），
   于是报了「互算坏了」。≈ 售价必须在：它和积分互算，运营要看的就是这个数。
   四列的宽度按 292px 的行量过：项目 60+ / 积分 54 / ≈售价 66 / 成本 66，间距收到 6。
   项目那一列只写「480p」「1K」，「每秒」「每张」挪进表头的「积分/秒」里，否则放不下。 */
/* 卡片里的价格表：五列全给，一列都不藏。
   **别再拿「卡片窄」当理由砍列**——砍掉 ≈售价 那次，运营把旁边的成本当成售价，
   报了「积分和售价不能互算」；砍掉毛利这次，运营问「毛利怎么没了」。
   292px 的一行按最挤的数据量过：1080p / 54 / 3.597 / 2.694 / 25%，
   字号收到 12px、内边距 4px、间距 4px 正好排得下。 */
.provider-model-prices {
  --pricing-cols: 42px 48px 56px 56px 40px;
  margin-top: 10px;
  max-width: 100%;
  overflow-x: auto;
}
.provider-model-prices .pricing-row { gap: 4px; }
/* 窄屏的通用定价规则会隐藏两列；模型价格必须始终保留五列，表头和数据共用列宽。 */
.provider-model-prices .pricing-row > .pricing-money,
.provider-model-prices .pricing-row > .pricing-margin,
.provider-model-prices .pricing-row > .pricing-blank:nth-child(n),
.provider-model-prices .pricing-row-head > span:nth-child(n) { display: block; }
.provider-model-prices .pricing-row-head { font-size: 11px; }
.provider-model-prices .pricing-name > span { font-size: 12px; }
/* 视频模型每一档名字旁边的 ×（2026-09-24，删掉的分辨率视频台里选不到）：名字和 × 一行，底下一排「已删除」点了恢复。 */
.provider-model-prices .pricing-name.has-tier-remove { flex-direction: row; align-items: center; gap: 4px; }
.provider-model-tier-remove {
  display: inline-grid; flex: none; width: 16px; height: 16px; min-height: 0; place-items: center; padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: var(--muted); cursor: pointer;
}
.provider-model-tier-remove .close-icon { width: 9px; height: 9px; }
.provider-model-tier-restore {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; padding-top: 8px;
  border-top: 1px solid var(--line); color: var(--muted); font-size: 12px;
}
.provider-model-tier-restore > button {
  min-height: 24px; padding: 0 9px; border: 0; border-radius: 999px;
  background: var(--surface-soft); color: var(--text); font-size: 12px;
}
.provider-model-prices .pricing-margin { font-size: 11px; }
.provider-model-prices .pricing-input {
  width: 100%;
  min-width: 0;
  padding: 0 4px;
  font-size: 12px;
}
/* 卡里的格子只有 52px，鼠标一进去 Chrome 那对上下箭头就压在最后一位数字上，
   「2.694」看着像「2.69」。**这几格本来也用不着箭头**：成本步进 0.001，
   点一下加千分之一没有任何意义。只关卡片里这几格，宽表那边不动。 */
.provider-model-prices .pricing-input::-webkit-outer-spin-button,
.provider-model-prices .pricing-input::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}
.provider-model-prices .pricing-input { -moz-appearance: textfield; }

/* 档位表也不铺满整屏：一行拉到两千像素，八列输入框之间空一大片。 */
#consolePanePricing .table-wrap { max-width: 1120px; }

/* 开关 + 百分比 + % 挤在一格里。
   **别在这儿写死开关的尺寸。** 控制台里的勾选框是一份共用的药丸开关
   （`#consolePage input[type=checkbox]`，30×17，里面的圆点按 13px 位移）。
   这儿原来写着 width:16px，选择器又更具体、压得过它——开关被压扁成 16 宽，
   圆点还按 13px 挪，直接从右边缘挤出去压在百分比输入框上（线上截图里就是这样）。
   `#pricingPackageBody input { width:100% }` 是 (1,0,1)，压不过共用那条的 (1,1,1)，
   所以什么都不用在这里写。 */
.pricing-bonus { display: flex; align-items: center; gap: 6px; }
#pricingPackageBody .pricing-bonus input[type="number"] {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  padding: 0 6px;
}
.pricing-bonus input:disabled { opacity: .4; }
.pricing-unit { flex: none; color: var(--muted); font-size: var(--font-base); }

/* 到账积分是纯加法，做成不可编辑的样子，别让人去点。 */
.pricing-derived {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 八列里只有这几列需要宽度，其余交给内容。 */
#consolePanePricing .console-table th:nth-child(4),
#consolePanePricing .console-table td:nth-child(4) { width: 132px; }
#consolePanePricing .console-table th:nth-child(5),
#consolePanePricing .console-table td:nth-child(5) { width: 104px; }
#consolePanePricing .console-table th:nth-child(6),
#consolePanePricing .console-table td:nth-child(6) { width: 92px; }

/* 定价页的档位表：输入框撑满格子，右边那一列单价用等宽好对齐。 */
#pricingPackageBody input { width: 100%; min-width: 0; }
/* 折算单独占一行，压在档位行下面：跟在最后一列会被操作列挤窄，数字读不全。 */
.pricing-rate {
  padding-top: 0 !important;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-base);
}
.pricing-rate-row td { border-bottom: 1px solid var(--cell-line); }
/* 档位行和它的折算行之间不划线，看着才是一组。 */
#pricingPackageBody tr[data-package-index] td { border-bottom: 0; }
#consolePanePricing .console-table th:last-child,
#consolePanePricing .console-table td:last-child { width: 90px; }

/* 管理员页：一行邮箱+备注+按钮，下面一排权限勾选。 */
.console-admin-form { gap: 8px; }
.console-admin-form input { flex: 1; min-width: 0; }

.console-permission-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin: 10px 0 14px;
}

.console-permission-item {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text);
  font-size: var(--font-base);
  cursor: pointer;
}

/* 管理员页的开关「无限 Minimo 助手额度」：不是权限位，单独一行放在权限勾选下面；
   表里用带色标签标出来，不混进权限那一串。 */
.console-admin-switch { margin: 0 0 14px; }
#consolePaneAdmins .console-admin-quota { color: var(--green); font-weight: 600; }


/* 功能权限：一行一项，说明缩在标签底下。这句说明比标签长得多，跟标签挤在
   同一行会把整块拉成横向一长条，勾选框反而找不着。 */
.console-feature-picker { display: grid; gap: 8px; }

.console-feature-item {
  display: grid;
  /* **第一列按控件实际宽度算，别写死。** 控制台里的勾选框被做成了 30×17 的药丸开关
     （`#consolePage input[type=checkbox]`，ID 选择器压过上面那条 16px），写死 16px
     就溢出 14px，正好压在标签文字上——线上看到的「开关和文本重叠」就是这么来的。 */
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: 8px;
  row-gap: 3px;
}

.console-feature-item small { grid-column: 2; color: var(--text-muted); line-height: 1.6; }

/* 兑换码：生成区保持紧凑，批量结果在下面的可滚动表格中管理。 */
.console-code-form {
  display: grid;
  grid-template-columns: 180px 160px minmax(240px, 1fr);
  gap: 12px 14px;
  max-width: 860px;
  margin: 12px 0 4px;
}
.console-code-table th:first-child,
.console-code-table td:first-child { width: 250px; }
.console-code-table { min-width: 860px; }
.console-code-table th:nth-child(2),
.console-code-table td:nth-child(2) { width: 100px; }
.console-code-table th:nth-child(3),
.console-code-table td:nth-child(3) { width: 100px; }
/* 有效期：「永久」「365 天」，最长也就五个字。 */
.console-code-table th:nth-child(4),
.console-code-table td:nth-child(4) { width: 80px; }
/* 最后一列放「作废」键或使用 / 作废时间：130 → 160（2026-10-12 字体换成 Inter 之后「2026-10-13 01:05」差 25 像素放不下）。 */
.console-code-table th:last-child,
.console-code-table td:last-child { width: 160px; }
@media (max-width: 860px) {
  .console-code-form { grid-template-columns: minmax(0, 1fr); }
}

/* 长期邀请码：和兑换码同一套骨架，但多一列，宽度单独给。 */
.console-invite-form {
  display: grid;
  grid-template-columns: minmax(200px, 1fr) 150px 130px minmax(200px, 1fr);
  gap: 12px 14px;
  max-width: 860px;
  margin: 12px 0 4px;
}
.console-invite-table { min-width: 880px; }
.console-invite-table th:first-child,
.console-invite-table td:first-child { width: 230px; }
.console-invite-table th:nth-child(3),
.console-invite-table td:nth-child(3) { width: 110px; }
.console-invite-table th:nth-child(5),
.console-invite-table td:nth-child(5) { width: 90px; }
/* 150 → 160：字体换成 Inter 之后最后一列的时间差 5 像素放不下（2026-10-12）。 */
.console-invite-table th:last-child,
.console-invite-table td:last-child { width: 160px; }
.console-invite-table .invite-uses-row td { padding-top: 4px; }
.invite-uses-list { margin: 0; padding: 0 0 0 2px; list-style: none; }
.invite-uses-list li {
  display: flex;
  gap: 14px;
  justify-content: space-between;
  max-width: 420px;
  padding: 3px 0;
  color: var(--muted);
}
.invite-uses-time { font-variant-numeric: tabular-nums; }
@media (max-width: 860px) {
  .console-invite-form { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  #consolePaneCodes .console-models-foot {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }
  #consolePaneCodes .console-models-foot button { width: 100%; min-width: 0; }
  #consoleCodeCopyBatch, #consoleCodeStatus { grid-column: 1 / -1; }
  #consolePaneCodes .console-code-list-toolbar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px;
  }
  #consolePaneCodes .console-code-list-toolbar h3 { margin: 0; }
  #consoleCodeSearch { grid-column: 1 / -1; width: 100%; }
  #consoleCodeFilter { width: 100%; min-width: 0; }
}

.status-pill, .type-label {
  display: inline-block;
  border-radius: 4px;
  padding: 3px 6px;
  font-size: var(--font-base);
  line-height: 1.2;
  white-space: nowrap;
}

.status-pending { background: var(--orange-soft); color: var(--orange); }
.status-running { background: var(--blue-soft); color: var(--blue); }
.status-succeeded { background: var(--success-soft); color: var(--success); }
.status-error { background: var(--red-soft); color: var(--red); }
.type-label { background: var(--type-bg); color: var(--type-text); }
.view-button { min-height: 30px; padding: 4px 8px; }
.generate-button { border-color: var(--generate-border); color: var(--green); font-weight: 400; }
.generate-button:hover:not(:disabled) { border-color: var(--green); background: var(--green-soft); }
.row-actions { display: flex; width: max-content; min-width: max-content; align-items: center; gap: 6px; }
.action-column, .action-cell { min-width: 230px; }
.action-cell { overflow: hidden; }
/* 没资源时这一格撑满表格区域，文字上下左右居中。 */
.empty-row td { height: min(60vh, 560px); text-align: center; vertical-align: middle; color: var(--muted); }

.pagination-bar {
  display: flex;
  min-height: 54px;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 24px;
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
}
.pagination-summary, .page-indicator { color: var(--muted); font-size: var(--font-base); white-space: nowrap; }
.pagination-controls { display: flex; align-items: center; gap: 8px; }
.pagination-controls button { min-height: 32px; padding: 5px 10px; }
.page-size-field { display: flex; align-items: center; gap: 7px; color: var(--muted); font-size: var(--font-base); white-space: nowrap; }
.page-size-field select { width: 72px; height: 32px; padding-top: 4px; padding-bottom: 4px; }
.page-indicator { min-width: 78px; text-align: center; }

dialog {
  /* 定位钉死，别靠浏览器默认样式里的 `dialog:modal`。close() 一调用 `:modal`
     当场不再匹配，而关闭动画还要跑 160 毫秒——那段时间弹窗会退回 position: absolute、
     没有上下 inset，整块往页面顶上蹿一下再消失。开着关着都写成一样就不会跳。 */
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(820px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  padding: 0;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 20px 70px var(--dialog-shadow);
}

/* 弹窗的蒙版一律铺满整屏，一个例外都不留。
   原来这里裁掉了顶上 72 像素，好让右上角那条常驻公告不被压暗。
   代价是每个弹窗顶上都横着一道没压暗的亮边——公告大多数时候根本没有，用户看到的
   只是「这个蒙版没盖全」。已经为新建项目和被挤下线各开过一次例外，控制台的用户
   详情弹窗又撞上同一件事，说明例外的那条才是对的。规矩反过来：默认铺满。 */
dialog::backdrop { background: var(--dialog-backdrop); }

/* 弹窗开合动画：开的时候放大淡入，关的时候缩小淡出，蒙版跟着一起。
   **写在 dialog 上，不逐个弹窗写**——全站二十多个弹窗，逐个加漏一个就是一处不一致，
   以后新加的也自动就有。
   靠的是 display / overlay 的离散过渡（allow-discrete）：close() 立刻返回、
   `open` 当场变 false，动画由浏览器自己跑完。JS 那边不用等动画、也不用兜超时——
   原来资产描述那个弹窗是加一个 .closing 类等 animationend，还得防着
   prefers-reduced-motion 下事件根本不来，那一套现在不需要了。
   不支持 allow-discrete 的浏览器直接开合，功能一个字不差。
   开着的时候是 transform: none 而不是 scale(1)：**任何 transform 都会给里面
   position: fixed 的东西当包含块**，弹窗里再摆浮层就会错位。 */
dialog {
  opacity: 0;
  transform: scale(.96);
  transition:
    opacity .16s ease,
    transform .16s ease,
    overlay .16s ease allow-discrete,
    display .16s ease allow-discrete;
}
dialog[open] { opacity: 1; transform: none; }
dialog::backdrop {
  opacity: 0;
  transition:
    opacity .16s ease,
    overlay .16s ease allow-discrete,
    display .16s ease allow-discrete;
}
dialog[open]::backdrop { opacity: 1; }
/* 进场的起点。@starting-style 要写在对应规则后面才盖得住。 */
@starting-style {
  dialog[open] { opacity: 0; transform: scale(.96); }
  dialog[open]::backdrop { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  dialog, dialog::backdrop { transition: none; }
}
.dialog-header { display: flex; justify-content: space-between; gap: 16px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.dialog-header h2 { margin: 7px 0 0; font-size: 18px; }
/* 弹窗标题上面那个小标签（「请确认」「公告」「视频」「MinimoTV」…）一律不要——用户 2026-09-21：「所有的弹窗都不需要什么 小标签」。
   标记留在 HTML 里：好几处是 JS 按内容换字的（资产 / 镜头、通知类别、任务类型），删元素要动一串代码和断言；
   这里一条规则藏掉，以后新加的弹窗也自动没有。标题上面原来给它留的 7px 收回，标题和右边的关闭键上下对齐。
   目录选择框里那个路径（#folderDialogPath）不在 .dialog-header 里，是真信息，不受影响。 */
dialog .dialog-header .type-label { display: none; }
dialog .dialog-header { align-items: center; }
dialog .dialog-header h2 { margin-top: 0; }
.icon-command { width: 36px; padding: 0; font-size: 24px; line-height: 1; }
.task-details { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin: 0; padding: 8px 20px; }
.task-details > div { min-width: 0; padding: 9px 12px 9px 0; }
.task-details dt { color: var(--muted); font-size: var(--font-base); }
.task-details dd { margin: 4px 0 0; word-break: break-all; }

/* 视频右栏资源沿用卡片规格：角色 9:16，场景和道具 16:9。 */
.image-preview-layer[data-preview-kind="landscape"] img {
  width: var(--resource-preview-width, 708px);
  height: var(--resource-preview-height, 398.25px);
  max-width: none;
  max-height: none;
  object-fit: cover;
}

.dialog-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px 18px; }
.session-kicked-dialog { width: min(420px, calc(100vw - 32px)); text-align: center; }
/* 背景整个糊掉：被挤下线时不该还看得清、更不该点得动后面的界面。 */
.session-kicked-dialog::backdrop {
  background: var(--dialog-backdrop);
  /* 蒙版本来就铺满整屏，这里只额外加一层模糊：被挤下线是要人立刻处理的事。 */
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.session-kicked-body { display: grid; gap: 14px; padding: 26px 24px 22px; justify-items: center; }
/* 「请重新登录」那一页：一句话两颗按钮，摆在内容区正中。 */
.relogin-page:not(.hidden) { display: grid; place-items: center; }
.relogin-box { display: grid; gap: 22px; justify-items: center; text-align: center; }
.relogin-box h2 { margin: 0; font-size: 22px; }
.relogin-actions { display: flex; gap: 12px; }
.relogin-actions button { min-width: 128px; min-height: 40px; }
.session-kicked-body h2 { margin: 0; font-size: 18px; }
.session-kicked-body p { margin: 0; color: var(--muted); line-height: 1.6; }
.session-kicked-body button { min-width: 128px; margin-top: 4px; }
.cloud-account-dialog { width: min(620px, calc(100vw - 32px)); }
.cost-dialog { width: min(460px, calc(100vw - 32px)); }
.cloud-account-body { display: grid; gap: 14px; padding: 20px; }
.cloud-auth-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 2px;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface-soft);
}
.cloud-auth-switch button { min-height: 32px; border: 0; background: transparent; }
.cloud-auth-switch button.active { background: var(--surface); color: var(--green); font-weight: 700; }
.cloud-account-note { min-height: 20px; margin: 0; color: var(--muted); font-size: var(--font-base); overflow-wrap: anywhere; }
.cloud-wallet-band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px auto;
  align-items: center;
  gap: 18px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-alt);
}
.cloud-wallet-band > div:not(.cloud-wallet-actions) { display: flex; min-width: 0; flex-direction: column; gap: 5px; }
.cloud-wallet-band span { color: var(--muted); font-size: var(--font-base); }
.cloud-wallet-band strong { overflow: hidden; font-size: 16px; text-overflow: ellipsis; white-space: nowrap; }
#cloudWalletBalance { color: var(--green); font-size: 24px; font-variant-numeric: tabular-nums; }
.cloud-wallet-actions { display: flex; gap: 8px; }
.cloud-packages-section { padding: 18px 20px 20px; }
.cloud-packages-section h3 { margin: 0 0 12px; font-size: 15px; }
.cloud-package-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.cloud-package {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 12px;
  background: var(--surface);
}
.cloud-package strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cloud-package span { color: var(--muted); font-size: var(--font-base); }
.cloud-package button { grid-column: 2; grid-row: 1 / span 2; }

/* 充值卡改成竖排：档位名 + 加赠角标 / 到账总数 / 充多少+赠多少 / 价格 + 立省 / 按钮。
   总数放最大，因为那才是用户真正拿到手的东西。 */
.cloud-package {
  grid-template-columns: minmax(0, 1fr);
  align-items: stretch;
  gap: 6px;
  padding: 14px 16px;
}

.cloud-package button { grid-column: 1; grid-row: auto; margin-top: 4px; }

.cloud-package-head { display: flex; align-items: center; gap: 8px; }
.cloud-package-head strong { flex: 0 1 auto; }

.cloud-package-tag {
  flex: none;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--green-soft);
  color: var(--green);
  font-size: 12px;
}

/* 首购角标换个颜色，和常态的加赠区分开——它只有一次。 */
.cloud-package-tag-first { background: var(--primary-bg); color: var(--on-primary); }

.cloud-package-amount {
  font-size: 26px;
  line-height: 1.15;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.cloud-package-detail { color: var(--muted); font-size: var(--font-base); }
/* 「赠 N」用主色标出来，一眼看到多给了多少。 */
.cloud-package-detail em { font-style: normal; color: var(--green); }

.cloud-package-price { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.cloud-package-price strong { font-size: 19px; }
.cloud-package-price span { color: var(--muted); font-size: 12px; }

/* 六档两列太挤，宽屏排三列。 */
@media (min-width: 1200px) {
  .cloud-package-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* 顶部通知（2026-09-28 照 New API 的通知搬过来：它用的是开源的 sonner 2.0.7，MIT，© 2023 Emil Kowalski）。
   顶部居中，压在顶栏上、离顶栏上沿 8px（JS 按顶栏的位置设 top）；新的一条压在最前，旧的缩小叠在后面露一道边，看得见的最多三条；
   鼠标移上去整摞展开、计时暂停；往上一划就关。几何和过渡照抄 sonner 的样式表，换了这几样：
   字号跟全站（最小 14px，sonner 是 13px）、字体跟全站、配色跟主题变量、上下内边距 11px（sonner 16px）、关闭键在右上角（sonner 左上角）。
   叠放、展开全靠 data-* 状态和 --offset / --toasts-before / --front-toast-height 这几个数，由 shared.js 的 layoutToasts 每次重算。 */
.toast-stack {
  position: fixed;
  /* popover 的浏览器默认样式会写死 inset / margin / 边框 / 内距 / 底色，
     先全清掉再摆自己的位置，否则通知会跑到屏幕正中、还带一圈白框。 */
  inset: auto;
  margin: 0;
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  overflow: visible;
  top: 8px;
  /* 居中靠左右都贴边 + 定宽 + 左右 auto 外边距，不用 left: 50% + translateX：抽屉开着时只改 right 就能在剩下那块里居中。 */
  left: 0;
  right: 0;
  margin-inline: auto;
  /* 顶层里没有 z-index 之争，这个数只给不支持 popover 的浏览器兜底。 */
  z-index: 95;
  width: min(356px, calc(100vw - 32px));
  /* 通知都是绝对定位的，这一层自己没有高度，不挡页面上的任何东西。 */
  height: auto;
  max-height: none;
  --toast-gap: 14px;
  pointer-events: none;
  transition: right .24s cubic-bezier(.22, .61, .36, 1);
}
/* 通知抽屉开着时在抽屉左边那块里居中：通知在顶层，窄屏时会正好压在抽屉表头的 × 上，点关闭就点到了通知
   （2026-09-13 用户：「点击右上角的关闭没有隐藏抽屉」）。宽度和 .notify-drawer 的一致，类名由 toggleNotifyDrawer 挂在 <html> 上。 */
.notify-drawer-open .toast-stack { right: calc(min(400px, 92vw) + 20px); }

.toast {
  --y: translateY(-100%);
  position: absolute;
  top: 0;
  left: 0;
  z-index: var(--z-index);
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  box-sizing: border-box;
  /* 上下 11px（sonner 是 16px，用户 2026-09-28：「通知的高度可以小一点」）：一行字的通知 45px 高。 */
  padding: 11px 16px;
  border: 1px solid var(--toast-border);
  border-radius: 16px;
  background: var(--toast-bg);
  color: var(--toast-text);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .1);
  font-size: var(--font-base);
  outline: none;
  overflow-wrap: anywhere;
  opacity: 0;
  transform: var(--y);
  touch-action: none;
  pointer-events: auto;
  transition: transform 400ms, opacity 400ms, height 400ms, box-shadow 200ms;
}
.toast[data-type="error"] {
  border-color: var(--toast-error-border);
  background: var(--toast-error-bg);
  color: var(--toast-error-text);
}
.toast:focus-visible { box-shadow: 0 4px 12px rgba(0, 0, 0, .1), 0 0 0 2px var(--accent); }
.toast > * { transition: opacity 400ms, visibility 0s; }

/* 圈叉图标：竖着按内容区居中，但落点由 shared.js 按物理像素取整后写进 margin-top（居中算出来常是 13.5px，
   图标的边会被摊到两排像素上，用户 2026-09-29：「圆圈中间一把叉的 icon 也很糊」）。横着离内容区左边 -4px（-3 和 -1 两截），
   **别改**：边框按物理像素取整之后，内容区左边在 1 / 1.25 / 1.5 倍屏上都在整像素上，4 的倍数才跟着落在整像素上。 */
.toast-icon {
  display: flex;
  flex-shrink: 0;
  align-self: flex-start;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: flex-start;
  margin: 0 4px 0 -3px;
}
.toast-icon svg { flex-shrink: 0; margin-left: -1px; }
/* 文字那一块单独画一段（contain: paint）、垫一块和这一条同色的矩形底：通知在浏览器顶层、被画进一个透明的合成层，
   整条又有圆角和阴影，Chrome 认不出字底下是实心的，就不给 ClearType、只给灰度抗锯齿——同一句话比页面上别的字虚一圈
   （用户 2026-09-29：「顶部横幅通知的字体很糊」）。切出来这一段是实心矩形，ClearType 就回来了；颜色一样，看不出多了一块。
   实心底往四周多铺 2px（负外边距抵掉，排版一点不动）：字要是贴着底色的边，边落在半个物理像素上时 Chrome 往里收整，
   贴边的字就被判成「不在实心底上」——1.5 倍屏上就是这么又没了 ClearType。
   will-change: opacity 让这一块自己成一个合成层、整层都是实心底：1.5 倍及以上的屏上 Chrome 不看「字底下这一块是不是实心」，
   只认整层不透明的层，光有上面那块底还是灰度（用户的屏就是 1.5 倍）；鼠标移上去整摞展开时，上面几条也靠它不掉回灰度。
   **别换成 will-change: transform**：Chrome 对它明确不给 ClearType。New API 的通知三种倍率都是灰度，它浅色底深色字、英文，看着不显。 */
.toast-content {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  contain: paint;
  will-change: opacity;
  margin: -2px;
  padding: 2px;
  background: var(--toast-bg);
}
.toast[data-type="error"] .toast-content { background: var(--toast-error-bg); }
.toast-text { font-weight: 500; line-height: 1.5; white-space: pre-line; overflow-wrap: anywhere; }
/* 第二行「作业号：xxxxxxxx」＋复制键，照 sonner 描述行的样子：不加粗、行高 1.4，颜色跟着这一条走。 */
.toast-job { display: flex; align-items: center; gap: 4px; font-weight: 400; line-height: 1.4; }
/* 复制键只是一颗图标：悬浮不换色（全站按钮的规矩），按下缩小照旧。 */
.toast-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: inherit;
}
.toast-copy .check-icon { width: 14px; height: 14px; vertical-align: 0; }

/* 关闭键是压着右上圆角的一颗小圆，一直显示（sonner 默认在左上角，用户 2026-09-28：「把关闭放到右上角」）。
   定位用负的 top / right（sonner 原来是 translate(35%, -35%)）：全站按钮按下是 transform: scale，两个 transform 会打架、一按就跳。
   悬浮不换色（全站按钮的规矩），按下缩小照旧。 */
.toast-close {
  position: absolute;
  top: -7px;
  right: -7px;
  z-index: 1;
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--toast-border);
  border-radius: 50%;
  background: var(--toast-bg);
  color: var(--toast-text);
  font-size: 0;
  line-height: 1;
}
.toast[data-type="error"] .toast-close {
  border-color: var(--toast-error-border);
  background: var(--toast-error-bg);
  color: var(--toast-error-text);
}

/* 进场：从顶上滑下来、淡入。 */
.toast[data-mounted="true"] { --y: translateY(0); opacity: 1; }
/* 收起时后面几条压成和最前那条一样高、每层往下错 14px、缩小 5%，字藏起来，只露一道边。 */
.toast[data-expanded="false"][data-front="false"] {
  --scale: var(--toasts-before) * 0.05 + 1;
  --y: translateY(calc(var(--toast-gap) * var(--toasts-before))) scale(calc(-1 * var(--scale)));
  height: var(--front-toast-height);
}
/* 字淡出之后真不画（visibility 等淡出完再切）：只是透明的字照样算在那个合成层里，整层的 ClearType 又没了。 */
.toast[data-expanded="false"][data-front="false"] > * { opacity: 0; visibility: hidden; transition: opacity 400ms, visibility 0s 400ms; }
.toast[data-visible="false"] { opacity: 0; pointer-events: none; }
/* 展开：每条按自己的偏移往下排开，恢复原来的高度。 */
.toast[data-mounted="true"][data-expanded="true"] {
  --y: translateY(var(--offset));
  height: var(--initial-height);
}
/* 展开后条与条之间隔着 14px：这块透明的桥让鼠标从一条挪到下一条时不算离开，整摞不会收回去。 */
.toast[data-expanded="true"]::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 100%;
  width: 100%;
  height: calc(var(--toast-gap) + 1px);
}

/* 退场：最前那条往上收走；展开着的从自己的位置往上收；收起时后面那几条往下沉着淡出。 */
.toast[data-removed="true"][data-front="true"][data-swipe-out="false"] {
  --y: translateY(-100%);
  opacity: 0;
}
.toast[data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="true"] {
  --y: translateY(calc(var(--offset) - 100%));
  opacity: 0;
}
.toast[data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="false"] {
  --y: translateY(40%);
  opacity: 0;
  transition: transform 500ms, opacity 200ms;
}

/* 划动：手指跟着走（不带过渡），划的时候上下多铺一大块透明区，手滑出通知也不断。 */
.toast[data-swiping="true"] {
  transform: var(--y) translateY(var(--swipe-amount-y, 0px));
  transition: none;
}
.toast[data-swiping="true"]::before {
  content: "";
  position: absolute;
  left: -100%;
  right: -100%;
  bottom: 50%;
  height: 100%;
  z-index: -1;
  transform: scaleY(3) translateY(50%);
}
.toast[data-swiping="false"][data-removed="true"]::before {
  content: "";
  position: absolute;
  inset: 0;
  transform: scaleY(2);
}
.toast[data-removed="true"][data-front="false"]::before { height: calc(var(--initial-height) + 20%); }
.toast[data-swiped="true"] { user-select: none; }
.toast[data-swipe-out="true"] { animation: .2s ease-out forwards; }
.toast[data-swipe-out="true"][data-swipe-direction="up"] { animation-name: toast-swipe-out-up; }
.toast[data-swipe-out="true"][data-swipe-direction="down"] { animation-name: toast-swipe-out-down; }
@keyframes toast-swipe-out-up {
  from { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
  to { transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%)); opacity: 0; }
}
@keyframes toast-swipe-out-down {
  from { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
  to { transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .toast, .toast > * { transition: none !important; animation: none !important; }
}
/* 窄屏铺满，左右各留 16px（sonner 手机上也是这样）。 */
@media (max-width: 600px) {
  .toast-stack,
  .notify-drawer-open .toast-stack { left: 16px; right: 16px; width: auto; }
}
.hidden { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

@media (max-width: 1000px) {
  .app-header { grid-template-areas: "brand tools" "modes modes"; grid-template-columns: 1fr auto; }
  .header-tools { width: auto; }
  .filter-group, .command-group { flex-wrap: wrap; }
  .search-field { width: min(100%, 420px); }
  /* 窄屏：类型按钮和搜索各占一行。 */
  .image-type-bar { align-items: stretch; flex-direction: column; }
  .image-type-bar .search-field { margin-left: 0; }
}

@media (max-width: 640px) {
  .app-header { grid-template-columns: 1fr auto; align-items: center; padding: 10px 14px; gap: 9px; }
  .mode-switch { width: 100%; }
  .mode-switch .mode-card { height: 58px; padding: 6px 8px; }
  .mode-card-title { font-size: var(--font-base); text-align: center; }
  .header-tools { width: auto; gap: 3px; }
  .header-progress { width: min(260px, 62vw); }
  .service-connection { padding: 2px 6px; font-size: var(--font-base); }
  /* Hide secondary account text when header space is tight. */
  #serviceConnection .service-connection-prefix { display: none; }
  #cloudAccountLabel { display: none; }
  .cloud-account-button { padding-right: 8px; padding-left: 4px; }
  .control-band, .image-type-bar { padding-left: 14px; padding-right: 14px; }
  .check-column { padding-right: 14px; padding-left: 14px; }
  .project-band, .workflow-source-band { align-items: stretch; flex-direction: column; }
  .workflow-name-field, .project-path-field {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    max-width: none;
  }
  .cloud-wallet-band { grid-template-columns: 1fr 90px; gap: 12px; }
  .cloud-wallet-actions { grid-column: 1 / -1; }
  .cloud-package-list { grid-template-columns: 1fr; }
  .provider-band > * { flex: 1 1 100%; width: 100%; }
  .workflow-provider-band .base-url-field,
  .workflow-provider-band .key-field,
  #imageView .provider-band .base-url-field,
  #imageView .provider-band .key-field {
    flex: 1 1 100%;
    width: 100%;
    max-width: none;
  }
  .filter-group, .command-group { align-items: stretch; flex-direction: column; }
  .filter-group > *, .command-group > * { width: 100%; }
  .band-commands { flex-wrap: wrap; padding-left: 0; border-left: 0; }
  .band-commands > button { flex: 1 1 auto; }
  .pagination-bar { align-items: stretch; flex-direction: column; padding-left: 14px; padding-right: 14px; }
  .pagination-controls { justify-content: space-between; }
  .task-details { grid-template-columns: 1fr; }
}

.captcha-row { display: flex; gap: 8px; align-items: center; }
.captcha-row input { flex: 1; min-width: 0; }
.captcha-row img {
  height: 40px; width: 116px; border: 1px solid var(--line);
  border-radius: 4px; cursor: pointer; background: var(--surface-muted);
}

/* 暂不开放的页签：保留占位，但明确不可点。 */
.mode-card:disabled {
  opacity: .45;
  cursor: not-allowed;
  filter: grayscale(1);
}
.mode-card:disabled:hover { background: inherit; }

/* 界面文字不参与选中，避免拖选表格时整片变蓝。输入框和提示词文本域要留着，
   否则用户没法选中内容去改或复制提示词。 */
body { user-select: none; -webkit-user-select: none; caret-color: transparent; }
input, textarea, select { user-select: text; -webkit-user-select: text; caret-color: auto; }

/* ==================== 左侧导航 ==================== */
/* 展开时是图标加文字的横条；收起后变成图标加小字的按钮，宽度只留一列。 */

/* 两行两列的格子：第一行是横跨整页的顶部横幅（没有横幅时那一行高度是 0），第二行左边侧边栏、右边内容区。
   原来是一条横排的 flex（侧边栏 | 内容区），横幅塞在内容区里，压不到侧边栏（用户 2026-09-20）。
   三个格子都明写落在哪：横幅藏起来（display: none）时它不占格子，不写死的话侧边栏和内容区会被自动排进第一行，
   那一行是 auto 高度，整页就塌了。body 其余的子元素全是 fixed / absolute / dialog，不参与排版。 */
body { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
body > .urgent-banner { grid-column: 1 / -1; grid-row: 1; }
body > .sidebar { grid-column: 1; grid-row: 2; }
body > .app-shell { grid-column: 2; grid-row: 2; }

/* ---------- 铺满视口 ----------
   整页不滚：表格在自己的框里滚，分页条作为最后一个弹性项钉在视口底边。
   分页跟着内容走的话，任务少时它会飘在半空，任务多时又得先滚到底才看得见。 */

html, body { height: 100%; }
body { overflow: hidden; }

/* 高度跟着格子那一行走（视口减去顶部横幅），不再写死 100vh：写死的话横幅一出现，底边就被挤出视口。
   **内容区里凡是按视口高度算的排版，一律减去 --top-banner-height**（横幅的实际高度，notifications.js 量了写在 :root 上，没有横幅是 0）：
   剧本页 / 分镜页的主体、设定卡、Minimo 助手面板原来都是 calc(100vh - 固定值)，横幅一出现它们还按满屏算，整块被顶下去、
   助手的输入框掉到视口外面（用户 2026-09-20）。弹窗的 max-height: calc(100vh - 48px) 不用减——弹窗压在整页上面，不在内容区里。 */
.app-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.app-header { flex: none; }
/* 顶栏右边那一排（喇叭、主题、铃铛、账户）的右边，和下面内容底板的右边对齐（用户 2026-09-20）。
   原来顶栏左右都是 24px，而内容底板离右边是 46px（侧边栏切出来的页面：.app-page 的右内边距）或 72px（工作台：#mainView 的 64
   加各模式自己的 8），那一排按钮比底板多探出去二三十像素。两种页面的留白不一样，所以顶栏的右内边距跟着「现在显示的是哪一种」走。
   只管宽屏：800px 以下各页的留白另有一套（24 / 14），顶栏也是。改 #mainView 或 .app-page 的右内边距时，这两个数要跟着改。 */
@media (min-width: 801px) {
  .app-header { padding-right: 46px; }
  #appShell:has(#mainView:not(.hidden)) > .app-header { padding-right: 72px; }
}

#mainView {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-top: 6px;
  padding-left: 22px;
  padding-right: 64px;
}

.mode-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* 剧本拆解和图片任务区按视频页的面板节奏排列。 */
#workflowView,
#imageView {
  gap: 10px;
  padding: 8px;
  background: var(--bg);
}

#imageView > .control-band,
#imageView > .image-type-bar,
#imageView > .task-section {
  border: 0;
  border-radius: 8px;
  overflow: hidden;
}

#imageView > .control-band,
#imageView > .image-type-bar { flex: none; }

/* 上方那几条设置栏按内容取高，剩下的全给表格。 */
.mode-view > * { flex: none; }
.mode-view > .task-section,
.mode-view > .workflow-results {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* 图片生成的表格列宽总是被拉到和容器一样宽，横向从来不该滚；关掉横向滚动，免得竖向滚动条出现的那一瞬间多出一条横向的。 */
.task-section > .table-wrap,
.workflow-results > .table-wrap { flex: 1; min-height: 0; overflow-x: hidden; }
.pagination-bar { flex: none; }

/* 侧边栏切出来的页面内容不定长，让它自己竖着滚。 */
.app-page { flex: 1; min-height: 0; overflow-y: auto; }

.sidebar {
  position: sticky;
  top: 0;
  flex: none;
  width: 208px;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 10px 12px;
  /* 底色比右边内容区浅一档（用户 2026-09-21 先要「和右侧一致」，看了之后改成「稍微浅一点，不要太浅，和右侧的
     内容背景区分开」）：夹在页面 #141414 和面板 #212121 中间。#1b1b1b 他看了说「有点浅了，可以深一点，但是不能和右侧
     内容栏的颜色一样」，收到 #181818——比页面亮 4 级，加上右边那道线，分得开又不抢。
     宽度固定 208，折叠那一套撤了，手机上的窄侧栏由下面的媒体查询给。 */
  background: var(--sidebar-bg);
  border-right: 1px solid var(--line);
  overflow: hidden;
}

/* 整块是回「我的项目」的按钮，所以要把全局那套 button 皮扒干净。
   左内边距是 0：logo 因此落在 x=10，正好是选中滑块的左边线，
   和下面那排按钮同一列。 */
.sidebar-head {
  display: flex;
  /* 按基线对齐，不按中线：标是个空盒子，它的基线就是自己的底边，于是标的底落在字的基线上、
     标的顶落在大写字母的顶上（标高 = 大写字母高，见下面）。按中线对的话，字在行盒里本来就不居中
     （上伸部和下伸部不一样长），标和字会错开一两个像素，换一套字体错得还不一样。 */
  align-items: baseline;
  gap: 9px;
  /* 38 → 18 → 4：logo 贴着顶、导航紧跟在它下面（用户 2026-09-21 两次：「整体上移一点，控制台不动」
     「项排列靠上一点」，第二次给了参考图）。底下那组（控制台）是 margin-top: auto 钉在底边的，上面怎么挪它都不动。 */
  margin-top: 4px;
  /* 左内边距 12 = 下面导航按钮的左内边距（.sidebar-item 的 padding: 0 12px）：标的左边和那排图标的左边是同一条线
     （用户 2026-09-21：「logo的左边距，和下方的按钮要一致，现在有点少了」）。原来是 0——那时标是 42 见方，
     对的是选中滑块的左边线；换成小标之后贴着滑块边就显得太靠左了。改按钮的左内边距，这个数要跟着改。 */
  padding: 4px 6px 0 12px;
  min-height: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* 和选中滑块（.sidebar-thumb）一样大，收起展开都是这个尺寸——
   原来展开时放大到 60，一收一展 logo 就跳一下。 */
.sidebar-logo {
  flex: none;
  /* 标：两个相对的圆角三角形（用户 2026-09-21 给的图，`logo-mark.svg` 是照着它画的，逐像素比过只差 0.47%）。
     原来是一只猫头鹰（`logo.png`，文件还在，没人引用了）。宽高比 738 : 458；高度就是 --brand-mark，
     旁边那几个字的大写字母也是这么高——他要的就是「文字和三角形高度一致」，参考的是 LibTV 那种标。 */
  --brand-mark: 16px;
  width: calc(var(--brand-mark) * 738 / 458);
  height: var(--brand-mark);
  margin: 0;
  /* 不加底色方块。白色的标在浅色主题里会没影，所以用遮罩取它的形状，颜色交给主题定——深色白、浅色黑。 */
  background-color: #ffffff;
  -webkit-mask: url("/static/logo-mark.svg?v=1") center / contain no-repeat;
  mask: url("/static/logo-mark.svg?v=1") center / contain no-repeat;
}

/* 大写字母的高 = 标的高（16px）。`font-size-adjust: cap-height .7` 让「大写字母高 = 字号 × 0.7」这件事不看字体：
   Windows 上是 Segoe UI、苹果上是苹方 / SF，各家的大写字母高本来不一样，不钉住的话换台电脑字就比标高一截或矮一截。
   所以字号 = 16 ÷ 0.7。不认这个属性的老浏览器按字体自己的比例走（常见的几套都在 0.7 上下），差不到一个像素。
   行高 34 只管这一块占多高，和对齐无关（对齐靠上面的基线）。改标的高度，这里的 16 要跟着改。 */
.sidebar-brand { line-height: 34px; font-size: calc(16px / .7); font-size-adjust: cap-height .7; font-weight: 700; white-space: nowrap; }

/* 独占 logo 和第一个导航按钮之间的空档，上下等距居中。 */
/* logo 正下方，水平居中，上下留白都是 36px。 */

/* logo 和导航之间的空当。原来 70px，是给骑在它正中间的折叠按钮留的；按钮撤了之后这一段就是一大块空，
   用户给了参考图要「项排列靠上一点」（2026-09-21），收到 14px。 */
/* 2026-10-08 侧栏改「统一横栏」（方案一）时 14 → 18：行高收到 40、行距收到 6 之后导航整列往上收，空当补一点 */
.sidebar-toggle-slot { height: 18px; flex: none; }

.sidebar-section {
  margin: 0 0 8px;
  padding: 0 8px;
  font-size: var(--font-base);
  color: var(--text-muted);
  letter-spacing: .5px;
}

/* 行与行之间 6px（2026-10-08 方案一「统一横栏」：全站竖排可点的横栏以剧本页目录栏为准，用户：「栏位1点击时，下方栏位2的悬浮样式，离的很近是不行的」）。
   原来 11：行距大是因为悬浮、选中都是灰色块，挨近了分不清；现在选中是强调色浅底、悬浮是中性灰，6 就分得开。 */
.sidebar-nav { display: flex; flex-direction: column; gap: 6px; }
/* 底部这组仍然沉到最下面，但不画分隔线——上面本来就有足够的留白。 */
.sidebar-foot { margin-top: auto; padding-top: 8px; }
/* 侧栏底部的版本号：和导航项同一条左线（12px），灰的，不是按钮。
   写成「当前版本：v1.0.0」、13px（用户 2026-09-22：原来 11.5px「太小了」）。前缀由 CSS 画：网关烤进去的和
   心跳写回来的都只是号本身，两处不用各拼一遍；空着（老网关不带 version）时连前缀一起藏。 */
.sidebar-version { display: block; padding: 6px 12px 8px; color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sidebar-foot .sidebar-version:not(:empty)::before { content: "当前版本："; }
.sidebar-version:empty { display: none; }

/* 每一行照剧本页目录栏的分区行（2026-10-08 方案一「统一横栏」，用户在本机演示站上选的）：40 高、圆角 10。
   字 13px / 400 照 LibTV 的左侧导航（用户 2026-10-12 对比之后：「改成一样的」；原来是目录栏分区行那一身 14px / 650）。
   字和图标每一行都是最亮那一档（--sidebar-text，深色纯白），也照 LibTV（用户同一天：「为什么我们的左侧栏icon和字体看起来很暗？」→「改」）：
   原来没选中的退一档 --muted（#b3b3b3），图标再叠 .85 的不透明度（约 #9c9c9c），整列只有选中那一行是亮的。
   选中那一行靠强调色浅底的滑块和强调色图标分出来，悬浮只换底色。**别把没选中的改回灰字**。 */
.sidebar-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  box-sizing: border-box;
  padding: 0 12px;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--sidebar-text);
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  position: relative;
}

/* 选中和悬浮是同一个形状、两种颜色：悬浮是中性灰底（--surface-hover），选中是强调色浅底——选中态不在按钮上画底色，
   由下面那块 .sidebar-thumb 滑块画出来，切页时滑块从旧位置滑到新位置。原来两个都是灰的，选中首页、鼠标停在我的项目上，两行看着都像选中了。
   **选中那一行悬浮不变**（用户 2026-10-08：「在选中后，就不要有悬浮样式了」）：按钮照旧透明、字照旧是 --sidebar-text。 */
.sidebar-item:hover { background: var(--surface-hover); color: var(--sidebar-text); }
/* 选中项不比别的行粗（用户 2026-09-22：「都要相同的字体样式」）：选中态由滑块和图标颜色画，字和别的行一样 13px / 400、一样亮。 */
.sidebar-item.active,
.sidebar-item.active:hover {
  background: transparent;
  color: var(--sidebar-text);
  font-weight: 400;
}
/* 选中那一行的图标亮成强调色 */
.sidebar-item.active .sidebar-icon { background: var(--accent); opacity: 1; }
/* 导航行不是独立的操作按钮：按下不缩；键盘聚焦一圈强调色 */
.sidebar .sidebar-item:active { translate: none; scale: none; transform: none; }
.sidebar .sidebar-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sidebar-item { z-index: 1; }

/* 侧栏滑块：贴着导航项的形状，宽度跟着侧栏展开 / 收起走（和侧栏同一条 .16s 过渡），
   纵向位置由 JS 按选中项的 offsetTop 用 transform 摆，只过渡 transform 不掉帧。 */
.sidebar-thumb {
  position: absolute;
  top: 0;
  left: 10px;
  z-index: 0;
  width: calc(100% - 20px);
  height: 40px;
  box-sizing: border-box;
  border-radius: 10px;
  background: var(--accent-soft);
  opacity: 0;
  pointer-events: none;
  will-change: transform;
  transition: transform .32s cubic-bezier(.22, .61, .36, 1), width .16s ease, opacity .16s ease;
}
.sidebar-thumb.shown { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .sidebar-thumb { transition: none; } }

.sidebar-icon {
  flex: none;
  width: 18px;
  height: 18px;
  background: currentColor;
  /* 和字一样亮、不另外调淡（2026-10-12 照 LibTV；原来 .85，压在字的灰上还要再暗一截） */
  opacity: 1;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  /* 图标画出来的高度要和旁边的字一样高（用户 2026-09-22 拿截图问「icon 和文本标题是不是一样的高度」）。
     14px 的汉字渲染出来墨迹正好 14px（量过）；各张 PNG 里图形占画布的比例不一样（works 铺满、cart 只占 82%），
     所以按高度定 mask-size、宽度随图（auto），墨迹 14px 的各张在下面单独给；盒子照旧 18 见方。
     字改成 13px / 400 之后（2026-10-12，照 LibTV）墨迹中线比原来高 1px，图标不再往下挪（原来是 top: 1px）：
     1 倍、1.25 倍屏下中线差 0，1.5 倍屏 ±0.5 个设备像素，导演台那张照旧比字高一格（量过）。图标大小没跟着缩，比字高 1–2px。 */
  position: relative;
  top: 0;
  -webkit-mask-size: auto 14px;
  mask-size: auto 14px;
}

.sidebar-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 侧栏「3D 导演台」的「3D」用微软雅黑画（用户 2026-09-27：「3D 和导演台高度不一致」→「数字和英文、中文不是相同的字体吗？」）。
   全站字体是 "Segoe UI", "Microsoft YaHei"：「3D」由 Segoe UI 画、汉字退到雅黑，一行里两种字体，Segoe UI 的字母矮一截、位置也不同。
   雅黑自带的英文数字是照它的汉字配的：同字号不挪位置，1 / 1.25 / 1.5 倍屏下和「导演台」一样高、居中（量过）。
   **别再靠放大字号、挪位置去凑**：18.8px 大一圈，16px 在 1.25 倍屏下偏下 1.6 像素，都被他说过。
   2026-10-12 全站字体照 LibTV 之后，汉字按 苹方 → 思源黑体 → 雅黑 取（装了思源黑体的 Windows 上汉字就是它，用户那台就是），
   「3D」照同一个顺序取，和汉字永远是同一种字体，不再写死雅黑。 */
.sidebar-label-latin { font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; }

/* 分组之间那几条线：上下各是 .sidebar-nav 的 gap（6）+ 这里的外边距（6），两边对称；左右缩进 12，和图标、字同一条左线
   （2026-10-08 方案一「统一横栏」，原来上下各 11、左右顶到边）。 */
.sidebar-divider {
  height: 0;
  margin: 6px 12px;
  border: 0;
  border-top: 1px solid var(--line);
}

/* 帮助按钮右端那个右尖角，指的是「点开在右边」。给的是纯白的图，
   当遮罩用才能跟着主题走——和 .studio-board-arrow 同一个道理。 */
.sidebar-chevron {
  flex: none;
  margin-left: auto;
  width: 14px;
  height: 14px;
  /* 和这一行的字、图标一样亮（2026-10-12 照 LibTV，它那颗尖角也是不透明的白；原来 .55） */
  opacity: 1;
  background: currentColor;
  -webkit-mask: url("/static/icon-chevron-right.png") center / contain no-repeat;
  mask: url("/static/icon-chevron-right.png") center / contain no-repeat;
}

.sidebar-external {
  margin-left: auto;
  width: 11px;
  height: 11px;
  opacity: .6;
  background: currentColor;
  -webkit-mask: var(--icon-external) no-repeat center / 11px 11px;
  mask: var(--icon-external) no-repeat center / 11px 11px;
}

.app-shell { flex: 1; min-width: 0; }

/* 窄屏上的侧栏（笔记本收成图标列、手机收进抽屉）在文件末尾「屏幕适配」那一块。 */

/* ==================== 侧边栏切换出来的页面 ==================== */

/* 左右留白和图片生成页的面板对齐：#mainView 的 22/38 加 #imageView 自己的 8。 */
.app-page { padding: 12px 46px 22px 30px; }
/* 项目 / 订单 / 工单 / 通知：页面本身不滚，标题在上，下面一块面板做底，内容在面板里滚。 */
.page-with-panel { display: flex; flex-direction: column; overflow: hidden; padding-bottom: 8px; }
.page-with-panel > .page-head { flex: none; }
.page-panel { display: flex; flex: 1; min-height: 0; flex-direction: column; border: 0; border-radius: 8px; overflow: hidden; background: var(--surface); }
/* 用子选择器压过 .orders-toolbar / .orders-table-wrap 这些写在后面的旧规则。 */
.page-panel > .page-panel-toolbar { flex: none; margin: 0; padding: 12px 24px; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }
.page-panel > .page-panel-body { flex: 1; min-height: 0; overflow: auto; border: 0; border-radius: 0; background: transparent; }
@media (max-width: 800px) { .app-page { padding-right: 24px; padding-left: 24px; } }
@media (max-width: 640px) { .app-page { padding-right: 14px; padding-left: 14px; } }
.page-head { margin-bottom: 18px; }
.page-head h2 { margin: 0; font-size: 20px; font-weight: 600; }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 20px 22px;
}

/* 相邻面板之间留一道缝。原来没有这条，两块的边框直接贴在一起，看着像叠住了。 */
.panel + .panel, .panel + .metrics-grid, .metrics-grid + .panel,
.stat-strip + .panel { margin-top: 16px; }

.panel h3 { margin: 0 0 10px; font-size: 15px; font-weight: 600; }
.panel-note { margin: 0 0 16px; font-size: var(--font-base); line-height: 1.7; color: var(--text-muted); }

.panel-callout {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  padding: 10px 12px;
  border-radius: 6px;
  background: rgba(63, 184, 132, .12);
  border: 1px solid rgba(63, 184, 132, .3);
  font-size: var(--font-base);
  line-height: 1.6;
}

.invite-grid { display: grid; grid-template-columns: minmax(300px, 380px) 1fr; gap: 18px; align-items: start; }
.invite-grid > .panel + .panel { margin-top: 0; }

.invite-quota { display: flex; gap: 34px; margin-bottom: 18px; }
.invite-quota span { display: block; font-size: var(--font-base); color: var(--text-muted); }
.invite-quota strong { font-size: 22px; font-variant-numeric: tabular-nums; }

.invite-create { width: 100%; height: 38px; }

.invite-list { display: flex; flex-direction: column; gap: 8px; min-height: 180px; }

.invite-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-muted);
}

.invite-code {
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 1px;
  user-select: text;
}

.invite-meta { margin-left: auto; font-size: var(--font-base); color: var(--text-muted); }

.invite-tag { font-size: var(--font-base); padding: 2px 7px; border-radius: 3px; }
.invite-tag.active { background: rgba(63, 184, 132, .18); color: #7fd8b0; }
.invite-tag.used { background: var(--line); color: var(--text-muted); }
.invite-tag.expired { background: rgba(224, 122, 106, .16); color: #e0a49a; }

.invite-empty {
  margin: auto;
  padding: 40px 0;
  text-align: center;
  font-size: 14px;
  color: var(--text-muted);
}

@media (max-width: 900px) {
  .invite-grid { grid-template-columns: 1fr; }
}

/* 侧边栏图标：用 mask 上色，跟随 currentColor，深浅主题都不用换图。 */
.sidebar-item[data-icon="workbench"] .sidebar-icon { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='9' rx='1'/%3E%3Crect x='14' y='3' width='7' height='5' rx='1'/%3E%3Crect x='14' y='12' width='7' height='9' rx='1'/%3E%3Crect x='3' y='16' width='7' height='5' rx='1'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='9' rx='1'/%3E%3Crect x='14' y='3' width='7' height='5' rx='1'/%3E%3Crect x='14' y='12' width='7' height='9' rx='1'/%3E%3Crect x='3' y='16' width='7' height='5' rx='1'/%3E%3C/svg%3E"); }
.sidebar-item[data-icon="home"] .sidebar-icon { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 10.5L12 3.5l8.5 7'/%3E%3Cpath d='M5.5 9.5V20h13V9.5'/%3E%3Cpath d='M10 20v-5.5h4V20'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 10.5L12 3.5l8.5 7'/%3E%3Cpath d='M5.5 9.5V20h13V9.5'/%3E%3Cpath d='M10 20v-5.5h4V20'/%3E%3C/svg%3E"); }
.sidebar-item[data-icon="works"] .sidebar-icon { -webkit-mask-image: url("/static/icon-works.png?v=2"); mask-image: url("/static/icon-works.png?v=2"); }
/* 进了项目之后「我的项目」换成当前模式的图标（用户 2026-09-13）：data-mode-icon 由 app.js 的 renderWorksNavIcon 挂，
   四张图和顶上模式页签用的是同一套（icon-mode-*.png）。 */
.sidebar-item[data-page="works"][data-mode-icon="script"] .sidebar-icon { -webkit-mask-image: url("/static/icon-mode-script.png"); mask-image: url("/static/icon-mode-script.png"); }
.sidebar-item[data-page="works"][data-mode-icon="workflow"] .sidebar-icon { -webkit-mask-image: url("/static/icon-mode-storyboard.png"); mask-image: url("/static/icon-mode-storyboard.png"); }
.sidebar-item[data-page="works"][data-mode-icon="image"] .sidebar-icon { -webkit-mask-image: url("/static/icon-mode-image.png"); mask-image: url("/static/icon-mode-image.png"); }
.sidebar-item[data-page="works"][data-mode-icon="video"] .sidebar-icon { -webkit-mask-image: url("/static/icon-mode-video.png"); mask-image: url("/static/icon-mode-video.png"); }
/* 换图时上下滚动（app.js 的 renderWorksNavIcon）：滚的是 mask-position，出了盒子就不画，不用裁。
   时长和 WORKS_ICON_ROLL_MS 一致。 */
/* 图 14 高、盒子 18：平时停在 2px 才是居中（原来 0px 是图铺满盒子那会儿定的，改小之后图顶在盒子上沿，比字高出 3px）；
   滚动的两个落点跟着挪同样多。 */
/* 换成模式图标时各张图的上下空白不一样（图片生成上空 27 下空 17、视频生成上空 39 下空 25），又各自放大过，
   都停在 2px 的话图形比默认那张低 2 像素多（用户 2026-09-27：「显示的icon明显比默认的icon要低一点」）。
   停的位置按各张算（--works-icon-y），让墨迹中线和默认那张一样；滚动的两个落点不变。
   只写整数：浏览器画遮罩时把位置取整到整像素，1.5px 画出来就是 2px（剧本那张量过），照小数算的偏移不会生效。 */
.sidebar-item[data-page="works"] .sidebar-icon {
  -webkit-mask-position: center var(--works-icon-y, 2px); mask-position: center var(--works-icon-y, 2px);
  transition: -webkit-mask-position .16s ease, mask-position .16s ease;
}
.sidebar-item[data-page="works"] .sidebar-icon.rolling-out { -webkit-mask-position: center -20px; mask-position: center -20px; }
.sidebar-item[data-page="works"] .sidebar-icon.rolling-in-start { transition: none; -webkit-mask-position: center 24px; mask-position: center 24px; }
@media (prefers-reduced-motion: reduce) { .sidebar-item[data-page="works"] .sidebar-icon { transition: none; } }
.sidebar-item[data-icon="invite"] .sidebar-icon { -webkit-mask-image: url("/static/icon-invite.png"); mask-image: url("/static/icon-invite.png"); }
/* 上传作品（用户投稿，2026-10-07）：向上的箭头落进托盘，和首页、Minimo3D 那两张同一种线描（24 格、1.8 粗、圆头）。
   墨迹从 2.9 到 21.4，按 18 画出来正好 14 高（和别的图标一样按墨迹 14 对齐文字）。 */
.sidebar-item[data-icon="showcase"] .sidebar-icon { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15.5V3.8'/%3E%3Cpath d='M7.5 8.2 12 3.8l4.5 4.4'/%3E%3Cpath d='M4 13.5v5.2a1.8 1.8 0 0 0 1.8 1.8h12.4a1.8 1.8 0 0 0 1.8-1.8v-5.2'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15.5V3.8'/%3E%3Cpath d='M7.5 8.2 12 3.8l4.5 4.4'/%3E%3Cpath d='M4 13.5v5.2a1.8 1.8 0 0 0 1.8 1.8h12.4a1.8 1.8 0 0 0 1.8-1.8v-5.2'/%3E%3C/svg%3E"); -webkit-mask-size: auto 18px; mask-size: auto 18px; }
.sidebar-item[data-icon="credits"] .sidebar-icon { -webkit-mask-image: url("/static/icon-credits-nav.png"); mask-image: url("/static/icon-credits-nav.png"); }
/* 套餐订阅：购物车图标。用 mask 而不是 background——图本身是白的，直接铺上去在浅色主题里就看不见了；走 mask 才能跟着 currentColor 变。 */
.sidebar-item[data-icon="billing"] .sidebar-icon { -webkit-mask-image: url("/static/icon-cart.png"); mask-image: url("/static/icon-cart.png"); }
/* 设置：和购物车一样走 mask——图本身是白的，直接铺上去在浅色主题里看不见。 */
.sidebar-item[data-icon="settings"] .sidebar-icon { -webkit-mask-image: url("/static/icon-settings.png"); mask-image: url("/static/icon-settings.png"); }
/* 大小在上面按墨迹统一给（13px 高），这里只管图。 */
.sidebar-item[data-icon="help"] .sidebar-icon { -webkit-mask-image: url("/static/icon-help.png"); mask-image: url("/static/icon-help.png"); }
/* 工单那张图的笔画比别的细，按 18px 摆出来显小，单独放到 21px。
   多出来的 3px 用负 margin 吃掉，占位还是 18——不然这一行的文字会比
   别的行往右挪，一列标签就不齐了。 */
.sidebar-item[data-icon="tickets"] .sidebar-icon {
  -webkit-mask-image: url("/static/icon-tickets.png?v=2");
  mask-image: url("/static/icon-tickets.png?v=2");
  width: 21px;
  height: 21px;
  margin-right: -3px;
  -webkit-mask-size: 21px 21px;
  mask-size: 21px 21px;
}
.sidebar-item[data-icon="notify"] .sidebar-icon { -webkit-mask-image: url("/static/icon-notify.png"); mask-image: url("/static/icon-notify.png"); }
.sidebar-item[data-icon="orders"] .sidebar-icon { -webkit-mask-image: url("/static/icon-orders.png"); mask-image: url("/static/icon-orders.png"); }
/* 3D 导演台：摄像机（用户 2026-09-27 给的图，裁掉上下空白存成 icon-director.png，按 14px 高画出来墨迹正好 14）。
   上下边和文字齐的时候看着偏低（他说「高度有点偏下了」）：上面两个卷盘是细线、实心的机身压在下半截，分量全在下面。
   停在 1px（比默认的 2px 高一格）是 1 倍、1.25 倍屏截图挨个比出来的，2px 时卷盘顶已经高出字；只用整数，边才不糊 */
.sidebar-item[data-icon="director"] .sidebar-icon { -webkit-mask-image: url("/static/icon-director.png"); mask-image: url("/static/icon-director.png"); -webkit-mask-position: center 1px; mask-position: center 1px; }
/* 每张图按自己的墨迹占比换算，画出来都是 14px 高（= 14px 汉字的墨迹，截图量的）：占比 = 图形高 ÷ 画布高，用 Pillow 量的。
   home 那张 SVG 按 18 画出来正好 14；mode-video 是 212 宽的横图，按 14 高会超出 18 的盒子，收到 17。
   放在各张的 mask-image 规则后面（同优先级、后者赢）；没列的（works / orders 铺满画布）就是上面那条 14px。 */
.sidebar-item[data-icon="home"] .sidebar-icon { -webkit-mask-size: auto 18px; mask-size: auto 18px; }
.sidebar-item[data-icon="billing"] .sidebar-icon { -webkit-mask-size: auto 17px; mask-size: auto 17px; }      /* cart .82 */
.sidebar-item[data-icon="credits"] .sidebar-icon { -webkit-mask-size: auto 14px; mask-size: auto 14px; }      /* credits-nav .97，量出来 14.5 画成 15，收到 14 */
.sidebar-item[data-icon="notify"] .sidebar-icon { -webkit-mask-size: auto 16px; mask-size: auto 16px; }       /* notify .86 */
.sidebar-item[data-icon="invite"] .sidebar-icon { -webkit-mask-size: auto 15.5px; mask-size: auto 15.5px; }   /* invite .89 */
.sidebar-item[data-icon="settings"] .sidebar-icon { -webkit-mask-size: auto 14px; mask-size: auto 14px; }     /* settings .95，「设置」两个字只有 13.5 高 */
.sidebar-item[data-icon="help"] .sidebar-icon { -webkit-mask-size: auto 16px; mask-size: auto 16px; }         /* help .88 */
.sidebar-item[data-page="works"][data-mode-icon="script"] .sidebar-icon { -webkit-mask-size: auto 15px; mask-size: auto 15px; }
/* 图片生成那张的大小和停的位置是 1 倍屏下试出来的：中线和默认那张齐（原来停在 2px 低 2 像素多）。 */
.sidebar-item[data-page="works"][data-mode-icon="image"] .sidebar-icon { -webkit-mask-size: auto 17px; mask-size: auto 17px; --works-icon-y: 0px; }  /* 上空 27 下空 17 */
/* 视频生成：原来那张上下空白不对称，怎么摆都要么偏低、要么发糊（用户 2026-09-27：「icon都有点变糊了」→「直接换icon」）。
   换成他给的图、裁掉空白（157×112）：画 12px 高、停在 3px，图形上下边正好落在整像素上、中线和默认那张齐。 */
.sidebar-item[data-page="works"][data-mode-icon="video"] .sidebar-icon { -webkit-mask-size: auto 12px; mask-size: auto 12px; --works-icon-y: 3px; }
:root { --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 5 8 12l7 7'/%3E%3C/svg%3E"); }
:root { --icon-account: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='5'/%3E%3Cpath d='M4 21a8 8 0 0 1 16 0'/%3E%3C/svg%3E"); }
:root { --icon-credits: url("/static/icon-credits-bolt.png"); }
/* Minimo Design 积分的标（2026-10-27，用户给的四角星）：Design 积分单独一个余额，画它的地方都换成这颗星。
   画积分标的规则一律是 mask: var(--icon-credits)，所以只要在外层挂 .design-credits 把变量换掉，里面的闪电就都成了星，颜色照旧。 */
:root { --icon-design-credits: url("/static/icon-design-star.png"); }
.design-credits { --icon-credits: var(--icon-design-credits); }
:root { --icon-chevron-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 5 7 7-7 7'/%3E%3C/svg%3E"); }
:root { --icon-crown: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M11.56 3.27a.5.5 0 0 1 .88 0l2.95 5.6a1 1 0 0 0 1.52.3l4.27-3.67a.5.5 0 0 1 .8.52l-2.83 10.25a1 1 0 0 1-.96.73H5.81a1 1 0 0 1-.96-.73L2.02 6.02a.5.5 0 0 1 .8-.52l4.27 3.67a1 1 0 0 0 1.52-.3z'/%3E%3Cpath d='M5 21h14'/%3E%3C/svg%3E"); }
/* 积分闪电默认是滑轨按钮背景那个紫（--primary-bg，用户定的）。例外两种。
   一：压在按钮底色上会看不见——主按钮、智配弹窗里那颗掏钱按钮，闪电跟着按钮字色。
   （顶栏会员那颗原来整颗铺渐变、也在这里；2026-10-07 改成中性底 + 头像渐变环之后闪电照旧是紫。） */
.primary-command,
.smart-fit-dialog .smart-fit-enable { --credits-icon-color: currentColor; }
/* 二：花积分的地方一律单色（用户 2026-09-10：「除了套餐订阅，和右上角的账户详情面板弹窗，其他的所有有积分消耗的地方，
   都改成单色的icon。深浅主题自动适配」）——生成按钮上的价签、视频生成悬浮提示里的价、智配弹窗的预估。
   闪电跟着旁边那个数字的字色走，字色本来就随主题变，深浅主题自动对上。
   套餐订阅（含付款弹窗）、账户面板这两处照旧是紫；顶栏余额、积分明细和订单里的积分是余额和买到的，不是花掉的，也不动。 */
.cost-chip,
.hover-tip-credits,
.smart-fit-estimate-cost { --credits-icon-color: currentColor; }
:root { --icon-logout: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'/%3E%3Cpath d='m16 17 5-5-5-5'/%3E%3Cpath d='M21 12H9'/%3E%3C/svg%3E"); }
:root { --icon-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E"); }

/* 闪电的外框是居中的，但主体重心偏上；统一下移 1px 才和右侧数字对齐。 */
.cost-chip::before,
.balance-chip.credits::before,
.cloud-account-menu-icon-credits,
.credits-value::before,
.buy-package-credits::before,
.order-credits::before { transform: translateY(1px); }

/* ---- 作品首页 ---- */
.works-head { display: flex; align-items: center; justify-content: flex-start; gap: 16px; padding: 0 0 16px; }
/* 搜索框跟着标题靠左，中间一条竖线把它和「x 个项目」隔开。
   .asset-search 的 margin-left:auto 写在这条后面、特异性又一样，所以这里必须带上 .works-head 才压得住。 */
.works-search { flex: 0 1 300px; }
.works-head .works-search { margin-left: 0; }
.works-search input { height: 34px; }
.works-head-divider { width: 1px; height: 22px; flex: none; background: var(--line-strong); }
.works-empty { grid-column: 1 / -1; margin: 0; padding: 32px 0; text-align: center; }
/* 项目页照图片生成页的样子：页面本身不滚，标题在上，下面一块面板做底；
   卡片在面板里滚，分页条钉在面板底边，和 #imageView > .task-section 同一套。 */
#worksPage { display: flex; flex-direction: column; overflow: hidden; padding-bottom: 8px; }
/* 顶上往上收一点（用户 2026-10-07：「我的项目页，顶部可以往上移一点」）：去掉 .app-page 那 12px 上内边距，标题和面板一起上移。
   搜索框聚焦只换描边色、没有往外扩的光圈，贴着页面上沿不会被 overflow: hidden 裁掉。手机上顶栏不一样，不动。 */
@media (min-width: 768px) { #worksPage { padding-top: 0; } }
.works-head { flex: none; }
.works-panel { display: flex; flex: 1; min-height: 0; flex-direction: column; border: 0; border-radius: 8px; overflow: hidden; background: var(--surface); }
.works-panel-body { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 24px 8px; }
.works-panel > .pagination-bar { flex: none; border-top: 0; }
#worksPage .works-grid:not(.works-new-grid):not(.works-recent) { padding-bottom: 16px; }
.works-head h2 { display: flex; align-items: baseline; gap: 10px; margin: 0; font-size: 20px; }
.works-head #worksCount { font-size: var(--font-base); font-weight: 400; }
/* 一行四张；窗口窄下去再减列。 */
.works-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; padding: 0 0 32px; }
/* 项目卡悬浮：抬 6px，加一层投影和更高的层级，像一块浮起来的板，离开时过渡回去。
   按鼠标位置倾斜只在左键按住卡片时有（用户 2026-09-13）：角度和透视由 breakdown.js 的 bindWorkCardLift
   写在 transform 上、松开就清掉，回到这儿的抬起；透视不能写在网格上，否则离网格中心远的卡斜得夸张。 */
.work-card:not(.work-card-new) { transition: transform .18s ease, box-shadow .25s ease; will-change: transform; }
.work-card.is-lifted { position: relative; z-index: 2; transform: translateY(-6px); box-shadow: 0 18px 40px rgba(0, 0, 0, .38); }
/* 列表重画时给鼠标底下那张新卡直接摆成抬起的样子（breakdown.js 的 relightHoveredWorkCard），这一帧不走过渡。 */
.work-card.no-transition { transition: none; }
:root:not([data-theme="dark"]) .work-card.is-lifted { box-shadow: 0 18px 40px rgba(24, 32, 29, .18); }
@media (prefers-reduced-motion: reduce) { .work-card:not(.work-card-new) { transition: none; } }
@media (max-width: 1600px) { .works-grid { gap: 12px; } .works-grid .work-card { padding: 14px; } .works-grid .work-actions { gap: 6px; } .works-grid .work-actions button { padding: 7px 8px; } }
@media (max-width: 1400px) { .works-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1150px) { .works-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 800px) { .works-grid { grid-template-columns: minmax(0, 1fr); } }
.works-empty { padding: 0 0 32px; }
.work-card {
  display: flex; flex-direction: column; gap: 10px; min-width: 0; min-height: 236px; box-sizing: border-box;
  padding: 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  color: var(--text); text-align: left; font: inherit;
}
.work-card-new {
  flex-direction: column; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
  border: 1px dashed var(--line-strong); background: var(--surface);
}
.work-card-new:disabled { cursor: not-allowed; opacity: .5; }
.work-new-plus { width: 44px; height: 44px; display: grid; place-items: center; color: var(--green); }
.work-head { display: flex; gap: 10px; align-items: center; }
/* 剧名是卡片上唯一的大字：加粗、一行、放不下省略。 */
.work-title { flex: 1; display: block; min-width: 0; font-size: 18px; font-weight: 700; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.work-head .work-title { flex: 1 1 auto; min-width: 3em; }
/* 卡片头部的「进行中」签：小绿点慢慢呼吸，失败换红点。不变卡片底色、不描边。 */
.work-activity { display: inline-flex; flex: none; align-items: center; gap: 6px; height: 22px; min-height: 0; margin: 0; padding: 0 8px; border: 0; border-radius: 999px; background: var(--green-soft); color: var(--green); font: inherit; font-size: 12px; line-height: 1; white-space: nowrap; cursor: pointer; }
.work-activity-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; animation: work-activity-breathe 1.2s ease-in-out infinite; }
.work-activity.failed { background: rgba(224, 82, 82, .14); color: #e05252; }
.work-activity.failed .work-activity-dot { animation: none; }
@keyframes work-activity-breathe { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .work-activity-dot { animation: none; } }
.work-chips { display: flex; flex-wrap: wrap; gap: 8px; }
/* 卡片标签：浅色块 + 强调色图标，不描边。 */
.work-chip { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border: 0; border-radius: 10px; background: rgba(255, 255, 255, .07); color: var(--text); font-size: var(--font-base); font-variant-numeric: tabular-nums; white-space: nowrap; }
:root:not([data-theme="dark"]) .work-chip { background: rgba(0, 0, 0, .05); }
.work-chip-icon { flex: none; width: 16px; height: 16px; color: var(--accent); }
/* 两行时间贴着卡底，每行上面一条分隔线；卡片的最小高度没变，多出来的空白留在标签和时间之间。 */
.work-meta { display: flex; flex-direction: column; margin-top: auto; }
.work-meta-row { display: flex; align-items: center; gap: 10px; padding: 11px 0; border-top: 2px solid var(--line); color: var(--muted); font-size: var(--font-base); }
/* 深色下 --line 只比卡片亮一点点，创建时间和更新时间之间那道线看不清（用户 2026-09-12），换亮一档的。 */
:root[data-theme="dark"] .work-meta-row { border-top-color: var(--line-strong); }
.work-meta-row:first-child { border-top: 0; padding-top: 0; }
.work-meta-row:last-child { padding-bottom: 0; }
.work-meta-icon { flex: none; width: 16px; height: 16px; color: var(--text); }
.work-meta-label { flex: 1; }
.work-meta-value { flex: none; font-variant-numeric: tabular-nums; white-space: nowrap; }
.work-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line); }
.work-actions button { padding: 7px 10px; }
.new-work-dialog { width: min(880px, calc(100vw - 32px)); }
.new-work-note { margin: 0; padding: 8px 20px 4px; color: var(--muted); font-size: var(--font-base); }


/* ---- 「安全验证」弹窗（设置里绑定 / 验证手机号）---- */
/* **别再给它挂 new-work-dialog。** 那条 `.new-work-dialog { width: 880px; height: 640px }`
   写在样式表更后面，同权重后来者胜，一个「一行输入 + 一张图」的表单会被撑成整屏高、
   底下空一大片——加资产那三个弹窗踩的是同一个坑，那边的注释也写着这句。
   带 [open] 提权，按内容取高。 */
.captcha-dialog, .captcha-dialog[open] {
  width: min(420px, calc(100vw - 32px));
  height: fit-content;
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
}
.captcha-dialog:not([open]) { display: none; }
.captcha-body { padding: 16px 20px 0; }
.captcha-hint { margin: 0 0 14px; color: var(--muted); font-size: var(--font-base); line-height: 1.6; }
/* 输入框和验证码图并排：图在右边当参照，眼睛不用上下来回找。
   底对齐是因为左边多一行标签，两者的下沿要齐。 */
.captcha-row { display: flex; align-items: flex-end; gap: 10px; }
.captcha-row .field { flex: 1; min-width: 0; }
/* 图片按钮只是个可点的相框。**不写 hover / focus / active 的颜色**——
   全站按钮那条规矩（四态同样式、只有按下缩小）由全局样式管，这里只管尺寸。 */
.captcha-image { padding: 3px; min-height: 36px; line-height: 0; }
.captcha-image img { display: block; height: 28px; width: auto; border-radius: 3px; }
/* 报错就显示在这儿。留一行的位置，出错时按钮不会往下跳。 */
.captcha-status { margin: 10px 0 0; min-height: 20px; color: var(--red); font-size: var(--font-base); line-height: 1.5; }

/* ---- 剧本拆解详情 ---- */
/* 顶栏里的项目名和四枚芯片：跟在「选择项目」后面，名字放大。 */
/* 顶栏左边只剩剧名，和「选择项目」同高、同一条中线。 */
.header-project { display: flex; align-items: center; min-width: 0; }
.header-project h2 { margin: 0; font-size: 26px; font-weight: 700; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
.breakdown-muted { color: var(--muted); font-size: var(--font-base); }
.breakdown-empty { padding: 28px 24px 32px; border-bottom: 1px solid var(--line); }
.breakdown-empty h2 { margin: 0 0 14px; font-size: 17px; }
.breakdown-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.breakdown-steps li { position: relative; padding: 12px 14px 12px 40px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); }
.breakdown-steps li::before { counter-increment: step; content: counter(step, decimal-leading-zero); position: absolute; left: 14px; top: 12px; color: var(--green); font-size: var(--font-base); font-weight: 700; font-variant-numeric: tabular-nums; }
.breakdown-steps strong { display: block; margin-bottom: 3px; font-size: var(--font-base); }
.breakdown-steps span { color: var(--muted); font-size: var(--font-base); line-height: 1.5; }
.breakdown-tabs { display: grid; grid-template-columns: repeat(2, minmax(200px, 340px)); gap: 8px; padding: 12px 24px; border-bottom: 1px solid var(--line); }
.stat-tile { display: grid; grid-template-columns: max-content minmax(0, 1fr); grid-template-rows: auto 28px 20px; gap: 0 8px; padding: 8px 12px 7px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); color: var(--text); text-align: left; font: inherit; cursor: pointer; }
.stat-tile:hover { border-color: var(--control-hover-border); background: var(--surface-hover); }
.stat-tile.active { border-color: var(--green); background: var(--green-soft); }
.stat-tile.active .stat-label { color: var(--green); }
.stat-label { color: var(--muted); font-size: var(--font-base); }
.stat-value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.stat-tile small { color: var(--muted); font-size: var(--font-base); }
/* 三栏：分集 / 本集 / Minimo 助手（右侧第三栏和剧本创作页一样宽，用户 2026-09-14） */
.breakdown-columns { display: grid; grid-template-columns: 260px minmax(0, 1fr) 540px; min-height: 0; gap: 10px; }
/* 三栏都要 min-height: 0：网格行是 auto，随便哪一栏按内容算的最小高度都会把整行撑高、把三栏一起顶出视口。
   前两栏有 overflow 兜着（自动最小高度本来就是 0），助手那一栏是 overflow: visible，对话一长整块就比视口高，
   输入框被顶到屏幕外（用户 2026-09-14：「助手对话框都看不到了」）。剧本页的 .script-side 就是这么写的。 */
.breakdown-col { min-width: 0; min-height: 0; padding: 8px 16px 14px; border: 0; border-radius: 8px; background: var(--surface); }
/* 助手那一列不是卡：面板自己有底色，套在卡里就是双层框。写成两个类叠加，优先级压过上面那条。 */
.breakdown-col.breakdown-col-assistant { padding: 0; background: transparent; border-radius: 14px; overflow: visible; }
/* 面板撑满这一列，不再自己按视口算高度：三栏底边才在同一条线上（用户 2026-09-14：「助手的底部高度你忘记改了」） */
.breakdown-col-assistant .script-assistant-panel { height: 100%; min-height: 0; }
.breakdown-col h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; font-size: var(--font-base); letter-spacing: .06em; color: var(--muted); text-transform: uppercase; }
.asset-state { margin-left: auto; font-size: var(--font-base); font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--orange); }
.asset-state.ready { color: var(--success); }
/* 右上角的 × 探出条目 6px，留出边距别被滚动区裁掉。 */
.episode-nav { display: grid; gap: 4px; padding: 6px 6px 0 0; }
.episode-row { position: relative; }
/* 分集条的尺寸（原来照视频台镜头列表那一行定的，那列表 2026-09-15 撤了，数字留着）：44px 的序号列、78px 行高、10/12 内边距、12px 间距。 */
.episode-item { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 12px; align-items: center; width: 100%; min-height: 78px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--text); text-align: left; font: inherit; }
/* 管理模式：每章右上角一个圆形 ×。 */
.episode-remove { position: absolute; top: -6px; right: -6px; z-index: 1; width: 22px; height: 22px; min-height: 0; padding: 0; display: grid; place-items: center; border: 1px solid var(--red); border-radius: 50%; background: var(--surface); color: var(--red); font-size: 15px; line-height: 1; }
.episode-remove:hover { background: var(--red); color: #fff; border-color: var(--red); }
/* 列表末尾的「新建章节」：和章节条一样大，虚线框，中间一个加号。 */
.episode-add { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 78px; margin-top: 4px; padding: 10px 12px; border: 1px dashed var(--line-strong); border-radius: 10px; background: transparent; color: var(--muted); font: inherit; }
.episode-add:hover:not(:disabled) { border-color: var(--green); color: var(--green); background: var(--green-soft); }
.episode-add-plus { display: inline-flex; line-height: 1; }
.breakdown-col h3 .episode-manage { margin-left: auto; min-height: 26px; padding: 2px 10px; font-size: var(--font-base); font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--muted); }
.breakdown-col h3 .episode-manage.active { border-color: var(--green); color: var(--green); background: var(--green-soft); }
.new-episode-fields { display: grid; gap: 12px; padding: 18px 20px 6px; }
.new-episode-fields textarea { width: 100%; min-height: 220px; box-sizing: border-box; }
.episode-item:hover { background: var(--surface-hover); border-color: var(--line); }
.episode-item.active { background: var(--green-soft); border-color: var(--green); }
/* 序号列照 .studio-shot-index：小字「分集」压着一个 20px 的集号，都是绿的。 */
.episode-code { display: grid; justify-items: center; gap: 1px; color: var(--text); font-size: 11px; font-weight: 600; white-space: nowrap; }
.episode-code b { font-size: 20px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
/* 集名在上、签压在集名下面一行（用户 2026-09-13：「去写正文」这类不要放集名后面，放集名下面）；
   原来只有分镜过期那条这么摆、别的签靠右。集名太长照样省略号，整格上下居中。 */
.episode-item-body { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 4px; text-align: left; }
.episode-item-body strong { min-width: 0; max-width: 100%; font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.episode-item-body .episode-pill { flex: none; }
.episode-item-body small { color: var(--muted); font-size: var(--font-base); }
/* 状态签照 .studio-shot-state：不描边、12px、只靠颜色分态。 */
.episode-pill { padding: 0; border: 0; border-radius: 0; font-size: 12px; white-space: nowrap; color: var(--muted); }
.episode-pill.split { color: var(--blue); }
.episode-pill.partial { color: var(--orange); }
.episode-pill.done { color: var(--success); }
/* 「去写正文」那颗签是个入口，得长得像按钮：强调色描边的药丸。按钮规矩——悬浮 / 聚焦不换样，只有按下缩小。 */
.episode-pill.action { padding: 1px 9px; border: 1px solid var(--accent); border-radius: 999px; color: var(--accent-text); cursor: pointer; }
.episode-pill.action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.episode-pill.action:active { transform: scale(.97); }
.detail-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; margin-bottom: 8px; min-height: 30px; }
.detail-head h3 { margin: 0; font-size: 16px; letter-spacing: 0; text-transform: none; color: var(--text); }
.detail-actions { margin-left: auto; display: flex; gap: 8px; }
.detail-beats { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin: 0 0 10px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); font-size: var(--font-base); }
.detail-beats dt { color: var(--muted); }
.detail-beats dd { margin: 0; }
.detail-beats .risk { color: var(--orange); }
.detail-cta { display: grid; gap: 16px; justify-items: start; padding: 20px 18px; border: 1px dashed var(--line-strong); border-radius: 6px; background: var(--surface-soft); }
.detail-cta-text { display: grid; gap: 8px; }
.detail-cta-title { margin: 0; color: var(--text); font-size: 15px; font-weight: 600; }
/* 「智能解析本集内容」：标签占固定宽、两端对齐，冒号竖着对齐。新建项目弹窗底部用。 */
/* 标签列由整个列表共用：各行描述对齐，标签本身不拉伸（两端对齐会把「分镜信息」撑成「分 镜 信 息」） */
/* 资产卡上的完整设定提示词 */
.asset-prompt { margin: 4px 0 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); color: var(--text); font: inherit; font-size: var(--font-base); line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
.shot-table-wrap { display: grid; gap: 8px; }
.shot-toolbar { display: flex; align-items: center; gap: 8px; }
.shot-summary { margin-right: auto; color: var(--muted); font-size: var(--font-base); font-variant-numeric: tabular-nums; }
.shot-hint { margin: 0; color: var(--muted); font-size: var(--font-base); }
/* table-layout: fixed 是 colgroup 起作用的前提——不加的话浏览器按内容重算列宽，
   拖出来的宽度当场被覆盖。图片生成那张表也是这么设的。 */
.shot-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: var(--font-base); }
/* 表头、行高、勾选框都交给全局那套（`th, td` 和 `.check-column`）——图片生成
   那张表用的就是它。分镜表原来自己写了一份更小的表头和一个没限尺寸的勾选框：
   全局 `input { width: 100%; height: 36px }` 会套到勾选框上，勾中时是一整块
   102×36 的色块。行高仍按 52px，分镜行没有缩略图，不必跟着图片表的 81px。 */
/* 内边距不在这儿写：这条选择器（0,1,1）压得过 .check-column（0,1,0），
   勾选列就拿不到那 24px 左内边距，行里的框比表头的全选框往左偏 15px。
   横竖全局 `th, td` 给的就是 10px 9px，一样的值，交给它。 */
.shot-table td { min-height: 52px; border-bottom: 1px solid var(--cell-line); vertical-align: middle; }
.shot-table td .status-pill { vertical-align: middle; }
.shot-table td.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.shot-table td.shot-refs { color: var(--muted); font-size: var(--font-base); }
.shot-table tr.expanded td { color: var(--muted); }
/* 正在写提示词的那几行：和图片页「正在跑的那行」（tbody tr.current）同一个底色。 */
/* 中栏宽度有限：画面、参考、对白都允许换行，别把状态挤到滚动条后面去。
   多宽由 colgroup 说了算（列宽能拖，拖完记在本机），这儿只管折不折行——
   写死宽度会和拖出来的打架。 */
.breakdown-col-detail .table-wrap .shot-table { min-width: 0; }
.shot-table td:nth-child(3),
.shot-table td:nth-child(4) { white-space: normal; line-height: 1.45; }
.shot-table td:nth-child(4) { padding-right: 14px; }
.shot-cell-primary,
.shot-cell-secondary { display: block; min-width: 0; overflow-wrap: anywhere; }
.shot-cell-primary { color: var(--text); font-weight: 600; line-height: 1.35; }
.shot-cell-secondary { margin-top: 4px; color: var(--muted); font-size: .92em; line-height: 1.35; }
.shot-identity-cell .shot-cell-secondary { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shot-content-cell { vertical-align: middle; }
.shot-content-line { display: block; }
.shot-content-line + .shot-content-line { margin-top: 5px; padding-top: 5px; border-top: 1px solid var(--cell-line); }
/* 对白按剧本的排法：一人一行，名字右对齐到一条看不见的竖线，台词从那条线
   齐头往右。名字列取 max-content，所以一格里所有行的竖线是对齐的。
   原来是「甲：…… / 乙：……」挤成一行，几个人说话就糊成一坨。 */
/* 对白和参考共用这套两列：标签右对齐到一条看不见的竖线，内容从线上齐头
   往右。标签列取 max-content，一格里所有行的竖线才对得齐。 */
.shot-dialogue,
.shot-refs-grid {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 2px;
  row-gap: 5px;
  align-items: baseline;
}
.shot-dialogue b,
.shot-refs-grid b {
  justify-self: end;
  color: var(--muted);
  font-weight: 500;
  white-space: nowrap;
}
.shot-dialogue span,
.shot-refs-grid span { min-width: 0; overflow-wrap: anywhere; }
/* 对白的说话人那一列最多 7 个字宽、超出省略（2026-10-09）：带表演提示的长说话人原来按 max-content 把这一格撑出去，
   台词挤到看不见。新版只写名字、提示挪到台词前面（.shot-dialogue-cue）；回退的旧页面没拆，长的照样省略、悬浮看全 */
.shot-dialogue { grid-template-columns: fit-content(7em) minmax(0, 1fr); }
.shot-dialogue b { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.shot-dialogue-cue { margin-right: 4px; color: var(--muted); font-size: .92em; font-style: normal; }
/* 行尾的操作列宽度也归 colgroup 管（默认 158px：「生成图提示词」带价签比
   「去生图」宽不少，按前者留）。 */
/* 最后一列靠右（用户 2026-09-20：「那 4 个按钮，靠右对齐」）：四颗按钮贴着这一行色块的右端，表头「下一步」跟着靠右，
   和按钮共用右边那条线。09-15 那版是靠左——那时这一格里是滑轨加按钮上下两行，现在只剩一行按钮。 */
.shot-action-cell, .shot-table thead th.fixed-column { text-align: right; }
/* 有了生图提示词之后两颗并排：「详情」「去生图」（用户 2026-09-12）。td 不能改成 flex，套一层 inline-flex。 */
.shot-action-group { display: inline-flex; align-items: center; gap: 6px; }
.shot-action-command {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px;
  border: 0; border-radius: 8px; background: var(--command-neutral-bg); color: var(--text);
  font: inherit; font-size: var(--font-base); white-space: nowrap; cursor: pointer;
}
.shot-action-command:disabled { opacity: .48; cursor: not-allowed; }
/* 最后一列（用户 2026-09-20）：四颗固定的按钮横排一行、在这一格里上下居中——「生成分镜图提示词」「去生图」「详情」「…」。
   原来是上下两行（上面一条「首帧 | 直出」滑轨、下面按钮撑满），滑轨挪进了「…」的浮层。
   td 自己不能改成 flex，套一层 .shot-action-inner；上下居中靠 td 的 vertical-align。按钮按自己的字宽，不拉伸、不换行。 */
.shot-action-cell { white-space: normal; vertical-align: middle; }
/* 两行两列（2026-09-24，用户：「按钮改成两行两列」）：上面「生成分镜图提示词」「去生图」，下面「详情」「…」。四颗横排要 392 宽，
   1920 屏上分镜表那一栏只有 758，前面三列文字只剩三百来像素、还得横着滚才看得到后面几颗。每一列按那一列最宽的一颗定宽，
   同一列上下两颗拉成一样宽，整块贴右。 */
.shot-action-inner { display: grid; grid-template-columns: max-content max-content; justify-content: end; align-items: center; gap: 6px; }
.shot-action-inner > .shot-action-command,
.shot-action-inner > .status-pill { justify-self: stretch; justify-content: center; text-align: center; }
.shot-action-inner > .shot-more { justify-self: stretch; }
.shot-action-inner > .shot-more > .shot-action-command.is-more { width: 100%; }
/* 按钮不许缩到比字加价签还窄：缩了字会挤出按钮左边（用户 2026-09-16）。列已按四颗的宽度定宽，放得下。 */
.shot-action-inner > .shot-action-command.is-prompt { min-width: max-content; }
.shot-more { position: relative; display: inline-flex; flex: none; }
/* 第四颗写这一镜的出片方式 + 下箭头（2026-09-24，原来是「…」三个点）。箭头和分镜设置那颗下拉同一个图标。 */
.shot-action-command.is-more { min-width: 34px; justify-content: center; gap: 4px; padding-left: 10px; padding-right: 8px; }
.shot-action-command.is-more .spec-chevron { display: inline-flex; flex: none; color: var(--muted); }
.shot-action-command.is-more .spec-chevron svg { width: 12px; height: 12px; }
.shot-action-command.is-more[aria-expanded="true"] .spec-chevron { transform: rotate(180deg); }
/* 「…」点开的浮层：两行「首帧 / 直出」，选中的打勾。钉成 fixed、位置由 JS 按按钮摆——表格外面那层 overflow 会裁掉 absolute 的浮层。 */
.ratio-popover.shot-more-popover { position: fixed; top: auto; right: 8px; left: auto; z-index: 30; min-width: 132px; width: 132px; }
#mainView .shot-more-popover .ratio-option, .shot-more-popover .ratio-option { grid-template-columns: 1fr auto; }
.shot-action-line { display: inline-flex; align-items: center; gap: 6px; }
/* 控制行上的「已选设为：首帧 / 直出」 */
.shot-mode-batch { display: inline-flex; align-items: center; gap: 6px; }
.shot-mode-batch-label { margin-right: 2px; color: var(--muted); font-size: var(--font-base); white-space: nowrap; }
/* 混着的（勾中的镜头不是同一种出片方式）：滑块藏起，两段都是未选的字色 */
.shot-mode-batch .shot-mode-thumb[hidden] { display: none; }

/* ---- 列表选中态：三处共用一套 ----
   图片生成的任务表、分镜生成的镜头表、剧本创作的分集行：每行一块独立的圆角底色，
   点行内空白就勾选，勾上之后**描一圈绿边、底色不变**。样板是剧集列表那一份，
   三处各写各的样子会让人以为是三种不同的东西。

   表格那两处的边只能一格一格地画：box-shadow 加在 tr 上不生效（表格行没有自己的
   盒子），加在每个 td 上又会把行内的每条竖线都描一遍。所以中间格只画上下两条，
   首格补左边、末格补右边，拼成一整圈。 */
#taskTable tbody tr.picked td,
.shot-table tbody tr.picked td {
  box-shadow: inset 0 1px 0 var(--green), inset 0 -1px 0 var(--green);
}
#taskTable tbody tr.picked td:first-child,
.shot-table tbody tr.picked td:first-child {
  box-shadow: inset 1px 0 0 var(--green), inset 0 1px 0 var(--green), inset 0 -1px 0 var(--green);
}
#taskTable tbody tr.picked td:last-child,
.shot-table tbody tr.picked td:last-child {
  box-shadow: inset -1px 0 0 var(--green), inset 0 1px 0 var(--green), inset 0 -1px 0 var(--green);
}
#taskTable tbody tr:not(.empty-row),
.shot-table tbody tr { cursor: pointer; }
/* 从分镜「去生图」跳过来时，被选中的那一行闪一下，省得在一屏任务里找。 */
/* 外框画在整行上，不是每个单元格上：加在 td 上的话每一格都描一圈，
   看着就是把行内的分割线全高亮了一遍。底色不用管——跳过来时这一条
   已经被勾上了，.picked 那条规则给过绿底。 */
#taskTable tbody tr.just-picked { animation: just-picked-flash 1.6s ease-out; }
@keyframes just-picked-flash {
  0%, 45% { outline: 2px solid var(--green); outline-offset: -2px; }
  100% { outline: 2px solid transparent; outline-offset: -2px; }
}
.asset-list { display: grid; gap: 12px; }
.asset-group-head { margin-bottom: 4px; color: var(--muted); font-size: var(--font-base); font-weight: 600; }
.asset-row { display: grid; grid-template-columns: 34px 1fr; gap: 6px; padding: 6px 0; border-top: 1px solid var(--cell-line); }
.asset-id { color: var(--muted); font-family: var(--font-mono); font-size: var(--font-base); padding-top: 2px; }
.asset-body { display: grid; gap: 1px; min-width: 0; }
.asset-body strong { font-size: var(--font-base); font-weight: 600; }
.asset-body small { color: var(--muted); font-size: var(--font-base); line-height: 1.45; }
.asset-tag { padding: 0 5px; border-radius: 3px; background: var(--green-soft); color: var(--green); font-size: var(--font-base); font-weight: 500; vertical-align: 1px; }
.asset-voice { font-style: italic; }
.workflow-files-details { border-bottom: 1px solid var(--line); }
.workflow-files-details summary { padding: 10px 24px; cursor: pointer; font-size: var(--font-base); color: var(--muted); }
.workflow-files-details summary:hover { color: var(--text); }
@media (max-width: 1100px) {
  .breakdown-columns { grid-template-columns: 220px minmax(0, 1fr); }
  .breakdown-col-assistant { grid-column: 1 / -1; }
  .breakdown-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- 资产页签：角色 / 道具 / 场景 ---- */
.breakdown-asset-pane { padding: 14px 24px 24px; }
.asset-pane-head { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-bottom: 12px; min-height: 30px; }
.asset-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 12px; }
.asset-card { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 14px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); }
.asset-figure { width: 128px; display: grid; grid-template-rows: 170px auto; gap: 6px; }
.asset-figure a, .asset-figure img { display: block; width: 100%; height: 170px; }
.asset-figure img { object-fit: cover; border-radius: 4px; border: 1px solid var(--line); background: var(--surface-soft); }
/* 只是一块状态占位，不是按钮：批量生成的入口已经不在这一页了。
   底色和边框统一在下面「卡片统一规范」那一段里定，这里只管排版。 */
.asset-placeholder { display: grid; place-content: center; width: 100%; height: 100%; color: var(--muted); font-size: var(--font-base); }
.asset-card .asset-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; min-height: 206px; }
.asset-card-head { display: flex; align-items: baseline; gap: 8px; }
.asset-card-head strong { font-size: 16px; }
.asset-card-head .asset-id { padding-top: 0; }
.asset-card-head .asset-tag { margin-left: auto; }
.asset-desc { color: var(--text); font-size: 14px; line-height: 1.6; }
.asset-line { color: var(--muted); font-size: var(--font-base); line-height: 1.55; }
.asset-status { display: flex; flex-wrap: wrap; gap: 6px; }
.asset-status span { padding: 2px 8px; border: 1px solid var(--line-strong); border-radius: 10px; color: var(--muted); font-size: var(--font-base); }
.asset-status span.on { border-color: var(--green); color: var(--green); }
.asset-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: auto; padding-top: 6px; min-height: 40px; }
@media (max-width: 960px) { .asset-grid { grid-template-columns: 1fr; } }
.asset-pane-actions { display: flex; gap: 8px; }
.asset-placeholder.selected { border-style: solid; border-color: var(--green); color: var(--green); background: var(--green-soft); }
/* 生成中（写提示词 / 出图）：不再蓝框蓝字（用户 2026-09-12：「资产卡片在生成的配色太丑了」）。
   框还是虚线、只比平时亮一档，字用灰色轻轻呼吸——「在忙」由动效说，不靠一块跳色。 */
.asset-placeholder.busy { border-style: dashed; color: var(--muted); border-color: var(--line-strong); }
.asset-placeholder.busy > span { animation: script-pulse 1.8s ease-in-out infinite; }
/* 页签卡上的累计积分：跟着 20px 的数字走，不是按钮里那种小芯片。 */
.stat-value .stat-chip, .stat-side .stat-chip { margin-left: 10px; padding: 0 8px 0 6px; font-size: var(--font-base); line-height: 16px; vertical-align: 3px; background: rgba(0, 0, 0, .3); }
.stat-value .stat-chip::before, .stat-side .stat-chip::before { width: 12px; height: 12px; }
/* 图片框下面那一排。分集卡片已经不放按钮了，只有资产库那份在用。 */
.asset-figure-tools { display: grid; grid-template-columns: minmax(0, 1fr) 30px; gap: 6px; width: 128px; }
.asset-figure-tools button { min-height: 30px; padding: 0 6px; font-size: var(--font-base); }
/* 文字刷新不能让格子变高：数字行、小字行、说明行都给固定高度。 */
.stat-value { display: flex; align-items: center; height: 28px; }
.stat-value .stat-chip { vertical-align: baseline; }
.stat-tile small { display: block; height: 20px; line-height: 20px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stat-tile small.active { color: var(--blue); }
.stat-tile small.failed { color: var(--red); }
.asset-state { height: 30px; line-height: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.asset-card { min-height: 230px; }
.asset-card-head { min-height: 20px; }
/* 页签卡一行三个数：标签小而灰，数字和左边的总数一模一样。 */
.stat-pair { display: inline-flex; align-items: baseline; gap: 3px; }
.stat-pair-label { color: var(--muted); font-size: var(--font-base); font-weight: 500; }
.stat-pair b { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; color: var(--text); }
.stat-tile .stat-label { grid-column: 1 / -1; }
.stat-tile .stat-value { grid-column: 1; }
.stat-tile small { grid-column: 1 / -1; }
.stat-side { grid-column: 2; grid-row: 2; justify-self: end; min-width: 0; display: inline-flex; align-items: center; gap: 10px; height: 28px; }
.stat-pairs { display: inline-flex; gap: 6px; white-space: nowrap; }
.stat-side .stat-chip { margin-left: 0; flex: none; }
.breakdown-asset-pane { padding-top: 12px; }
.stat-pairs { gap: 10px; }
.stat-pair-label { font-size: var(--font-base); }

/* ---- 拆解面板占满剩下的高度：整页本身不滚，镜头表和分集栏各自竖着滚 ---- */
/* 高度和剧本创作页同一个数（用户 2026-09-14：「分镜生成底部的高度和剧本生成不一致，改成和剧本创作一致」）：
   剧本页的 .script-page-body 是 calc(100vh - 122px)，这里不再靠 flex 撑满，两页的卡底才在同一条线上。 */
.mode-view > .breakdown:not(.hidden) { flex: none; height: calc((100vh - var(--top-banner-height, 0px)) - 122px); min-height: 0; display: flex; flex-direction: column; }
.breakdown > .breakdown-tabs { flex: none; }
/* 「一键拆解」「重新生成分镜」和状态条真正的位置在正文标题行里，index.html 里
   那几个只是宿主。还没渲染出分集时（项目正在加载、或者根本没有分集）它们不会
   被搬走，留在 .breakdown 底下就会变成一颗孤零零的大按钮飘在空页面上。 */
.breakdown > .one-click-command { display: none; }
.breakdown > .breakdown-columns { flex: 1; min-height: 0; }
/* 底部「生成全集分镜」：和剧本创作页底部那颗同一身、同一个位置（居中、上 10 下 2）。
   它在 index.html 里写在三栏外面，renderBreakdown 每次重画把它搬进「本集」那一栏的底部——剧本页那颗就在左栏底部，
   三栏才能一起到底（用户 2026-09-14）。margin-top: auto 把它推到栏底。 */
.breakdown-foot { display: flex; flex: none; justify-content: center; margin-top: auto; padding: 10px 0 2px; }
#mainView #workflowView .breakdown-foot .generate-command { min-width: 0; width: auto; padding-left: 22px; padding-right: 22px; justify-content: center; border-radius: 999px; font-weight: 400; }
.breakdown > .breakdown-asset-pane:not(.hidden) { flex: 1; min-height: 0; overflow-y: auto; }
.breakdown-col-episodes { overflow-y: auto; }
/* 分镜表的滚动区给个底（2026-09-24）：上面几条一长，flex 会先把 min-height: 0 的表格区挤掉——《漂亮的恶意》复刻第 1 集
   挤成了 0 高，表格整个看不见、也滚不到。现在表格区至少 260；空间不够时让「开场钩子 / 核心转折 / 结尾悬念」那一块
   先缩、在自己里面滚（它是上面唯一会很长的一块）。这一栏本身不能改成能滚：带圆角的滚动容器左上角会画出一个方角缺口。 */
.breakdown-col-detail { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.breakdown-col-detail > .detail-head, .breakdown-col-detail > .detail-cta { flex: none; }
/* 它是个 grid：高度被压的时候行轨会跟着被压扁、几行字叠在一起，行高钉成内容高，多出来的在它自己里面滚。 */
.breakdown-col-detail > .detail-beats { flex: 0 1 auto; min-height: 56px; overflow-y: auto; grid-auto-rows: max-content; }
.breakdown-col-detail > .shot-table-wrap { flex: 1; min-height: 260px; display: flex; flex-direction: column; gap: 8px; }
.shot-table-wrap > .shot-toolbar, .shot-table-wrap > .shot-hint { flex: none; }
.shot-table-wrap > .table-wrap { flex: 1; min-height: 0; overflow: auto; border: 1px solid var(--line); border-radius: 6px; }
.shot-table-wrap .shot-table tr:last-child td { border-bottom: 0; }

/* 「全部生成」是个宽按钮：名字、积分、已选几项横着排在正中，不再像一张空卡。 */
/* 1600 以下：四张卡放不下「数字 + 待生成 + 已选择 + 积分」一整行，右侧那组挪到大数字下面一行。 */
@media (max-width: 1600px) {
  .breakdown-tabs { grid-template-columns: repeat(2, minmax(160px, 300px)); }
  .stat-tile { grid-template-rows: auto 28px 28px 20px; }
  .stat-side { grid-column: 1 / -1; grid-row: 3; justify-self: start; }
  .stat-tile small { grid-row: 4; }
}
@media (max-width: 1440px) {
  .breakdown-tabs { grid-template-columns: repeat(2, minmax(140px, 260px)); }
  .stat-side { gap: 6px; }
  .stat-pairs { gap: 6px; }
  .stat-side .stat-chip { padding: 0 5px 0 4px; }
}
/* 资产卡右侧的字统一放大 1.2 倍：编号、名字、标签、描述、别名、音色、状态、生成按钮。 */
.asset-card-head { min-height: 26px; }
.asset-card-head .asset-id { font-family: inherit; font-size: 17px; font-weight: 400; color: var(--text); }
.asset-card-head strong { font-size: 19px; }
.asset-card-head .asset-tag { font-size: var(--font-base); padding: 1px 6px; }
.asset-desc { font-size: 17px; line-height: 1.55; }
.asset-line { font-size: 15.5px; line-height: 1.5; }
.asset-status span { font-size: 14.5px; padding: 2px 10px; border-radius: 12px; }
.asset-foot { min-height: 44px; }
.asset-foot button { font-size: 15.5px; min-height: 40px; padding: 8px 14px; }
.asset-foot .cost-chip { font-size: 14.5px; margin-left: 9px; padding: 1px 8px 1px 6px; }
.asset-foot .cost-chip::before { width: 13px; height: 13px; }
/* 页签卡上的积分芯片放大 1.2 倍，并且和「已选择」同一条基线。 */
.stat-side { align-items: baseline; }
.stat-side .stat-chip { font-size: 15px; line-height: 19px; padding: 0 9px 0 7px; }
.stat-side .stat-chip::before { width: 14px; height: 14px; }

/* 左上角的「选择项目」：和右上角那排按钮（套餐订阅、铃铛、主题、「中文」）同一身——深色半透明黑底 + --header-control-border，
   浅色白底 + --line，字用正文色。原来是青蓝浅底 + 青蓝粗字的实色块，是顶栏里唯一一块强调色，用户 2026-10-08：「太丑了」，别改回色块。
   照这屋子的规矩，平时 / 悬浮 / 聚焦一个样，只有按下缩小。 */
.pick-project,
.pick-project:hover:not(:disabled),
.pick-project:focus,
.pick-project:focus-visible {
  border: 1px solid var(--header-control-border);
  background: rgba(0, 0, 0, .28);
  color: var(--text);
  font-weight: 600;
  white-space: nowrap;
  outline: none;
  box-shadow: none;
  transition: transform .08s ease;
}
:root:not([data-theme="dark"]) .pick-project,
:root:not([data-theme="dark"]) .pick-project:hover:not(:disabled),
:root:not([data-theme="dark"]) .pick-project:focus,
:root:not([data-theme="dark"]) .pick-project:focus-visible { border-color: var(--line); background: var(--surface); }
.pick-project:active:not(:disabled) { translate: 0 1px; scale: .98; }
/* 返回箭头用图片，不再用「‹」这个字符——字符的粗细和字体绑死，换字体就变样。
   走 mask 而不是 <img>：那张图是纯白的，直接贴在浅色主题上会看不见；
   当遮罩用就跟着 currentColor 走，按钮什么颜色它就什么颜色。 */
.pick-project { display: inline-flex; align-items: center; gap: 7px; }
.pick-project-icon {
  width: 8px;
  height: 14px;
  flex: none;
  background: currentColor;
  -webkit-mask: url("/static/icon-arrow-left.png") center / contain no-repeat;
  mask: url("/static/icon-arrow-left.png") center / contain no-repeat;
}
/* 图片生成的命令组靠右（参考稿），竖线左边留 16px 和参数隔开。 */
.provider-band .image-commands { margin-left: auto; padding-left: 16px; align-self: end; }
.provider-band .image-commands > button {
  height: 36px;
  min-height: 36px;
  padding-top: 0;
  padding-bottom: 0;
}
@media (max-width: 1000px) {
  .provider-band .image-commands { margin-left: 0; padding-left: 0; }
}

/* 1600 以下顶栏左边放不下「选择项目 + 项目名」：模式卡不再居中，项目名缩一号。 */
@media (max-width: 1600px) {
  .app-header { grid-template-columns: minmax(0, 1fr) auto auto; }
  .header-project h2 { font-size: 21px; }
}

/* 资产卡：描述标签 + 可编辑的提示词框；末尾一张虚线「新增资产」卡。 */
.asset-field-label { color: var(--muted); font-size: 15px; }
.asset-prompt-edit { width: 100%; flex: 1; min-height: 120px; box-sizing: border-box; padding: 10px 12px; font-size: 15px; line-height: 1.6; resize: vertical; }
.asset-add-plus { width: 44px; height: 44px; border: 1.5px solid currentColor; border-radius: 50%; display: grid; place-items: center; font-size: 26px; line-height: 1; }
.new-asset-dialog { width: min(480px, calc(100vw - 32px)); }

/* 卡片高度钉死：图片列不跟着卡片拉伸（否则上传按钮被拉高），提示词框在剩余高度里自己滚动。 */
.asset-card { height: 232px; box-sizing: border-box; min-height: 0; }
.asset-figure { align-self: start; }
.asset-card .asset-body { height: 100%; min-height: 0; }
.asset-prompt-edit { flex: 1; min-height: 0; height: auto; resize: none; overflow: auto; }

/* 统一确认框 */
.confirm-dialog { width: min(440px, calc(100vw - 32px)); }
.confirm-dialog-message { margin: 0; padding: 6px 20px 10px; color: var(--text); line-height: 1.65; white-space: pre-line; }
/* 确认那颗按钮：生成类那一身（深色白底黑字、浅色黑底白字，跟着主题走），不用渐变；
   删除类的也不再标红（用户 2026-09-13：「删除的弹窗，都改成白底黑字」）。
   :hover 那条要一起写：主题层 .primary-command:hover:not(:disabled) 排在后面，不然悬浮一下又变回渐变。 */
.confirm-dialog .dialog-footer .primary-command,
.confirm-dialog .dialog-footer .danger-command,
.confirm-dialog .dialog-footer .primary-command:hover:not(:disabled),
.confirm-dialog .dialog-footer .danger-command:hover:not(:disabled) {
  border-color: transparent; background: var(--command-create-bg); color: var(--command-create-text); box-shadow: none; filter: none;
}
/* 项目卡右上角的删除：和资产卡同一颗按钮，挨着日期 */
.work-head .work-delete { margin-left: 8px; }
.work-head .work-rename { margin-left: 8px; }
/* 卡片上就地改名的输入框：和剧名同一个字号字重，占剧名原来的位置。 */
.work-title-input { flex: 1 1 auto; min-width: 3em; height: 30px; padding: 0 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--input-bg, var(--surface-soft)); color: var(--text); font: inherit; font-size: 18px; font-weight: 700; }
/* 资产卡右上角的删除 */
.asset-card-head .asset-delete { margin-left: auto; width: 28px; height: 28px; min-height: 0; padding: 0; display: grid; place-items: center; border-color: transparent; background: transparent; color: var(--muted); }
.asset-card-head .asset-delete:hover:not(:disabled) { color: var(--red); border-color: var(--red); background: var(--red-soft); }

/* 项目卡整张可点 */
.work-card:not(.work-card-new) { cursor: pointer; }
.work-card:not(.work-card-new):focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.detail-beats dd { min-height: 1.4em; }

/* 顶栏左侧：「选择项目」和三张模式卡一样高（60px）；剧名在同一条中线上居中。 */
/* 左上角这一块的尺寸：剧名 + 药丸 一共 64，「选择项目」48 高、居中对着这一块。
   **别让按钮跟着内容拉伸**——试过 align-self: stretch，绿色实心块一路拔到 68 高，
   一个只有四个字的按钮占掉那么大一片纯色，看着比剧名还重。 */
.pick-project { align-self: center; height: 48px; min-height: 48px; padding: 0 20px; border-radius: 12px; font-size: 16px; }
/* 剧名和药丸信息栏上下摞成一块（药丸只有剧本创作才挂，见 script.js 的
   syncScriptSpecHeader）。高度写死 64：这样四个模式之间切换时左上角不跳，
   顶栏总高也不受影响——中间那排模式卡本来就有 68。 */
.header-project { height: 64px; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 2px; }
/* 两个孩子都不许被 64 的定高挤：剧名（带改名笔）34.9 + 2 + 药丸 28 比 64 多 0.9px，原来这 0.9 是从「能缩的那个」身上扣的——
   药丸那块 overflow: hidden 时 min-height 是 0、它缩；下拉一开 :has() 把它翻成 visible、缩不了了，改缩剧名，
   药丸整条上移 0.9px（用户 2026-09-22：「点击画面风格、秒数、比例，都会导致上移」）。flex: none 之后两种状态一个样。 */
.header-project > * { max-width: 100%; flex: none; }
.header-project-spec:empty { display: none; }
/* 药丸是剧名的第二行，是**副信息**：比剧名矮一半、字号小一档，别跟剧名争。
   28 + 2 + 剧名 31 = 61，正好落在 64 里。 */
.header-project-spec .script-spec-row { gap: 8px; }
.header-project-spec .script-spec-pill { height: 28px; padding: 0 6px; font-size: 13px; }
.header-project-spec .script-spec-input { padding: 0 8px; }
.header-project-spec .script-spec-input input,
#mainView .header-project-spec .script-spec-input input,
.header-project-spec .script-spec-ratio-button,
#mainView .header-project-spec .script-spec-ratio-button { font-size: 13px; }
/* 画风名太长会把整条药丸拉到剧名一样宽，压一压 */
.header-project-spec .script-spec-visual .script-spec-ratio-button { max-width: 12em; }
/* 窄窗口：药丸不跟着左上角那一格一起缩（用户 2026-09-16：「风格和镜头分割中的文本会变成多行，每行 1 个字」）。
   缩的是别的三个模式里锁定态的 <span>——它不像 <button> 那样自带 nowrap，格子一窄就一字一行。
   整条药丸保持原长、不许收缩，右端超出的部分由这一格裁掉；下拉浮层是药丸的子元素，开着时不裁，不然看不见。 */
.header-project-spec { max-width: 100%; overflow: hidden; }
.header-project-spec:has(.ratio-popover, .visual-style-popover) { overflow: visible; }
.header-project-spec .script-spec-row,
.header-project-spec .script-spec-pill,
.header-project-spec .script-spec-input,
.header-project-spec .script-spec-ratio-button { flex: none; white-space: nowrap; }
/* 浮层跟着矮下来的药丸走，别再按 36 高那会儿的固定像素掉 */
.header-project-spec .ratio-popover,
.header-project-spec .script-spec-visual .visual-style-popover { top: calc(100% + 8px); }
.header-project h2 { line-height: 1.2; }
/* 顶栏剧名：名字 + 改名笔 + 复制，一行排开；名字太长先省略，两颗按钮不挤掉。 */
.header-project h2 { display: flex; align-items: center; gap: 0; }
.header-project h2 .rename-text { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.header-project h2 .rename-pen { flex: none; }
.header-project h2 .header-copy { margin-left: 2px; }
.header-project h2 .rename-input { min-width: 160px; max-width: 320px; font-size: 22px; font-weight: 600; }
.header-project:hover .rename-pen { opacity: 1; }

/* 有改动没保存时点亮「保存」。**这不算按钮的第五种状态**（全站那条「悬浮 /
   未选中 / 选中 / 聚焦一律同一样式」说的是交互态），它说的是数据脏了。 */
.primary-command.needs-save { box-shadow: 0 0 0 2px var(--accent-soft); }

/* 卡片三态：默认 --surface，悬浮 --surface-hover，选中同悬浮（选中仍保留绿色边线做标识） */
.stat-tile.active { background: var(--surface-hover); }
.episode-item.active { background: var(--surface-hover); }
.episode-item:hover { background: var(--surface-hover); }
.asset-card:hover { background: var(--surface-hover); }

/* ---- 卡片统一规范：未选中 底 --surface / 边 --line；悬浮与选中 底、边都是 --surface-hover，边线 1px ---- */
:root[data-theme="dark"] { --card-bg: var(--surface); --card-border: var(--line); --card-active: var(--surface-hover); }
.work-card, .stat-tile, .episode-item, .asset-card, .episode-add, .asset-card-add, .work-card-new {
  background: var(--card-bg); border-color: var(--card-border); border-width: 1px; border-radius: 12px;
}
.stat-tile:hover, .stat-tile.active,
.episode-item:hover, .episode-item.active,
.asset-card:hover, .episode-add:hover:not(:disabled), .asset-card-add:hover:not(:disabled) {
  background: var(--card-active); border-color: var(--card-active);
}
/* 选中态不再描绿边，只靠底色区分；页签卡的标签字仍用绿色提示当前页签 */
.stat-tile.active .stat-label { color: var(--green); }
.episode-item.active .episode-code { color: var(--green); }

/* 「待生成」占位框与顶部三张模式卡，同一套卡片规范。
   它不再是按钮，所以没有悬浮态：原来那条 hover 会把底色和边框都换成卡片色，
   鼠标一放上去这块图片区域就跟卡片糊成一片，看着像整块没了。 */
.asset-placeholder { background: var(--card-bg); border: 1px dashed var(--card-border); border-radius: 8px; }
.asset-placeholder.busy { border-color: var(--line-strong); }
.mode-switch { gap: 6px; padding: 0; border: 0; background: transparent; }
.mode-switch .mode-card { border: 1px solid var(--card-border); border-radius: 999px; background: var(--card-bg); }
.mode-switch .mode-card:not(.active):hover:not(:disabled) { border: 1px solid var(--card-active); background: var(--card-active); }
.mode-switch .mode-card.active { border: 1px solid var(--card-active); background: var(--card-active); }
.mode-switch .mode-card:disabled { opacity: .55; }
/* 「选择项目」不跟模式卡一个长相：它原来就是因为长得像卡片才在顶栏里找不到。
   实色块那套写在前面（搜 .pick-project:active），这里只保证不被卡片样式盖回去。 */

/* 五张页签卡（含「全部生成」）完全同一套：不再虚线、不再绿边 */
.stat-tile { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px; }
.stat-tile:hover, .stat-tile.active { background: var(--card-active); border-color: var(--card-active); }

/* 删除小按钮（项目卡、资产卡同款）：默认无框，悬浮才出框 */
.asset-delete, .work-head .work-delete, .work-head .work-rename { width: 28px; height: 28px; min-height: 0; padding: 0; display: grid; place-items: center; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--muted); }
.work-head .work-rename:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.work-head .work-rename:disabled { opacity: .35; cursor: not-allowed; }
.asset-delete:hover:not(:disabled), .work-head .work-delete:hover:not(:disabled) { color: var(--red); border-color: var(--red); background: var(--red-soft); }
/* 资产详情卡没有悬浮态：直接用悬浮那档当默认色 */
.asset-card:not(.asset-card-add), .asset-card:not(.asset-card-add):hover { background: var(--card-active); border-color: var(--card-active); }

/* 弹窗右上角的 ×：1:1 方块，默认无框无底，悬浮出纯色底 */
.icon-command { width: 32px; height: 32px; min-height: 0; padding: 0; display: grid; place-items: center; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--muted); font-size: 22px; line-height: 1; }
.icon-command:hover:not(:disabled) { background: var(--card-active); border-color: var(--card-active); color: var(--text); }

/* ---- 运营数据页 ---- */
/* 控制台：用户 2026-09-13 给的图（icon-console.png），mask + currentColor；原来手画的柱状图 svg 撤了。 */
.sidebar-item[data-icon="metrics"] .sidebar-icon { -webkit-mask-image: url("/static/icon-console.png"); mask-image: url("/static/icon-console.png"); }
.metrics-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.metrics-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 运营数据的「刷新」（用户 2026-09-11）：贴在左边「运营数据」标题右边；主按钮配色；
   文字左边一枚刷新图标，点一下转一整圈——告诉用户点到了。图标是 mask，颜色跟着按钮字色走，
   深浅主题不用各配一张。转圈只在点击那一下，悬浮、聚焦一律不动（按钮那条规矩）。 */
.metrics-title { display: flex; align-items: center; gap: 14px; }
.metrics-refresh { display: inline-flex; align-items: center; gap: 6px; }
/* 导出 CSV 的日期区间弹窗：两个日期框和时间线筛选那一套同一个样子。 */
.metrics-export-dialog { width: min(480px, calc(100vw - 32px)); }
.metrics-custom-range-dialog { width: min(520px, calc(100vw - 32px)); }
.metrics-export-body { padding: 6px 20px 4px; }
.metrics-export-body .field span { display: block; margin-bottom: 6px; color: var(--muted); font-size: var(--font-base); }
/* 报表类型下拉下面那行说明：说清这张表里有什么，运营选之前就知道。 */
.metrics-export-body .field + .field { margin-top: 14px; }
.metrics-export-body select { width: 100%; }
.metrics-export-hint { display: block; margin-top: 8px; color: var(--muted); font-size: var(--font-sm); line-height: 1.5; }
.metrics-range-retention { display: block; margin-top: 8px; color: var(--muted); font-size: var(--font-sm); line-height: 1.5; }
.metrics-refresh-icon {
  width: 14px;
  height: 14px;
  flex: none;
  background: currentColor;
  -webkit-mask: url("/static/icon-refresh.png") no-repeat center / contain;
  mask: url("/static/icon-refresh.png") no-repeat center / contain;
}
.metrics-refresh-icon.is-spinning { animation: metrics-refresh-spin .6s cubic-bezier(.22,.61,.36,1); }
@keyframes metrics-refresh-spin { to { transform: rotate(360deg); } }
/* 同一颗刷新还用在控制台「用户」页的工具栏和用户时间线弹窗里（用户 2026-09-11），类名和图标照搬这一份。
   时间线那颗贴在「操作」下拉框右边，配色也和运营数据那颗一样（用户 2026-09-14）：弹窗在控制台外面拿不到控制台那套
   主按钮配色，所以 :is(#consolePage, .metrics-timeline-dialog) 那条把弹窗也算进去。 */
.metrics-timeline-filters .metrics-refresh { flex: none; min-height: 36px; padding: 0 12px; }

/* 用户表「在线状态」「账号状态」两格表头能点着筛（用户 2026-09-11）：字后面一枚上下箭头表示能点，点开一个小菜单。
   它是表头里的入口不是独立操作按钮，按下不缩（全局那串排除名单里有它）；
   筛着某一项时字和箭头换强调色，一眼看出这张表是筛过的。箭头是 mask，颜色跟着字走。 */
#consolePaneUsers .console-th-filter {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
}
#consolePaneUsers .console-th-filter:hover:not(:disabled) { background: transparent; }
#consolePaneUsers .console-th-filter.active { color: var(--accent-text); }
.console-th-filter-icon {
  width: 12px;
  height: 12px;
  flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 4.5 6 2l2.5 2.5M3.5 7.5 6 10l2.5-2.5' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 4.5 6 2l2.5 2.5M3.5 7.5 6 10l2.5-2.5' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
}
/* 菜单挂在 body 上按表头的位置现摆，表格外那层 overflow: auto 裁不到它。
   选项不换悬浮底色（按钮那条规矩），选中的那项换强调色、加粗；右边是挑它之后剩几条。 */
.console-th-filter-menu {
  position: fixed;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 132px;
  padding: 6px;
  box-sizing: border-box;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 8px 24px var(--dialog-shadow);
}
.console-th-filter-menu[hidden] { display: none; }
.console-th-filter-menu .console-th-filter-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 32px;
  padding: 6px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-size: var(--font-base);
  text-align: left;
}
.console-th-filter-menu .console-th-filter-option:hover:not(:disabled) { background: transparent; }
.console-th-filter-option small { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
/* 累计充值金额的菜单：上面筛已充值 / 未充值，分隔线下面按金额排（2026-10-20）。 */
.console-th-filter-divider { height: 1px; margin: 4px 6px; background: var(--line); }
.console-th-filter-menu .console-th-filter-option[aria-checked="true"] { color: var(--accent-text); font-weight: 600; }
.metrics-tools button.active { border-color: var(--green); color: var(--green); }
.metrics-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
/* 概览三个板块并排一行（用户 2026-09-11）：用户数据四格、用户事件五格、订阅与工单五格，宽度按 4:5:5 分，
   十四个格子一样宽。每个板块里的格子按个数平分、不换行（--tiles 由 JS 按格子数写），宽度跟着板块缩，
   数字用等宽字、放不下就截省略号。板块是 panel，格子退一档用 --surface-soft，不然格子和面板同色、只剩一圈线。窄屏上下叠。 */
.metrics-tile-groups { display: grid; grid-template-columns: 4fr 5fr 5fr; gap: 16px; margin-bottom: 16px; }
#consolePaneMetrics .metrics-tile-groups > .panel { min-width: 0; margin: 0; }
.metrics-tile-group .metrics-tiles { margin-bottom: 0; gap: 8px; grid-template-columns: repeat(var(--tiles, 4), minmax(0, 1fr)); }
.metrics-tile-group .metrics-tile { min-width: 0; padding: 12px 10px; background: var(--surface-soft); }
/* 20 → 18：字体换成 Inter 之后数字宽一成，1707 宽时「1,234.56」差 9 像素放不下（2026-10-12）。 */
.metrics-tile-group .metrics-tile strong { font-size: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.metrics-tile-group .metrics-tile span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1100px) { .metrics-tile-groups { grid-template-columns: 1fr; } }
.metrics-tile { display: grid; gap: 4px; padding: 14px 16px; border: 1px solid var(--card-border); border-radius: 12px; background: var(--card-bg); }
.metrics-tile strong { font-size: 24px; font-variant-numeric: tabular-nums; }
.metrics-tile span { color: var(--muted); }
#consolePaneMetrics .panel { margin-bottom: 16px; border-color: var(--card-border); border-radius: 12px; background: var(--card-bg); }
.metrics-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: stretch; }
#consolePaneMetrics .metrics-grid > .panel { min-width: 0; margin-top: 0; margin-bottom: 0; display: flex; flex-direction: column; }
@media (max-width: 1100px) { .metrics-grid { grid-template-columns: 1fr; } }
.metrics-chart { display: flex; align-items: flex-end; gap: 4px; height: 160px; padding-top: 8px; overflow-x: auto; }
/* 柱子是按钮：点得了、Tab 也聚焦得到。按钮的默认底色和边框要清掉，
   不然每根柱子后面会多一个灰方块。 */
.metrics-bar { flex: 0 1 36px; min-width: 14px; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; min-height: 0; padding: 0; gap: 4px; border: 0; border-radius: 4px; background: transparent; font: inherit; cursor: pointer; }
.metrics-bar span { display: block; width: 100%; min-height: 2px; border-radius: 3px 3px 0 0; background: var(--green); opacity: .8; transition: opacity .15s; }
.metrics-bar small { color: var(--muted); font-size: 11px; white-space: nowrap; }
.metrics-bar:hover span { opacity: 1; }
/* 选中那根：填满不透明 + 一圈底色，一眼看出在看哪天。 */
.metrics-bar.active { background: var(--accent-soft); }
.metrics-bar.active span { opacity: 1; background: var(--accent, var(--green)); }
.metrics-bar.active small { color: var(--accent-text, var(--accent)); font-weight: 650; }
.metrics-bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 图上方那一条：没选中时一句提示，选中了摆开那天的几个数。 */
.metrics-day-detail { display: flex; align-items: center; flex-wrap: wrap; gap: 18px; min-height: 34px; margin: 0 0 10px; }
.metrics-day-hint { color: var(--muted); font-size: var(--font-base); }
.metrics-day-date { font-size: 15px; font-variant-numeric: tabular-nums; }
.metrics-day-cell { display: flex; align-items: baseline; gap: 6px; }
.metrics-day-cell strong { font-size: 16px; font-variant-numeric: tabular-nums; }
.metrics-day-cell small { color: var(--muted); font-size: var(--font-base); }
.metrics-day-clear { margin-left: auto; min-height: 0; padding: 4px 12px; font-size: var(--font-base); }
.metrics-table { width: 100%; border-collapse: collapse; }
.metrics-table th, .metrics-table td { padding: 7px 10px; border-bottom: 1px solid var(--cell-line); text-align: left; vertical-align: middle; font-variant-numeric: tabular-nums; }
.metrics-table th { color: var(--muted); font-weight: 600; }
/* 消耗与花费：模式一行合计，功能缩进挂在下面，合计行压在最后。
   除第一列外全是数字，一律靠右——数位对齐才看得出量级差。 */
.metrics-usage-table td:not(:first-child), .metrics-usage-table th:not(:first-child) { text-align: right; }
.metrics-usage-mode td { font-weight: 650; }
.metrics-usage-feature td:first-child { padding-left: 26px; color: var(--muted); }
.metrics-usage-total-row td { font-weight: 700; }
.metrics-usage-total { color: var(--muted); font-size: var(--font-base); font-variant-numeric: tabular-nums; }
.metrics-grid .metrics-scroll { flex: 0 0 320px; height: 320px; min-height: 320px; max-height: 320px; overflow: auto; }
.metrics-panel-head { min-height: 36px; display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.metrics-panel-head h3 { flex: none; margin: 0; }
.metrics-filter { width: min(220px, 100%); min-width: 0; }
.metrics-empty { height: 64px; color: var(--muted); text-align: center !important; }
.metrics-dropped { color: var(--muted); font-size: 13px; }
.link-button { border: 0; background: transparent; padding: 0; min-height: 0; color: var(--green); cursor: pointer; text-decoration: underline; }
/* 「用户」+ 邮箱框 + 查看 + 提示语必须一行放完。原来是 wrap，窄一点提示语
   就掉到第二行，标题跟着往上跳。改成不换行，让邮箱框去挤。 */
.metrics-timeline-head { justify-content: space-between; flex-wrap: nowrap; }
.metrics-timeline-head h3 { flex: none; }
/* 邮箱框和「查看」紧跟在标题后面，靠左排。 */
.metrics-timeline-head { justify-content: flex-start; }
.metrics-timeline-head .metrics-tools { flex: 0 1 auto; min-width: 0; flex-wrap: nowrap; justify-content: flex-start; }
.metrics-timeline-head .metrics-tools input { flex: 0 1 200px; min-width: 90px; }
.metrics-timeline-head .metrics-tools button { flex: none; }
.metrics-timeline-head h3 { margin: 0; }
/* 整张时间线是一个 grid，每一行 <li> 用 subgrid 继承同一套列：各行整列对齐。
   六列依次是：时间、操作、内容、积分明细（进出）、积分余额、说明。积分余额原来挂在「积分明细」底下、
   表头横跨两列；用户 2026-09-12 要它单独成一列、有自己的表头，所以每列都只占一格。

   **列宽只看弹窗有多宽，不看里面写了什么**（用户 2026-09-10：「时间线的列，要固定，不要移动」）。
   按内容定宽做过一版（max-content / fit-content）：钱那几行比事件晚一步从云端回来、换筛选、换人，
   列宽都跟着变，整张表左右跳。更早那版全写死 px，「内容」168px 截掉长项目名，右边却空一大块。

   所以六列按比例分满整行（用户：「横向空间都要利用起来」）：内容三份、说明五份，其余一份上下。
   说明那列原来也是三份，「生成成功 · 2048x1152 · 按实际用量结算，实扣 9 积分」这种一句
   就截掉了（用户 2026-09-11：「说明都显示不全」），模型又改写成「供应商·上游型号」，
   所以它比内容多两份，弹窗也一起放宽。
   最小宽度是 2026-09-10 用 14px 字实测的最长文字，窄屏时这几列不截字，挤的是内容和说明：
   时间「12-31 23:59:59」91px、操作「视频台 · 生成视频提示词」168px、
   进出「+12,000,000 积分」108px、余额原来写「余额 12,013,245」98px，单独成列后只写数，更窄。
   往操作列加更长的名字，记得回来把 172px 调大。 */
.metrics-timeline {
  display: grid;
  grid-template-columns: minmax(100px, 1fr) minmax(172px, 1.3fr) minmax(0, 3fr) minmax(112px, 1fr) minmax(104px, 1fr) minmax(0, 5fr);
  column-gap: 12px;
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 520px;
  overflow: auto;
}
.metrics-timeline > li { grid-column: 1 / -1; }
.metrics-event { display: grid; grid-template-columns: subgrid; align-items: baseline; padding: 7px 0; border-bottom: 1px solid var(--cell-line); }

/* 定宽之后每一格都要自己截断，否则长内容会把这一格撑开、又把后面挤歪。 */
.metrics-event > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.metrics-event time { color: var(--muted); font-variant-numeric: tabular-nums; }
.metrics-event small { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.metrics-event-error strong, .metrics-event-api_error strong, .metrics-event-shotlist_failed strong, .metrics-event-assets_failed strong, .metrics-event-expand_failed strong { color: var(--red); }
.metrics-event-session_start strong, .metrics-event-project_create strong, .metrics-event-image_queue_start strong { color: var(--green); }
/* 时间线里那几行钱：花了多少、还剩多少是运营最想一眼看到的两个数，
   所以不跟着 small 一起变灰，等宽数字方便上下对齐。 */
.metrics-event-credit .metrics-credit-amount,
.metrics-event-credit .metrics-credit-balance { color: var(--text); font-variant-numeric: tabular-nums; }
/* 积分余额每一行都写（用户 2026-09-12：「积分要每个栏位都显示」），不是钱的那几行也一样用等宽数字，整列上下对齐。 */
.metrics-event .metrics-credit-balance { font-variant-numeric: tabular-nums; }
/* 「积分明细」「积分余额」各是一列（用户 2026-09-12：「积分明细列后面，单独增加一列积分余额」）。
   钱那几行两格各放一个数；事件行这两格是空的，照样各占一格，说明才不会挪进来。 */
.metrics-event-credit.is-earned .metrics-credit-amount { color: var(--success); }
.metrics-event-credit.is-failed strong { color: var(--red); }
/* 时间线的表头：钉在列表顶上——这个列表自己滚，不钉住的话往下翻两屏
   就不知道哪一列是哪一列了。底色要实的，否则滚上来的行会从它底下透出来。 */
.metrics-timeline-headings {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 6px 0;
  background: var(--surface);
  color: var(--muted);
  font-size: 12px;
}
/* 表头那条带底，贴着筛选栏的分隔线往下铺。原来列表容器顶上留了 8px 内边距，表头悬在
   一段空白下面，看着像和上面的区域之间裂了一条缝（用户 2026-09-11）；现在内边距归零、
   带底左右各多出 20px 盖住列表两侧的内边距，表头就是从筛选栏底下长出来的一条。
   画在伪元素上而不是直接改背景：这一行是 subgrid 的一员，加负外边距会把列对不齐。 */
.metrics-timeline-headings::before {
  content: "";
  position: absolute;
  inset: 0 -20px;
  z-index: -1;
  background: var(--table-header-bg);
}
#metricsTimelineMore { margin-top: 10px; }

/* ---- 语言切换器：国旗 + 语言名，下拉列出五种 ---- */
/* 有新版本：一颗药丸底，里面再套一颗药丸按钮。重建镜像后开着旧页面的人
   才看得到它，正常情况下这一格是空的。 */
/* 用户 2026-09-12 定的：不要外框线；深色下药丸白底黑字、「刷新」那块黑底白字，浅色反过来。
   药丸底取生成类那一档（深色是白、浅色是黑），「刷新」取它反过来的那一档，两套主题不用各写一份。
   原来那圈 #8B7DFF 描边（2026-09-10）随这次一起撤了。
   高度和右边那排按钮一样是 34（用户 2026-10-05：「要和右侧的按钮高度一致」，选了整排统一 34：右边「中文」也从 36 收到 34，
   主题、铃铛、积分本来就是 34）；「刷新」和 X 跟着加高，上下各留 3 像素。横向尺寸不动：手机上顶栏本来就挤，药丸再宽就把右边几颗压扁了。 */
.update-banner {
  --update-bg: var(--command-create-bg);
  --update-text: var(--command-create-text);
  --update-button-bg: var(--command-create-text);
  --update-button-text: var(--command-create-bg);
  display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 3px 0 10px; box-sizing: border-box;
  border: 0; border-radius: 999px; background: var(--update-bg);
  color: var(--update-text); font-size: 13px; font-weight: 400; line-height: 1; white-space: nowrap;
}
/* 文字左边那枚更新图标（用户 2026-09-12 给的图），mask 跟着字色走。 */
.update-banner-icon {
  flex: none; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: url("/static/icon-update.png") no-repeat center / contain;
  mask: url("/static/icon-update.png") no-repeat center / contain;
}
.update-banner-text { font-weight: 400; }
.update-banner-button {
  display: inline-flex; align-items: center; height: 28px; min-height: 0; padding: 0 10px; border: none; border-radius: 999px;
  background: var(--update-button-bg); color: var(--update-button-text); font-size: 13px; font-weight: 400; line-height: 1;
}
/* 悬浮、聚焦和平时同一个样子，只有按下缩小（全站按钮规矩）。 */
.update-banner-button:hover, .update-banner-button:focus-visible { background: var(--update-button-bg); color: var(--update-button-text); }
.update-banner-button:active { translate: 0 1px; scale: .98; }
/* 最右边的 X：只收起药丸，不刷新。悬浮、聚焦同样不换样子。 */
.update-banner-close,
.update-banner-close:hover,
.update-banner-close:focus-visible {
  display: inline-flex; flex: none; align-items: center; justify-content: center;
  width: 22px; height: 28px; min-height: 0; padding: 0; border: none; border-radius: 999px;
  background: transparent; color: var(--update-text);
}
.update-banner-close .close-icon { width: 11px; height: 11px; }
.update-banner-close:active { translate: 0 1px; scale: .98; }

.lang-switch { position: relative; flex: none; }
/* 「中文」和主题、铃铛、套餐订阅、积分同一身（用户 2026-10-08：「统一」）：12px 圆角，深色半透明黑底 + --header-control-border，
   浅色那一套和主题 / 铃铛同一条（「浅色主题：顶栏跟着页面是浅底」那块），悬浮不变。原来是 8px 圆角、卡片底（深色下比旁边几颗亮一档）、
   浅色下描边是深色的。找回 / 重置密码页右上角浮着的那颗只吃 auth.css 里自己那一份，不受这里影响。 */
.lang-switch .lang-current { display: inline-flex; align-items: center; gap: 8px; height: 34px; min-height: 0; box-sizing: border-box; padding: 0 10px 0 8px; border: 1px solid var(--header-control-border); border-radius: 12px; background: rgba(0, 0, 0, .28); color: var(--text, #f2f2f2); font: inherit; cursor: pointer; }
.lang-switch .lang-current:hover { border-color: var(--header-control-border); background: rgba(0, 0, 0, .28); }
.lang-switch .lang-flag { display: inline-flex; width: 22px; height: 15px; border-radius: 2px; overflow: hidden; box-shadow: 0 0 0 1px rgba(255, 255, 255, .12); }
.lang-switch .lang-flag svg { width: 100%; height: 100%; display: block; }
.lang-switch .lang-caret { color: var(--muted, #b3b3b3); font-size: 11px; }
.lang-switch .lang-menu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; min-width: 160px; margin: 0; padding: 6px; list-style: none; border: 1px solid var(--card-border, #262626); border-radius: 10px; background: var(--card-bg, var(--surface)); box-shadow: 0 12px 32px rgba(0, 0, 0, .45); }
.lang-switch .lang-menu li { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; cursor: pointer; white-space: nowrap; }
.lang-switch .lang-menu li:hover, .lang-switch .lang-menu li.active { background: var(--card-active, var(--surface-hover)); }
.lang-switch .lang-menu li.active { color: var(--green, #69d3a4); }
.lang-switch-floating { position: fixed; top: 16px; right: 16px; z-index: 60; }

/* 顶栏项目信息：芯片只排一行，超出就裁，别把项目名挤掉（英文法文的芯片更长） */

/* ---- 左栏顶部的「一键生成」 ----
   分镜表和角色/道具/场景提示词在后端本来就是一次任务，入口也只留这一个。 */
.one-click-command {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  /* 和右边四张页签卡同高：高度交给网格行撑，不写死。 */
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--primary-bg);
  border-radius: 8px;
  background: var(--primary-bg);
  color: var(--on-primary);
  font: inherit;
  font-weight: 400;
  cursor: pointer;
}

.one-click-command .cost-chip { margin-left: 0; }
.one-click-command .one-click-title { font-size: 15px; }

.one-click-command:hover:not(:disabled) {
  border-color: var(--primary-hover);
  background: var(--primary-hover);
}

/* 没得可生成时不该还是一颗醒目的绿按钮，退成描边灰。 */
.one-click-command:disabled {
  border-color: var(--card-border);
  background: transparent;
  color: var(--muted);
  cursor: not-allowed;
}

.detail-cta-hint { margin: 0; color: var(--muted); font-size: var(--font-base); }

/* ---- 就地改名：分集标题和资产名共用 ---- */
.detail-episode-code { margin-right: 8px; color: var(--muted); font-weight: 600; }

.rename-text { cursor: text; }

/* 笔平时是淡的，鼠标进到那一行才亮起来——名字首先是名字，其次才是入口。 */
.rename-pen {
  margin-left: 6px;
  width: 22px;
  height: 22px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  vertical-align: middle;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--muted);
  opacity: .45;
  cursor: pointer;
  transition: opacity .12s ease, color .12s ease;
}

.detail-head:hover .rename-pen,
.asset-card:hover .rename-pen,
.rename-pen:focus-visible { opacity: 1; }
.rename-pen:hover:not(:disabled) { color: var(--green); background: var(--green-soft); opacity: 1; }
.rename-pen:disabled { cursor: not-allowed; opacity: .2; }

.rename-input {
  min-width: 140px;
  max-width: 100%;
  padding: 1px 6px;
  border: 1px solid var(--green);
  border-radius: 4px;
  background: var(--input-bg, var(--surface-soft));
  color: var(--text);
  font: inherit;
}

.rename-input:focus { outline: none; }

/* ---- 资产子页签：资产库里分角色/道具/场景，本集页里再多一个分镜 ---- */
.asset-subtabs {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  margin-bottom: 10px;
}

.asset-subtab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font: inherit;
  font-size: var(--font-base);
  cursor: pointer;
}

.asset-subtab:hover { border-color: var(--control-hover-border); background: var(--surface-hover); }
.asset-subtab.active { border-color: var(--green); background: var(--green-soft); color: var(--green); }
.asset-subtab b { font-variant-numeric: tabular-nums; font-weight: 700; }

/* 本集顶部第一层：分镜 / 资产预览两段滑轨。和剧本创作页顶部那条同一套做法，
   选中态由会滑的滑块画。比原来那排小标签大一号——它是分层，不是筛选。 */
.episode-section-row { display: flex; align-items: center; gap: 6px; margin: 2px 0 8px; }
/* 这条（分镜 / 资产预览）原来自己抄了一整份颜色和形状，飘成了另一个样子。
   现在登记进「滑轨页签」那份共用样式，这里只留它排布上的差别。 */
.episode-rail { flex: none; }
.episode-rail-tab { min-width: 104px; padding: 9px 18px; }
/* 滑轨这一行也可能带上「全部选择 / 清空已选 / 生图提示词」（看分镜时），
   借资产页签行那套尺寸，两行的按钮才一样高。 */
.episode-section-row .subtab-pick-command,
.episode-section-row .storyboard-delete-command { height: 30px; min-height: 30px; padding: 0 10px; white-space: nowrap; }
.episode-section-row .prompt-command { margin-left: 8px; height: 30px; min-height: 30px; padding: 0 12px; white-space: nowrap; }

/* 本集页里的资产网格要跟着详情区一起滚，不能把分镜表挤没。 */
.episode-subtabs { margin-top: 2px; }
/* 分镜那一层的「删除 / 全部选择 / 清空已选 / 生图提示词」自己占一行（和资产预览那一层一样），
   前面没有页签，那道竖线分隔就不用画了。 */
.episode-shot-controls > .subtab-command-separator { display: none; }
.episode-asset-grid { flex: 1; min-height: 0; overflow-y: auto; align-content: start; }
.episode-asset-note { margin: 0 0 8px; }

/* 按钮挪进正文标题行后：横排、和标题一样高，副标题跟在后面而不是另起一行。 */
.detail-head .one-click-command {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  flex-direction: row;
  gap: 8px;
  height: 30px;
  min-height: 30px;
  max-height: 30px;
  box-sizing: border-box;
  padding: 0 12px;
  border-radius: 6px;
}

.detail-head .one-click-command .one-click-title { font-size: var(--font-base); }
/* 拆过的集主按钮接着往下走（生成剩余提示词 / 前往图片生成），重拆挪到这颗描边的「重新拆分」（2026-10-04），
   和左边「分镜设置」同一种描边小按钮的样子。 */
.detail-head .episode-redo-command, #mainView .detail-head .episode-redo-command {
  flex: none; height: 30px; min-height: 30px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--surface-soft); color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.detail-head .episode-redo-command:disabled { opacity: .5; cursor: not-allowed; }

/* 分镜设置（2026-09-16，按集设）：本集标题旁、「生成分镜」左边一颗描边小按钮，点开和药丸里画面比例同一种浮层；
   自定义那一档在浮层底部展开秒数范围和分镜数。位置相对浮层要有个锚。 */
.episode-shot-type { position: relative; margin-left: auto; }
.detail-head .episode-shot-type-button, #mainView .detail-head .episode-shot-type-button {
  height: 30px; min-height: 30px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); font-size: 13px; font-weight: 600;
}
/* 类型名那一格至少三个字宽，刚好装下「长镜头」（用户 2026-09-24：「不要因为从自动变为长镜头，导致宽度跳变」「能刚好容纳长镜头就可以了」）。 */
.episode-shot-type-value { flex: none; min-width: 3em; white-space: nowrap; text-align: left; }
/* 「自定义」后面那个 >：共用的下拉箭头转个方向。 */
.episode-shot-type-next { display: inline-flex; color: var(--muted); }
.episode-shot-type-next svg { transform: rotate(-90deg); }
/* 填秒数的小框：挂在选项框右边（放不下挂左边），top / left 由 JS 按「自定义」那一行摆。 */
.ratio-popover.episode-shot-type-side { position: fixed; top: auto; right: auto; left: auto; z-index: 31; width: 280px; min-width: 0; }
.episode-shot-type-side .episode-shot-type-custom { margin-top: 0; border-top: 0; }
.episode-shot-type-prefix { color: var(--muted); font-weight: 500; }
/* 本集那一栏 overflow: hidden，浮层 fixed 才不被裁；top / right 由 JS 按按钮位置摆。
   两个类名叠着写：共用的 .ratio-popover 在文件更后面、写的是 absolute + top 40 + left 0，单类名会输给它。 */
/* 宽度和上面那颗「分镜设置」按钮一样（用户 2026-09-24：「弹窗比上方的下拉框宽太多了」），由 JS 按量出来的按钮宽度写；
   这里的数只是量到之前那一帧的兜底。自定义那一档展开的秒数表单也在这个宽度里。 */
.ratio-popover.episode-shot-type-popover { position: fixed; top: auto; right: 8px; left: auto; z-index: 30; width: 180px; min-width: 0; }
/* 「分镜设置」后面那枚 i：悬浮看各类型说明。和前缀同色，不抢按钮上的类型名。 */
.episode-shot-type-info { display: inline-flex; align-items: center; margin: 0 6px 0 3px; color: var(--muted); cursor: help; vertical-align: -2px; }
.episode-shot-type-info svg { width: 14px; height: 14px; }
.episode-shot-type-tip-line + .episode-shot-type-tip-line { margin-top: 6px; }
.episode-shot-type-tip-line strong { font-weight: 600; }
/* 分镜表里画面段落的小标签（2026-09-24，叙事方式）：建立 / 登场 / 关键信息 / 余韵 / 转场。 */
/* 叙事作用标签：镜号下面单独一行（2026-09-24 从概要文字前面挪过来），四个字一枚。 */
.shot-moment-tag { display: inline-block; margin-top: 5px; padding: 0 6px; border-radius: 4px; background: var(--accent-soft); color: var(--accent-text); font-size: 12px; line-height: 18px; font-weight: 600; white-space: nowrap; }
/* 共用的选项行是「图标槽 34px + 文字 + 勾」三格，这里没有图标：两格，不然文字掉进图标槽、勾压在文字上 */
#mainView .episode-shot-type-popover .ratio-option, .episode-shot-type-popover .ratio-option { grid-template-columns: 1fr auto; }
/* 分镜设置里这一集用不了的那一档（单句台词装不进它的单个镜头）：灰掉、点不了，原因写在选项文字里。 */
#mainView .episode-shot-type-popover .ratio-option:disabled, .episode-shot-type-popover .ratio-option:disabled { color: var(--muted); cursor: not-allowed; white-space: normal; }
/* 本集台词装不下每集时长时，标题下面那一行说明。 */
#mainView .episode-timing-note, .episode-timing-note { margin: 6px 0 0; font-size: 13px; line-height: 1.6; }
.episode-shot-type-custom { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; padding: 10px 12px 8px; border-top: 1px solid var(--line); }
.episode-shot-type-range { display: flex; gap: 10px; }
.episode-shot-type-field { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: 12px; color: var(--muted); }
/* 多叠一层类名：工作台那条 `#mainView input:not([type="checkbox"]) { border-color: transparent }` 在文件更后面，同权重会输给它 */
.episode-shot-type-field input, #mainView .episode-shot-type-custom .episode-shot-type-field input { width: 100%; height: 32px; padding: 4px 8px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); color: var(--text); }
.episode-shot-type-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.episode-shot-type-save, #mainView .episode-shot-type-save { height: 30px; min-height: 30px; padding: 0 14px; border: 0; border-radius: 6px; background: var(--primary-bg); color: var(--on-primary); font-weight: 600; cursor: pointer; }

/* ---- 右侧资产库抽屉 ----
   资产是全项目共用的参考，不该占掉工作区的主视野。收起时只留右边一条把手，
   展开时盖在内容上方，不挤压分集列表和分镜表的宽度。 */
.asset-drawer {
  /* 贴着视口顶边、盖在顶栏之上：打开时顶部的模式按钮、公告、通知和账号按钮都被挡住，
     只有横幅提示（.toast-stack，z-index 95）还在它上面。把手竖直居中，收起时不会压到顶栏。 */
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  width: min(1680px, 80vw);
  /* 慢一点，让人看清它是从右边推出来的 */
  transition: transform .36s cubic-bezier(.22, .61, .36, 1);
}

/* 收起时整体移出去，只把手留在视口内。 */
.asset-drawer.collapsed { transform: translateX(calc(100% - 56px)); }

/* 把手（用户原话：太不起眼，照他给的样式改）：竖长的半个胶囊，上面一枚图标、中间竖排的字、
   下面一枚双线左箭头。**和抽屉是一体的**（用户原话）：同一块底色、同一道描边，右边不封口、
   压掉抽屉那道左边框，直接接进去；只有左边三个角是圆的。打开时字和图标换成强调色，
   箭头转过来指向右边（收起），底色不变——变了就又成了一张单独的卡片。
   颜色由底下 #mainView #assetDrawer 那套「固定色块、按下缩小」的规则统一画，
   这里只定两个变量和几何；描边和投影在那套规则后面再补一条，不然会被它抹成透明。 */
.asset-drawer-toggle {
  --asset-button-bg: var(--surface);
  --asset-button-text: var(--text);
  position: relative;
  z-index: 1;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 56px;
  margin-right: -1px;
  padding: 18px 0 16px;
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: 18px 0 0 18px;
  font: inherit;
  cursor: pointer;
  align-self: center;
}
/* 连接处的拐角就是直角：试过挖内圆角，用户看了说改回去。
   打开时不换色（用户原话「保持原色」）：状态只靠箭头转向表示。 */
.asset-drawer-toggle-label {
  /* 竖排，一字一行，像书脊上的标题 */
  writing-mode: vertical-rl;
  text-orientation: upright;
  font-size: var(--font-base);
  font-weight: 650;
  letter-spacing: 3px;
  line-height: 1;
}
/* 两枚图标：白色透明 PNG 当遮罩，颜色取 currentColor。 */
.asset-drawer-toggle-icon,
.asset-drawer-toggle-chevron {
  flex: none;
  background-color: currentColor;
  transition: transform .28s cubic-bezier(.22, .61, .36, 1);
}
.asset-drawer-toggle-icon { width: 24px; height: 24px; -webkit-mask: url("/static/icon-asset-library.png") center / contain no-repeat; mask: url("/static/icon-asset-library.png") center / contain no-repeat; }
.asset-drawer-toggle-chevron { width: 18px; height: 18px; -webkit-mask: url("/static/icon-chevron-double-left.png") center / contain no-repeat; mask: url("/static/icon-chevron-double-left.png") center / contain no-repeat; }
/* 抽屉从右边推出来：收起时箭头指左（拉出来），打开时转过来指右（收回去）。 */
.asset-drawer:not(.collapsed) .asset-drawer-toggle-chevron { transform: rotate(180deg); }

.asset-drawer-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 14px 16px;
  border-left: 1px solid var(--line);
  border-radius: 14px 0 0 14px;
  background: var(--bg);
  box-shadow: -12px 0 28px rgba(0, 0, 0, .28);
  overflow: hidden;
}

/* scrollbar-gutter 必须常驻：分镜图 / 视频一页 20 条会撑出滚动条，角色 / 道具 / 场景
   条目少时没有，网格宽度就差一条滚动条（15px）。卡片是 16:9，宽度一变高度跟着变，
   来回切页签整排卡片会跳一下。永远留出这条槽，五个页签的列宽完全相同。 */
.asset-drawer-body .asset-grid { flex: 1; min-height: 0; overflow-y: auto; scrollbar-gutter: stable; align-content: start; }

/* 抽屉够宽了，两列卡片各自撑开成横向矩形；窄屏退回一列。 */
.asset-drawer-body .asset-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* 行高按内容走。默认的 1fr 会把最后一行（往往只有「新增资产」一张）
     拉去填满剩余高度，看着比别的卡片高一截。 */
  grid-auto-rows: max-content;
}
@media (max-width: 1100px) {
  .asset-drawer { width: min(1680px, 94vw); }
  .asset-drawer-body .asset-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 抽屉：遮罩、搜索、分页 ---- */
.asset-drawer-scrim {
  /* 和抽屉一样盖满视口，顶栏一起压暗。 */
  position: fixed;
  inset: 0;
  z-index: 85;
  background: rgba(0, 0, 0, .35);
  cursor: pointer;
}

.asset-drawer-head { display: flex; flex-direction: column; align-items: stretch; gap: 8px; flex: none; margin-bottom: 10px; }
.asset-drawer-head .asset-subtabs { margin-bottom: 0; }
/* 第二行：分类按钮在左，搜索框紧跟其后，不再顶到最右。 */
.asset-drawer-tools { display: flex; align-items: center; justify-content: flex-start; gap: 12px; flex-wrap: wrap; }
.asset-drawer-tools .asset-search { margin-left: 0; flex: 0 1 220px; }
.asset-empty-hint { grid-column: 1 / -1; padding: 24px 0; text-align: center; }
.asset-subtab-group { display: inline-flex; align-items: center; gap: 6px; }
.asset-library-group-separator {
  width: 1px;
  height: 24px;
  flex: none;
  margin: 0 4px;
  background: var(--line-strong);
}

.asset-search { margin-left: auto; flex: 0 1 240px; min-width: 140px; }

.asset-search input {
  width: 100%;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--input-bg, var(--surface-soft));
  color: var(--text);
  font: inherit;
  font-size: var(--font-base);
}

.asset-search input:focus { outline: none; }

/* 分页栏直接复用图片生成页那套 .pagination-bar，这里只把它铺到抽屉两边，
   让上边框像图片页一样横贯整条，而不是缩在内边距里。 */
.asset-drawer-body > .pagination-bar {
  flex: none;
  margin: 12px -16px -14px;
}

/* 本集那边的资产网格也按内容排行，别把最后一行拉去填满剩余高度。 */
.episode-asset-grid { grid-auto-rows: max-content; }

/* 抽屉里的项目名：太长就省略，别把页签挤走。 */
.asset-drawer-project {
  flex: none;
  min-width: 0;
  max-width: none;
  padding-right: 0;
  margin-right: 0;
  border-right: 0;
  color: var(--text);
  font-size: 15px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- 控制台与工单 ---- */
/* .page-head 不是 flex，margin-left:auto 不起作用，这排页签是掉在标题正下方的，
   而且贴着标题没有一点间距。这里补上竖直方向的呼吸。 */
/* 控制台顶部页签：按四个区分开，每区一条槽，槽的样式和视频 / 文本模型那两条共用。
   分区之后一条槽里最多四颗，横着摆得下，不用再滚；区与区之间会自己折行。 */
.console-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  margin-top: 12px;
}
.console-tab-group { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.console-tab-group-title {
  padding-left: 10px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .04em;
}
/* 一条槽里的按钮不折行、不拉伸——四个区的按钮宽度各按自己的字数走。 */
.console-tab-rail { flex-wrap: nowrap; }
.console-tab { flex: none; height: auto; white-space: nowrap; font-size: var(--font-base); }
/* 整区都藏起来时（权限不够），别留一个只有标题的空槽。 */
.console-tab-group:not(:has(.console-tab:not(.hidden))) { display: none; }

.console-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.console-toolbar h3 { margin: 0; white-space: nowrap; }
/* 基础样式里 select 是 height:36px + padding:7px 9px。这里只压高度不改内边距的话，
   内容高度只剩 14px，装不下 14px 的字，文字上下会被切掉。跟分页那个 select 一样收内边距。 */
.console-toolbar select, .console-toolbar input { height: 30px; }
.console-toolbar select { padding-top: 3px; padding-bottom: 3px; }
/* 最长的选项也就「全部状态」四个字，撑满默认宽度会把标题挤到换行。 */
#consoleTicketFilter { width: 116px; }
.console-toolbar input { flex: 0 1 220px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--input-bg, var(--surface-soft)); color: var(--text); font: inherit; }
.console-models-foot { margin: 12px 0 0; }

/* 控制台 · 提示词：一个下拉选文件，一个大文本框改正文。正文是 Markdown，等宽字体、不自动换行的话一行几百字会横着跑，
   所以只换等宽、保留自动换行。高度给足——最长那份规范一千多行。 */
.console-prompt-editor { display: grid; gap: 10px; }
.console-prompt-editor select { max-width: 100%; }
.console-prompt-editor textarea { width: 100%; min-height: 520px; box-sizing: border-box; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; }

/* 设置页 · 我的 API 密钥：一个厂商一块，标题行右边写填没填，输入框和按钮一行。
   .console-toolbar 那套输入框样式是全局的，这里直接借用，别再抄一份。 */
.settings-api-keys { display: grid; gap: 14px; }
.settings-api-key { padding: 12px; border-radius: 8px; background: var(--surface-soft); }
.settings-api-key-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.settings-api-key-head .panel-note { margin: 0; }
.settings-api-key-filled { color: var(--success); font-variant-numeric: tabular-nums; }
.settings-api-key .console-toolbar { margin-bottom: 0; }
/* 密钥比搜索框长，给它整行；按钮跟在后面不被挤扁。框线在底下那条规则里。 */
.settings-api-key .console-toolbar input { flex: 1 1 auto; min-width: 0; }

/* 左列表右详情：工单在控制台和用户端用的是同一套。 */
.console-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 14px; align-items: start; }
@media (max-width: 1100px) { .console-split { grid-template-columns: minmax(0, 1fr); } }
.console-list, .console-scroll { max-height: 460px; overflow: auto; }
.console-list tr { cursor: pointer; }
.console-list tr.active { background: var(--green-soft); }
/* 这一条作用在 td 内层的 div 上。放 td 上会把单元格挤出表格布局，
   行分割线就画不到最后一列，右边看着是断的。 */
.console-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.console-actions button { min-height: 28px; padding: 3px 9px; }

.console-detail { min-height: 200px; padding: 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }

/* ---- 控制台：工单表和用户表铺满视口 ---- */
/* 这两页的列表是要一直翻的，固定 460px 高度在大屏上等于把下面半屏白扔了。
   改成整页不滚：表格在自己的框里滚，分页条钉在底边——和图片生成页一个套路。 */

#consolePage.console-page-full {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: 22px;
}
#consolePage.console-page-full > .page-head { flex: none; }
#consolePage.console-page-full > .console-pane:not(.hidden) {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#consolePage.console-page-full > .console-pane > .panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* 只有底部压着分页条的面板才把下内边距吃掉——分页条自己会铺到边。
   一刀切的话，像「发通知」那种以按钮收尾的面板，按钮就贴死在边框上了。 */
#consolePage.console-page-full > .console-pane > .panel:has(> .console-list-col > .pagination-bar),
#consolePage.console-page-full > .console-pane > .panel:has(> .console-split) { padding-bottom: 0; }
#consolePage.console-page-full .console-toolbar { flex: none; }
#consolePage.console-page-full .console-split,
#consolePage.console-page-full > .console-pane > .panel > .console-list-col { flex: 1; min-height: 0; }
/* 铺满时高度由外层给，别再让 max-height 把表格卡在半空。 */
#consolePage.console-page-full .console-list,
#consolePage.console-page-full .console-scroll { max-height: none; }

/* 汇总条也不该跟着表格滚，钉在工具栏下面。 */
#consolePage.console-page-full .console-order-totals { flex: none; }

/* 列表 + 分页条竖着排；表格吃掉剩余高度，分页条固定在底部。 */
.console-list-col { display: flex; flex-direction: column; min-height: 0; }
.console-list-col > .table-wrap { flex: 1; min-height: 0; }
.console-list-col > .pagination-bar { flex: none; margin: 12px 0 0; }

/* 用户页的列表直接坐在面板里，分页条铺到面板两边，上边框横贯整条；
   工单页的列表只占左半边，负边距会让它滑到右边详情栏底下，所以只给前者。 */
#consolePage.console-page-full > .console-pane > .panel > .console-list-col > .pagination-bar {
  margin: 12px -22px 0;
  border-radius: 0 0 8px 8px;
}

/* 工单页两栏都要铺满。这里从 grid 换成 flex：grid 的隐式行是 auto，
   两栏各自只有内容那么高，右边详情栏就缩成一小块浮在顶上。
   flex 的 align-items 默认就是 stretch，容器高度确定，两栏必然一样高。 */
#consolePage.console-page-full .console-split {
  display: flex;
  align-items: stretch;
  gap: 14px;
}
#consolePage.console-page-full .console-split > .console-list-col { flex: 1 1 0; min-width: 0; }
#consolePage.console-page-full .console-split > .console-detail { flex: 1.2 1 0; min-width: 0; }

/* 详情栏跟着列表一起铺满，内容多了自己滚。 */
#consolePage.console-page-full .console-detail {
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: auto;
}

/* 控制台工单详情固定为上四下一区域，输入框内直接按 Enter 回复。 */
#consolePage.console-page-full #consoleTicketDetail:not(.ticket-detail-empty) {
  display: grid;
  grid-template-rows: minmax(0, 4fr) minmax(0, 1fr);
  padding: 0;
  overflow: hidden;
}
#consolePage.console-page-full #consoleTicketDetail.ticket-detail-empty {
  display: grid;
  place-items: center;
  padding: 14px;
  overflow: hidden;
}
#consoleTicketDetail .ticket-detail-empty-hint {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  text-align: center;
}
#consoleTicketDetail .ticket-conversation {
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 14px;
  overflow: hidden;
}
#consoleTicketDetail .ticket-head,
.ticket-detail-dialog .ticket-head {
  flex: none;
  flex-wrap: nowrap;
  align-items: center;
  margin-bottom: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
#consoleTicketDetail .ticket-head h4,
.ticket-detail-dialog .ticket-head h4 { min-width: 0; line-height: 1.4; }
#consoleTicketDetail .ticket-title-block,
.ticket-detail-dialog .ticket-title-block {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ticket-title-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ticket-title-row > .status-pill { flex: none; }
.ticket-user-account {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  color: var(--muted);
  font-size: 13px;
}
.ticket-user-account-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#consoleTicketDetail .ticket-thread {
  flex: 1;
  min-height: 0;
  max-height: none;
  margin-bottom: 0;
  padding-right: 4px;
  overflow-y: auto;
}
#consoleTicketDetail .ticket-composer {
  position: relative;
  min-height: 0;
  padding: 14px;
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
}
#consoleTicketDetail .ticket-editor {
  height: 100%;
  min-height: 0;
  max-height: none;
  resize: none;
  line-height: 1.6;
}
#consoleTicketDetail .ticket-foot {
  position: absolute;
  right: 22px;
  bottom: 22px;
  margin: 0;
}

/* 通知页有两块面板：上面「发通知」按内容取高，下面「已发通知」吃掉剩下的高度，
   两条下边框这才落到视口底边，而不是停在半空。 */
#consolePage.console-page-full > #consolePaneNotices { gap: 14px; }
#consolePage.console-page-full > #consolePaneNotices > .panel:first-child { flex: none; }
#consolePage.console-page-full > #consolePaneNotices > .panel:last-child {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#consolePage.console-page-full > #consolePaneNotices > .panel:last-child > .table-wrap {
  flex: 1;
  min-height: 0;
}

/* 表格里的长文本不换行，撑不下就省略号；行高才能保持整齐。
   省略号要生效必须是固定表格布局——auto 布局下浏览器会按内容把列撑开，
   overflow 根本没机会触发，文本还是照换行。列宽因此在这里写死。 */
.console-table { table-layout: fixed; }
.console-cell-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 控制台工单表：和「我的工单」同一套 .ticket-table，只是嵌在左半栏里，
   所以要压掉外层圆角边框（框由 .console-list-col 那边给），列宽也重新分。 */
.console-ticket-table { table-layout: fixed; }
/* 这张表只占左半栏（约 545px），全宽表那套 18px 内边距会把 ID 和状态挤到出省略号。
   选择器带上 #consolePaneTickets 提权：不然同权重下后面的 .ticket-table td 会赢。 */
#consolePaneTickets .console-ticket-table th,
#consolePaneTickets .console-ticket-table td { padding-left: 12px; padding-right: 12px; }
#consolePaneTickets .console-ticket-table th:nth-child(1),
#consolePaneTickets .console-ticket-table td:nth-child(1) { width: 13%; }
#consolePaneTickets .console-ticket-table th:nth-child(2),
#consolePaneTickets .console-ticket-table td:nth-child(2) { width: 33%; }
#consolePaneTickets .console-ticket-table th:nth-child(3),
#consolePaneTickets .console-ticket-table td:nth-child(3) { width: 14%; }
#consolePaneTickets .console-ticket-table th:nth-child(4),
#consolePaneTickets .console-ticket-table td:nth-child(4) { width: 10%; }
#consolePaneTickets .console-ticket-table th:nth-child(5),
#consolePaneTickets .console-ticket-table td:nth-child(5) { width: 30%; }
/* 状态、优先级、时间这三列不换行，宁可让主题去收省略号。 */
#consolePaneTickets .console-ticket-table td:nth-child(3),
#consolePaneTickets .console-ticket-table td:nth-child(4),
#consolePaneTickets .console-ticket-table td:nth-child(5) { white-space: nowrap; }
/* 左半栏里的行要能点选，选中的高亮保留。 */
#consolePaneTickets .console-ticket-table tbody tr { cursor: pointer; }
#consolePaneTickets .console-ticket-table tbody tr.active { background: var(--green-soft); }
/* 固定表格布局下省略号才生效，主题两行都要收。 */
.ticket-subject.console-cell-ellipsis,
.ticket-subject-note.console-cell-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 兑换码列表的备注列：一行放不下就截断。 */
.console-code-table td.console-cell-ellipsis { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 用户：账号、在线状态、账号状态、余额、注册时间、最近活跃、累计充值金额、备注、操作。操作列留够，四个按钮不换行；
   备注从账号、状态、余额、注册时间几列里匀出来，放不下的省略号截断、悬浮看全文。
   在线状态那一列是 2026-09-11 加的，从账号和操作两列各匀了一点。
   最近活跃那一列是 2026-09-20 加的（「注册于」同时改叫「注册时间」），和注册时间同宽，从账号、备注、操作里匀出来。
   累计充值金额那一列是 2026-10-20 加的（用户：「在“最近活跃”和“备注”之间增加一列」），按 1366 宽（表格约 1246px）重新量过一遍：
   表头「累计充值金额」带筛选图标要 110、时间「2026-09-20 17:21」要 155、七位数余额 85、两个筛选表头 92、操作四颗按钮 280 都不能少，从账号、备注里匀出来。
   **列宽是渲染出来量的**：加列、改字都要拿真页面量一遍（第一版随手给了 12% / 25%，1366 上操作按钮就换行了）。 */
#consolePaneUsers .console-table th:nth-child(1),
#consolePaneUsers .console-table td:nth-child(1) { width: 12.5%; }
#consolePaneUsers .console-table th:nth-child(2),
#consolePaneUsers .console-table td:nth-child(2) { width: 8%; }
#consolePaneUsers .console-table th:nth-child(3),
#consolePaneUsers .console-table td:nth-child(3) { width: 8%; }
#consolePaneUsers .console-table th:nth-child(4),
#consolePaneUsers .console-table td:nth-child(4) { width: 7%; }
#consolePaneUsers .console-table th:nth-child(5),
#consolePaneUsers .console-table td:nth-child(5) { width: 12.5%; }
#consolePaneUsers .console-table th:nth-child(6),
#consolePaneUsers .console-table td:nth-child(6) { width: 12.5%; }
#consolePaneUsers .console-table th:nth-child(7),
#consolePaneUsers .console-table td:nth-child(7) { width: 9%; }
#consolePaneUsers .console-table th:nth-child(8),
#consolePaneUsers .console-table td:nth-child(8) { width: 7.5%; }
#consolePaneUsers .console-table th:nth-child(9),
#consolePaneUsers .console-table td:nth-child(9) { width: 23%; }
/* 备注那一格是链接样式的按钮，点了改备注。块级才撑得满一格、省略号才生效；
   没写过的「添加备注」用次要色，一眼和写过的分开。不画下划线：一整列下划线太吵。 */
#consolePaneUsers .console-user-note { display: block; width: 100%; text-align: left; text-decoration: none; }
/* 没写过备注只有一支笔（用户 2026-09-12）。按钮做成块级 flex、宽度收到图标大小：
   行内元素要跟着文字基线走，inline-flex 偏上 1.5px、再加 vertical-align: middle 又偏下 2px（实测）；
   块级盒子不进行框，单元格本身竖着居中，笔就正好在这一行的中线上。
   四周各留 4px 好点中，再用负外边距抵回去，行高不跟着变。 */
#consolePaneUsers .console-user-note.empty { display: flex; align-items: center; justify-content: center; width: max-content; padding: 4px; margin: -4px; color: var(--muted); }
/* 管理员：邮箱、权限、备注、添加于、操作。权限全勾上是九项四十来个字（要 593px），
   五列等分只给到 251px，一大半被省略号吃掉，看不出这个管理员到底有哪几块。
   这一列单独放宽，其余往后挤。 */
#consolePaneAdmins .console-table th:nth-child(1),
#consolePaneAdmins .console-table td:nth-child(1) { width: 22%; }
#consolePaneAdmins .console-table th:nth-child(2),
#consolePaneAdmins .console-table td:nth-child(2) { width: 40%; }
#consolePaneAdmins .console-table th:nth-child(3),
#consolePaneAdmins .console-table td:nth-child(3) { width: 16%; }
#consolePaneAdmins .console-table th:nth-child(4),
#consolePaneAdmins .console-table td:nth-child(4) { width: 10%; }
#consolePaneAdmins .console-table th:nth-child(5),
#consolePaneAdmins .console-table td:nth-child(5) { width: 12%; }
/* 放宽了也未必够（窗口一窄就又不够），所以这一格允许折行：宁可占两行，
   也不能让人看不出有哪几块权限。折行只落在「、」上，见 renderConsoleAdmins。 */
#consolePaneAdmins .console-admin-permissions { white-space: normal; line-height: 1.5; }
#consolePaneAdmins .console-admin-permissions span { white-space: nowrap; }

.ticket-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.ticket-head h4 { margin: 0; font-size: 15px; }
.ticket-close { margin-left: auto; min-height: 28px; padding: 3px 10px; }
.ticket-thread { display: flex; flex-direction: column; gap: 16px; max-height: 320px; overflow-y: auto; margin-bottom: 12px; }
.ticket-message { width: fit-content; max-width: 78%; display: flex; flex-direction: column; align-items: stretch; }
.ticket-message.ticket-peer { align-self: flex-start; }
.ticket-message.ticket-self { align-self: flex-end; }
.ticket-reply { padding: 9px 11px; border-radius: 8px; background: var(--surface-soft); }
.ticket-message.ticket-self .ticket-reply { background: var(--green-soft); }
.ticket-who { display: block; margin-bottom: 4px; color: var(--muted); font-size: 12px; }
.ticket-reply p { margin: 0; overflow-wrap: anywhere; line-height: 1.6; white-space: pre-wrap; }
.ticket-message-time { min-width: max-content; margin-top: 4px; color: var(--muted); font-size: 12px; line-height: 1.2; white-space: nowrap; }
.ticket-message.ticket-self .ticket-message-time { text-align: left; }
.ticket-message.ticket-peer .ticket-message-time { text-align: right; }
.ticket-editor { width: 100%; box-sizing: border-box; padding: 9px 11px 48px; border: 1px solid var(--line); border-radius: 6px; background: var(--input-bg, var(--surface-soft)); color: var(--text); font: inherit; resize: vertical; }
.ticket-composer .ticket-editor:focus { outline: none; border-color: var(--line); box-shadow: none; }
.ticket-foot { display: flex; justify-content: flex-end; margin-top: 8px; }

.console-models { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
.console-model-group { display: flex; flex-direction: column; gap: 8px; padding: 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-soft); }
.console-model-group h4 { margin: 0; font-size: 15px; }
/* 只管尺寸：底色和描边由文件末尾那条 #consolePage 的规则统一给（那里才压得住全局的透明边框）。 */
.console-model-group .field input,
.console-model-group .field select { height: 34px; padding: 0 10px; border-radius: 6px; color: var(--text); font: inherit; }
.console-model-group .field { min-width: 0; }

/* 模型类型：左半格填/选模型，右半格是「测试连接」。 */
.model-picker { display: flex; gap: 8px; min-width: 0; }
.model-picker > input,
.model-picker > select { flex: 1 1 50%; min-width: 0; }
.model-test-button { flex: 1 1 50%; min-width: 0; height: 34px; min-height: 34px; padding: 0 8px; white-space: nowrap; }
/* 白底黑字。**四个状态写在一条规则里**：悬浮 / 未选中 / 选中 / 聚焦一律同一样式，
   只有按下缩小（那一下由全局那条 button:active 管）。分开写就会飘。 */
.model-test-button,
.model-test-button:hover:not(:disabled),
.model-test-button:focus,
.model-test-button:focus-visible,
.model-test-button.active {
  border: 1px solid #d0d0d0;
  background: #fff;
  color: #111;
  font-weight: 600;
}
.model-test-button:disabled { opacity: .55; }

/* 密钥：右边一颗眼睛切明文。 */
.secret-field { display: flex; gap: 8px; min-width: 0; }
.secret-field > input { flex: 1; min-width: 0; }
/* 眼睛用两个伪元素拼：外圈是眼眶，中间是瞳孔。和登录页那颗同一个画法，
   没有额外图标资源。显示明文时瞳孔拉成一道斜杠，状态一眼可辨。 */
.secret-eye {
  position: relative;
  flex: none;
  width: 36px;
  height: 34px;
  min-height: 34px;
  padding: 0;
  color: var(--muted);
}
.secret-eye::before {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 10px;
  border: 1.5px solid currentColor;
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
}
.secret-eye::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.secret-eye.shown { color: var(--success); }
.secret-eye.shown::after {
  width: 18px;
  height: 1.5px;
  border-radius: 0;
  transform: rotate(-45deg);
}
.secret-eye:hover { color: var(--text); }

/* 通用输入弹窗：和确认框同一套外壳，中间换成字段。 */
.prompt-dialog-fields { display: grid; gap: 12px; padding: 4px 20px 10px; }
.prompt-dialog-field span { color: var(--muted); font-size: var(--font-base); font-weight: 650; }
.prompt-dialog-field input,
.prompt-dialog-field select,
.prompt-dialog-field textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--input-bg, var(--surface-soft));
  color: var(--text);
  font: inherit;
}
.prompt-dialog-field textarea { resize: vertical; line-height: 1.6; }
.prompt-dialog-field input:focus, .prompt-dialog-field select:focus, .prompt-dialog-field textarea:focus { outline: none; }
/* 字段自己是 grid / flex 排的，hidden 那条 UA 规则会输：跟着下拉框藏起来的字段要明写。 */
.prompt-dialog-field[hidden] { display: none; }
.console-self { color: var(--muted); font-size: var(--font-base); }
.console-actions button:disabled { opacity: .4; cursor: not-allowed; }

/* ---- 扫码支付弹窗 ---- */
.pay-dialog {
  width: min(860px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow: hidden;
  /* 收款方式的主色。两家品牌色不一样，码盘四角的框、「扫一扫」和倒计时都跟着它走，
     用户一眼能对上自己点的是哪家。默认支付宝蓝，切到微信时由 JS 换 data-pay-method。 */
  --pay-brand: #1677ff;
}

.pay-dialog { flex-direction: column; }
.pay-dialog[open] { display: flex; }

.pay-dialog[data-pay-method="wechat"] {
  --pay-brand: #07c160;
}

/* 标题和下面隔一条线：上半截是"买什么"，下半截是"怎么付"，两件事。 */
.pay-dialog .dialog-header {
  flex: none;
  align-items: center;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line);
}

.pay-dialog .dialog-header h2 { margin-top: 5px; font-size: 20px; }

.pay-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 18px 24px 8px;
  overflow-y: auto;
}

/* 左边只负责完成支付，右边负责让用户核对买的是什么。 */
.pay-panels {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 0;
  align-items: stretch;
  min-height: 430px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
}

.pay-left {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 28px 24px;
  border-right: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface-soft) 72%, var(--surface));
}

/* 支付宝自己画码，只有码底保持白色；其余文字跟随站点主题。 */
.pay-code-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 0;
  background: transparent;
}

/* 没码可摆的时候（这种方式暂未开通、正在下单）白底收起来：白底是给码用的，
   底下没有码却铺一整块白，看着像图裂了。 */
.pay-code-panel.is-empty {
  width: 100%;
  min-height: 250px;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
}

.pay-code-panel.is-empty .pay-code {
  background: transparent;
}

.pay-code-panel.is-empty .pay-code-box {
  background: transparent;
  box-shadow: none;
}

.pay-code-panel.is-empty .pay-code-missing {
  color: var(--muted);
}

/* 码和四角的框。白色内边距跟着二维码一起被四角框包住，深色主题下仍留足静区。 */
.pay-code-box {
  position: relative;
  display: flex;
  padding: 10px;
  border-radius: 6px;
  background: #ffffff;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .18);
}

/* 四角那四个角标。扫码框的通用语汇，颜色跟着当前收款方式走。 */
.pay-code-marks i {
  position: absolute;
  width: 16px;
  height: 16px;
  border: 3px solid var(--pay-brand);
  border-radius: 3px;
}

.pay-code-marks i:nth-child(1) { top: -8px; left: -8px; border-right: 0; border-bottom: 0; }
.pay-code-marks i:nth-child(2) { top: -8px; right: -8px; border-left: 0; border-bottom: 0; }
.pay-code-marks i:nth-child(3) { bottom: -8px; left: -8px; border-right: 0; border-top: 0; }
.pay-code-marks i:nth-child(4) { bottom: -8px; right: -8px; border-left: 0; border-top: 0; }

/* 码多大只有一个数：--pay-code-size。两颗码都是贴边画的（支付宝那一页里的码铺满整个 iframe，
   微信那颗我们自己画、不带白边），四周的留白全由外面那个白框给——所以两家的留白一定一样。
   原来各管各的：支付宝的码贴边、只剩白框那 8px；微信的 SVG 自带 3 格白边再加 8px，合起来 22px。
   用户 2026-09-21：「支付宝二维码的内边距太小了，微信的内边距太大了，取个中间值」→ 16px。
   白框外沿固定 184（上面的页签槽、下面的倒计时都按它对齐），所以码 = 184 − 2×16 = 152。 */
.pay-code {
  flex: 0 0 auto;
  width: var(--pay-code-size, 152px);
  min-height: var(--pay-code-size, 152px);
  display: flex;
  align-items: center;
  justify-content: center;
  background: #ffffff;
  box-sizing: border-box;
}

.pay-code-image,
.pay-code-image svg {
  display: block;
  width: var(--pay-code-size, 152px);
  height: var(--pay-code-size, 152px);
}

/* 支付宝嵌进来的那颗码。尺寸必须等于下单时给的 qrcode_width，大了留白、小了出滚动条。
   那个数写在这一单的收银台地址里，renderPayCode 读出来设成 --pay-code-size——
   所以云端改 QR_CODE_WIDTH 不用和这里掐着同时上线，换镜像前下的老单也照它自己的宽度摆。 */
.pay-code-frame {
  display: block;
  width: var(--pay-code-size, 152px);
  height: var(--pay-code-size, 152px);
  border: 0;
  overflow: hidden;
  background: #ffffff;
  color-scheme: light;
}
/* 切去别的收款方式时这一页只藏不删（app.js 的 setPayCodeContent）。上面写了 display: block，
   浏览器自带的 [hidden] 那条会输给它，得自己再写一遍。 */
.pay-code-frame[hidden] { display: none; }

/* 支付宝码中间那颗图标是支付宝在它自己那一页里叠上去的，跨域的 iframe 里动不了。试过从外面盖一块同样大的白，
   用户看了当场否了（2026-09-21：「去不掉就留着，不要改成大白块，太丑了」）。别再盖。 */

/* 码嵌不出来时才露出来的那行小字。平时藏着，所以做成一条可点的提示，
   不做成按钮——它不是这一步该干的事，只是一条退路。 */
.pay-cashier-link {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0;
  color: var(--pay-brand);
  font-size: 12px;
  line-height: 1.5;
  text-decoration: none;
}

.pay-cashier-link span {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.pay-cashier-link svg {
  flex: none;
  margin-top: 1px;
}

.pay-code-missing {
  margin: 0;
  padding: 0 6px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.6;
  text-align: center;
}

.pay-meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 28px 30px 26px;
  background: var(--surface);
}

.pay-summary-head {
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
}

.pay-section-label {
  display: block;
  margin-bottom: 7px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 650;
}

.pay-summary-head h2 {
  margin: 0;
  font-size: 23px;
  line-height: 1.35;
  letter-spacing: 0;
}

.pay-info-section h3,
.pay-detail-section h3 {
  margin: 0 0 11px;
  color: var(--text);
  font-size: 14px;
  font-weight: 650;
}

.pay-product-summary {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  min-height: 72px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-soft);
  box-sizing: border-box;
}

.pay-product-icon {
  width: 46px;
  height: 46px;
  border-radius: 8px;
  background-color: color-mix(in srgb, var(--pay-brand) 12%, var(--surface));
  background-repeat: no-repeat;
  background-position: center;
  background-size: 30px 30px;
}

.pay-product-icon.is-member { background-image: url("/static/icon-tier-member.png"); }
/* 积分商品那枚走遮罩（颜色是滑轨按钮的紫）：外框的品牌色底还要留着，所以画在 ::before 上。 */
.pay-product-icon.is-credits { position: relative; background-image: none; }
.pay-product-icon.is-credits::before {
  content: "";
  position: absolute;
  inset: 0;
  width: 30px;
  height: 30px;
  margin: auto;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}

.pay-product-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.pay-product-copy strong {
  overflow-wrap: anywhere;
  font-size: 15px;
  line-height: 1.35;
}

.pay-product-copy small { color: var(--muted); font-size: 13px; }

.pay-product-tag {
  padding: 4px 7px;
  border: 1px solid color-mix(in srgb, var(--pay-brand) 36%, var(--line));
  border-radius: 4px;
  color: var(--pay-brand);
  font-size: 11px;
  white-space: nowrap;
}

/* 倒计时。数字用等宽，不然每跳一秒整行字都在抖。 */
.pay-expiry {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.pay-expiry svg {
  flex: none;
}

.pay-hint {
  max-width: 230px;
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.6;
  text-align: center;
}

.pay-hint[data-tone="warn"] {
  color: #e0b96a;
}

.pay-detail-list {
  margin: 0;
}

.pay-detail-list > div,
.pay-order-detail {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  align-items: start;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}

.pay-detail-list dt,
.pay-order-detail > span:first-child { color: var(--muted); }

.pay-detail-list dd {
  margin: 0;
  color: var(--text);
  text-align: right;
  overflow-wrap: anywhere;
}

.pay-order-value {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-width: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  text-align: right;
  word-break: break-all;
}

.pay-total-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.pay-total-row > span { color: var(--text); font-size: 14px; font-weight: 650; }

.pay-total-row strong {
  color: var(--pay-brand);
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

/* 复制按钮：悬浮、聚焦都不变样，只有按下缩一下——和全站按钮一个规矩。 */
.pay-copy-button {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #8c8c8c;
  cursor: pointer;
  transition: transform .12s ease;
}

.pay-copy-button:active {
  translate: 0 1px;
  scale: .98;
}

.pay-dialog .dialog-footer {
  flex: none;
  justify-content: center;
  border-top: 1px solid var(--line);
}

/* 底部只是一句说明，不是按钮：点它没有任何用，做成可点的样子只会让人去点。 */
.pay-foot-note {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: #8c8c8c;
  font-size: 12px;
}
/* 盾牌和右边那句同高、同一条水平线。两件事各归各的属性管：
   高度用 1em——图已经把四周透明留白裁掉了，所以 1em 就实打实是文字的高度，
   不用去猜留白吃掉了多少（原图 200×200 里内容只有 148×176，
   直接拿原图设 1em 会矮一截）；
   对齐用 align-items: center 走中线，不用基线——中文没有下伸部，
   按基线对齐图标会整体偏上，看着像浮起来。
   宽度留 auto 跟着比例走，flex: none 免得被压扁。 */
.pay-foot-shield {
  flex: none;
  width: auto;
  height: 1em;
}

@media (max-width: 700px) {
  .pay-dialog { max-height: calc(100vh - 20px); }
  .pay-dialog .dialog-header { padding: 16px 18px; }
  .pay-body { padding: 14px 14px 6px; }
  .pay-panels { grid-template-columns: minmax(0, 1fr); }
  .pay-left { padding: 24px 18px; border-right: 0; border-bottom: 1px solid var(--line); }
  .pay-meta { padding: 22px 20px; }
  .pay-product-summary { grid-template-columns: 42px minmax(0, 1fr); }
  .pay-product-icon { width: 42px; height: 42px; background-size: 28px 28px; }
  .pay-product-tag { grid-column: 2; justify-self: start; }
  .pay-total-row strong { font-size: 27px; }
}

/* ---- 控制台：订单 ---- */
/* 三张小卡：已付那张是真金白银，另外两张只看单量。 */
.console-order-totals {
  display: flex;
  gap: 10px;
  margin: 0 0 12px;
  flex-wrap: wrap;
}

.console-order-total {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 132px;
  padding: 9px 13px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}

.console-order-total span {
  color: var(--muted);
  font-size: 12px;
}

.console-order-total strong {
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}

/* 订单号保留专用等宽字体，但字号和用户订单页一致，不能比右侧内容小一圈。 */
.console-order-id .console-copy-value {
  font-family: var(--font-mono);
  font-size: 15px;
}

/* 一格内容 + 一颗复制按钮。
   注意别把 <td> 自己改成 flex——单元格一旦不是 table-cell，整行的分隔线
   就跟着没了。布局交给里面这层 .console-copy-inner。 */
.console-copy-inner {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.console-copy-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.console-copy-button {
  flex: none;
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  color: var(--muted);
}

.console-copy-button:hover {
  color: var(--green);
  border-color: var(--green);
  background: var(--green-soft);
}

/* 筛选框最长的选项是「全部状态」，和工单那个一样宽就够。 */
#consoleOrderFilter { width: 116px; }

/* 订单：订单号、下单人、套餐、金额、状态、创建于、操作。
   操作列按英文的 Settle / Cancel 留宽——中文只要两个字，英法俄要长一截，
   按中文卡宽度会让它们折成两行，整张表跟着变高。 */
/* 订单号整串是 32 位十六进制，放大到 15px 后仍要给够；
   多出来的宽度从下单人和套餐那两列匀。 */
/* 十一列：订单编号 / 下单人 / 商品名称 / 应付 / 实付 / 支付方式 / 订单状态 / 退款状态 /
   下单时间 / 支付时间 / 操作。固定表格布局下**列宽必须写满 100%**，少写一列它就是 0 宽，
   直接消失（从七列改十一列时就是这么丢了四列）。加一列先来这里分宽度。
   十一列挤一屏：内边距从 20 收到 8，订单号那串等宽字缩到 13px，表的最小宽度提到 1360——
   按这套百分比，1360 正好是每一格都不出省略号的下限，再窄就横着滚，别把时间截成「2026-09-08 1…」。 */
#consolePaneOrders .orders-table { min-width: 1360px; }
#consolePaneOrders .console-table th,
#consolePaneOrders .console-table td { padding-left: 8px; padding-right: 8px; }
#consolePaneOrders .console-order-id .console-copy-value { font-size: 13px; }
#consolePaneOrders .console-table th:nth-child(1),
#consolePaneOrders .console-table td:nth-child(1) { width: 21.3%; }
#consolePaneOrders .console-table th:nth-child(2),
#consolePaneOrders .console-table td:nth-child(2) { width: 10.5%; }
#consolePaneOrders .console-table th:nth-child(3),
#consolePaneOrders .console-table td:nth-child(3) { width: 11.7%; }
#consolePaneOrders .console-table th:nth-child(4),
#consolePaneOrders .console-table td:nth-child(4) { width: 5.4%; }
#consolePaneOrders .console-table th:nth-child(5),
#consolePaneOrders .console-table td:nth-child(5) { width: 5.4%; }
#consolePaneOrders .console-table th:nth-child(6),
#consolePaneOrders .console-table td:nth-child(6) { width: 4.7%; }
#consolePaneOrders .console-table th:nth-child(7),
#consolePaneOrders .console-table td:nth-child(7) { width: 5.9%; }
#consolePaneOrders .console-table th:nth-child(8),
#consolePaneOrders .console-table td:nth-child(8) { width: 5.5%; }
#consolePaneOrders .console-table th:nth-child(9),
#consolePaneOrders .console-table td:nth-child(9) { width: 10.3%; }
#consolePaneOrders .console-table th:nth-child(10),
#consolePaneOrders .console-table td:nth-child(10) { width: 10.3%; }
#consolePaneOrders .console-table th:nth-child(11),
#consolePaneOrders .console-table td:nth-child(11) { width: 9%; }
#consolePaneOrders .console-actions { flex-wrap: nowrap; white-space: nowrap; gap: 6px; }

/* ---- 紧急通知横幅 ---- */
/* 放在 appShell 顶部、页头之上。红底白字，一眼就知道不是普通提示。 */
.urgent-banner {
  flex: none;
  display: flex;
  flex-direction: column;
  color: #fff5f5;
}

/* 底色画在每一行上，不画在容器上（2026-09-20）：同一条横幅里多了青蓝色的推广通知，只有它的时候容器不该露出红色。 */
/* 图标和文字当一组居中（用户 2026-09-20：「文字要居中」），关闭键钉在右端、不参与居中；左右留一样宽，文字才正对页面中线。
   关闭键用 margin 上下居中，不用 transform：全站按钮按下时那条 scale 会把 translateY 顶掉，一按就跳。 */
.urgent-banner-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  /* 第一版是 9px + 13px 的字，用户当天要它再高一点、字大一点：12px + 15px，一行约 47px。 */
  padding: 12px 48px;
  background: #7f1d1d;
  /* 横幅上不画任何线（用户 2026-09-20：「紧急通知和推广通知都不需要白线」）：原来每一行底下有一道亮一档的边、
     两行之间还有一道半透明白的分隔线。行和页面、行和行之间靠底色区分；两条同色的挨在一起就是一整块，各有各的关闭键。 */
}

/* ---- 推广通知：和紧急通知同一套排版，底色是流动的青蓝渐变（用户 2026-09-20）----
   渐变铺两个周期、宽度是横幅的两倍，位置从 0% 走到 100% 正好平移一个周期，首尾接得上，看着是一直往一个方向流。
   颜色写死、不跟主题：和红色那条一样，它是压在页面上面的一条色带，深浅主题下是同一个样子。
   第一版是 #0a5cff / #008ff5 / #00a6e0，用户当天嫌「颜色太鲜艳了」：整体降饱和、压暗一档，还是蓝到青在流，
   但不抢页面的戏；白字压在三档上的对比是 7.9 / 5.6 / 4.2，不用再靠字影托。别把强调色 #00c2ff 放进来，
   白字压在它上面对比只有 2 出头。 */
.urgent-banner-row.is-promo {
  background-color: #1f6391;
  background-image: linear-gradient(90deg, #1e4f8f 0%, #1f6b9c 16.66%, #2486a3 33.33%, #1e4f8f 50%, #1f6b9c 66.66%, #2486a3 83.33%, #1e4f8f 100%);
  background-size: 200% 100%;
  color: #ffffff;
  animation: promo-banner-flow 9s linear infinite;
}
@keyframes promo-banner-flow {
  from { background-position: 0% 50%; }
  to { background-position: 100% 50%; }
}
.urgent-banner-row.is-promo .urgent-banner-close { color: #ffffff; }
@media (prefers-reduced-motion: reduce) {
  .urgent-banner-row.is-promo { animation: none; }
}


.urgent-banner-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff5f5;
  color: #7f1d1d;
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
}

.urgent-banner-text {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0;
  text-align: center;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.5;
}

/* 字照 LibTV 顶部横幅：15px / 500（用户 2026-10-12：「把顶部横幅的字体样式也和libtv对齐」；同一天早些时候说过「不需要加粗」，那时是 400）。
   紧急、推广两种同一套，标题和正文一样的字重。只有微软雅黑的机器上 500 和常规一样，装了苹方 / 思源黑体的是中等粗细。 */
.urgent-banner-text strong { font-weight: 500; }
.urgent-banner-text span { opacity: .92; }

.urgent-banner-close {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 12px;
  height: 24px;
  margin: auto 0;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
  padding: 0 7px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: #fff5f5;
  line-height: 1;
  cursor: pointer;
}
.urgent-banner-close .close-icon { width: 12px; height: 12px; }

.urgent-banner-close:hover { background: rgba(255, 255, 255, .16); }

/* 横幅上的跳转按钮（2026-10-12，用户：「…后面需要一个“立即购买”的按钮，点击后跳转到套餐订阅页面」）：字和去处由控制台发通知时填，
   接在文字后面、和文字一起居中（关闭键照旧钉在右端）。**白底黑字、悬浮不变**（用户 2026-10-12：「按钮白底黑字，不需要悬浮样式」；
   第一版字取这一行的底色、悬浮底色变淡，都撤了，别加回），**字照 LibTV 横幅上那颗按钮：14px / 500、纯黑**（同一天先说「不需要加粗」，
   又要「按钮文本加粗吧」给过 650，最后「把顶部横幅的字体样式也和libtv对齐」定成 500），
   **不描边**——横幅上不画任何线（用户 2026-09-20）。按下照全站下沉一像素。高 30、上下各 -4px 外边距压进行的内边距里：只占 22 的排版高度，比一行字（22.5）矮，
   带按钮的那一行和不带的一样高（47），不把横幅撑高。站外网址是 <a>，站内页面是 <button>，两种长一个样。 */
.urgent-banner-action {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  height: 30px;
  margin: -4px 0;
  padding: 0 16px;
  border: 0;
  border-radius: 999px;
  background: #ffffff;
  color: #000000;
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

/* ---- 公告弹窗 ---- */
/* 520 → 720（用户 2026-09-21：「公告弹窗可以大一点」）：公告常带一张图加几段字，
   窄框里图小、字挤成很多行。正文能滚的高度和图的上限跟着一起放宽。 */
.announce-dialog { width: min(720px, calc(100vw - 32px)); }

.announce-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* 这个弹窗的 header / footer 各自带左右内边距，正文这一段也要自己补上，
     不然图片和文字会顶到弹窗边上。 */
  padding: 4px 20px 2px;
  /* 公告可能写得很长，超了就在弹窗内部滚，别把按钮顶出屏幕。 */
  max-height: min(64vh, 560px);
  overflow-y: auto;
}

.announce-image {
  display: block;
  width: 100%;
  max-height: 340px;
  object-fit: contain;
  border-radius: 8px;
  background: var(--surface-soft);
}

.announce-text {
  margin: 0;
  font-size: 14px;
  line-height: 1.7;
  /* 后台是多行文本框输入的，换行要保留 */
  white-space: pre-wrap;
}

/* ---- 控制台：发通知 ---- */
.notice-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 14px;
  margin-bottom: 4px;
  /* 输入框不需要横向铺满：一行标题拉到一米宽反而难读。表单收到一半，
     面板本身不动——右边框还在原来的位置。 */
  max-width: 50%;
}

/* 标题、内容、配图占满两列。 */
.notice-field-wide { grid-column: 1 / -1; }

/* 类型和展示频率并排：各自只占内容宽度，紧挨着排，不跟着栅格分半。
   频率那格在非公告类型下会被 .hidden 收掉，剩类型一个照样贴左。 */
.notice-form-row { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 12px 14px; }
.notice-form-row > .field { flex: none; }

.notice-form .field { display: flex; flex-direction: column; gap: 5px; }
/* 类型和展示频率是短枚举，最长的选项就「仅首次展示」五个字。
   跟着栅格拉到半个面板宽实在没必要。 */
/* 类型那一栏要放得下「常驻公告（右上角喇叭）」，168px 会把它截半截。
   频率那栏的词都短，维持原宽。 */
#noticeKind { width: 232px; max-width: 100%; }
#noticeFrequency { width: 168px; max-width: 100%; }
/* 横幅按钮的去处、按钮文字（2026-10-12）：去处最长「套餐订阅 · 创作会员」，和类型那栏一样宽；按钮文字最多 8 个字。 */
#noticeActionTarget { width: 232px; max-width: 100%; }
#noticeActionLabel { width: 168px; max-width: 100%; }
.notice-form .field > span { color: var(--muted); font-size: var(--font-base); }

.notice-form textarea {
  box-sizing: border-box;
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--input-bg, var(--surface-soft));
  color: var(--text);
  font: inherit;
  resize: vertical;
}

/* 配图：左边一个上传方框（点或拖），右边预览。不再给地址框——
   手填地址粘错一个字，公告发出去就是一张碎图。 */
.notice-image-row { display: flex; gap: 12px; align-items: flex-start; }
.notice-image-drop {
  display: flex;
  flex: 0 0 200px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 120px;
  padding: 0;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
  background: var(--surface-soft);
  color: var(--muted);
  font: inherit;
  font-size: var(--font-base);
  cursor: pointer;
}
.notice-image-drop:hover:not(:disabled),
.notice-image-drop.dragging { border-color: var(--accent); color: var(--accent-text, var(--accent)); }
.notice-image-drop-plus { display: inline-flex; line-height: 1; }
.notice-image-drop-text { padding: 0 10px; text-align: center; line-height: 1.4; }

.notice-image-preview-wrap { position: relative; flex: none; }
.notice-image-preview {
  display: block;
  max-width: 240px;
  max-height: 120px;
  object-fit: contain;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-soft);
}
/* 叉压在预览图右上角：清除图片是对着图做的事，不该跑到别处再找一颗按钮。 */
.notice-image-clear {
  position: absolute;
  top: -8px;
  right: -8px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

@media (max-width: 1200px) {
  .notice-form { max-width: none; }
}

@media (max-width: 720px) {
  .notice-form { grid-template-columns: minmax(0, 1fr); }
}

/* 已发通知表：类型、标题、详细内容、频率、状态、发布于、操作 */
#consolePaneNotices .console-table th:nth-child(1),
#consolePaneNotices .console-table td:nth-child(1) { width: 11%; }
#consolePaneNotices .console-table th:nth-child(2),
#consolePaneNotices .console-table td:nth-child(2) { width: 26%; }
#consolePaneNotices .console-table th:nth-child(3),
#consolePaneNotices .console-table td:nth-child(3) { width: 11%; }
#consolePaneNotices .console-table th:nth-child(4),
#consolePaneNotices .console-table td:nth-child(4) { width: 12%; }
#consolePaneNotices .console-table th:nth-child(5),
#consolePaneNotices .console-table td:nth-child(5) { width: 10%; }
#consolePaneNotices .console-table th:nth-child(6),
#consolePaneNotices .console-table td:nth-child(6) { width: 14%; }
#consolePaneNotices .console-table th:nth-child(7),
#consolePaneNotices .console-table td:nth-child(7) { width: 16%; }
#consolePaneNotices .console-actions { flex-wrap: nowrap; white-space: nowrap; }

/* 暂时不可点的导航项：看得见，但没有链接行为，也不给可点的暗示。 */
.sidebar-item-inert { cursor: default; }
.sidebar-item-inert:hover { background: none; color: var(--text-muted); }

/* ---- 设置页：修改密码 ---- */
.settings-account-details { padding-right: 0; padding-left: 0; }
.password-form {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px 14px;
  max-width: 720px;
  margin-bottom: 4px;
}

.phone-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 14px;
  max-width: 720px;
  margin-bottom: 4px;
}

.phone-form .field { display: flex; flex-direction: column; gap: 5px; }
.phone-form .field > span { color: var(--muted); font-size: var(--font-base); }

.password-form .field { display: flex; flex-direction: column; gap: 5px; }
.password-form .field > span { color: var(--muted); font-size: var(--font-base); }

@media (max-width: 860px) {
  .password-form, .phone-form { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 悬浮大图 ---- */
/* 固定定位挂在 body 上，跟着鼠标走。资产卡在会滚动、overflow 被裁掉的容器里，
   把预览塞在卡片内部会被切掉一半。 */
.image-preview-layer {
  position: fixed;
  z-index: 200;
  padding: 6px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  box-shadow: 0 16px 44px var(--dialog-shadow);
  /* 别挡住鼠标：浮层跟在指针旁边，接了事件会和 mouseleave 打架，一闪一闪。 */
  pointer-events: none;
}

.image-preview-layer img {
  display: block;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 4px;
}

/* ---- 本集剧本 ---- */
.episode-script { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 10px; }

.episode-script-body {
  flex: 1;
  min-height: 0;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: 14px 16px;
  overflow: auto;
  resize: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  line-height: 1.75;
  /* 剧本是排好版的纯文本：换行和缩进要原样留着，长行照常回绕。 */
  white-space: pre-wrap;
  word-break: break-word;
}

.episode-script-body:focus { outline: none; }

.episode-script-foot {
  position: relative; /* 「历史版本」弹层贴在这一行上方 */
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* 正文页「历史版本」（2026-09-14）：按钮在保存旁；弹层照助手的历史记录弹层，只是贴在脚部上方、靠右 */
.episode-history-button { flex: none; display: inline-flex; align-items: center; min-height: 0; padding: 6px 12px; border: 0; border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; cursor: pointer; }
/* 非会员：按钮后一颗素钻石，就是账户面板普通用户那颗（.account-tier），同一个样式 */
.episode-history-button .account-tier { margin-left: 6px; }
.episode-history-button:disabled { opacity: .5; cursor: not-allowed; }
.script-history { position: absolute; z-index: 5; right: 0; bottom: calc(100% + 8px); display: flex; flex-direction: column; gap: 6px; width: min(440px, 100%); max-height: 320px; padding: 10px; border-radius: 12px; background: #303030; box-shadow: 0 12px 36px rgba(0, 0, 0, .35); overflow-y: auto; }
:root:not([data-theme="dark"]) .script-history { background: #ffffff; box-shadow: 0 12px 36px rgba(0, 0, 0, .18); }
.script-history-head { margin: 0 2px 4px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.script-history-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; border-radius: 10px; background: rgba(255, 255, 255, .06); }
:root:not([data-theme="dark"]) .script-history-row { background: var(--surface-soft); }
.script-history-label { flex: 1; min-width: 0; font-size: 13px; }
/* 当前所在的版本：绿勾在文字后面 */
.script-history-current { display: inline-flex; margin-left: 6px; color: var(--green); vertical-align: -1px; }
.script-history-restore:disabled { opacity: .45; cursor: not-allowed; }
.script-history-restore { flex: none; min-height: 0; padding: 4px 10px; border: 0; border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; font-size: 12px; cursor: pointer; }

.episode-script-note { flex: 1; min-width: 0; color: var(--muted); font-size: var(--font-base); }

/* 「全部选择 / 清空已选 / 生图提示词」跟在页签后面，只空开一段，不推到最右边。 */
.episode-subtabs .subtab-pick-command {
  height: 30px;
  min-height: 30px;
  padding: 0 10px;
  white-space: nowrap;
}

.subtab-command-separator {
  flex: none;
  width: 1px;
  height: 30px;
  /* 父级已有 6px gap，再加 10px，竖线到两侧按钮都是 16px。 */
  margin: 0 10px;
  background: var(--line-strong);
}

.episode-subtabs .prompt-command {
  margin-left: 8px;
  height: 30px;
  min-height: 30px;
  padding: 0 12px;
  white-space: nowrap;
}

/* 状态栏每一步做完打一个勾，一眼看得出哪几步已经完成。 */

/* ---- 资产库抽屉里的卡片配色 ---- */
/* 抽屉底 #141414，卡片默认 #161616，悬浮 #222222。
   通用的资产卡规则把默认和悬浮都钉成了 var(--surface-hover)（没有悬浮态），这里覆盖掉。
   只作用于抽屉内，正文那边的资产卡不动。 */
.asset-drawer-body .asset-card:not(.asset-card-add),
.asset-drawer-body .asset-card:not(.asset-card-add):hover {
  background: var(--surface);
  border-color: var(--card-border);
}

.asset-drawer-body .asset-card:not(.asset-card-add):hover {
  background: var(--surface-hover);
  border-color: var(--surface-hover);
}

/* 「新建分集」这个弹窗只有一行标题：放大一档，左边和下面的表单对齐，
   和右上角那颗关闭按钮上下居中。 */
.new-episode-header { align-items: center; }
.new-episode-header h2 { margin: 0; font-size: 20px; }

/* 积分余额：标题右边直接跟一个闪电 + 数字，不再另起一段说明文字。 */
.billing-balance-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.billing-balance-head h3 { margin: 0; }
.billing-balance-head .balance-chip { font-size: 18px; font-weight: 700; }

/* ---- 首屏定妆 ---- */
/* <head> 里的同步脚本把状态写成 data-* ，这里在首帧就照它排版。
   app.js 一接管就把这些属性摘掉，之后完全由 .hidden 说了算。 */

/* 工作台在 HTML 里默认可见，但落地页是作品列表，先把它藏掉。 */
/* 落地页 2026-09-20 起是首页（原来是作品列表）。 */
html[data-boot-page="home"] #mainView { display: none; }
html[data-boot-page="home"] #homePage { display: block !important; }

/* 定妆这一帧不跑动画，等 app.js 摘掉 data-booting 才恢复过渡。 */
html[data-booting] .sidebar, html[data-booting] .sidebar * { transition: none !important; }

/* ==================== 工单 ==================== */
/* 蓝色留给优先级，绿色留给"已解决"，红色留给"关闭"。 */

.ticket-head { display: flex; align-items: flex-start; gap: 16px; }
/* 页头只剩标题，「创建工单」排在标题下面、和标题左边对齐（用户 2026-10-07 撤了副标题「查看和管理工单」、按钮改左对齐）。
   .ticket-head 弹窗里的工单详情也在用，这条只管页头。 */
.page-head.ticket-head { flex-direction: column; gap: 12px; }
/* 跟主按钮同一身黑白（深色白底黑字、浅色黑底白字，用户 2026-10-07 从蓝色改过来），悬浮不变色。 */
.ticket-new-button {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  min-height: 34px;
  padding: 5px 16px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: var(--command-create-bg);
  color: var(--command-create-text);
  font-size: var(--font-base);
  font-weight: 600;
}
.ticket-new-button:hover:not(:disabled) { border-color: transparent; background: var(--command-create-bg); color: var(--command-create-text); }
.ticket-new-plus { display: inline-flex; align-items: center; line-height: 1; }

.ticket-toolbar { display: flex; gap: 12px; margin: 16px 0 18px; }
.ticket-toolbar select {
  width: 128px;
  height: 36px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  /* 深色比面板亮一档、浅色比面板深一档，都是条目那档（浅色那句 2026-09-10 用户指出过）。 */
  background: var(--surface-soft);
}

.ticket-table-wrap {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  overflow: auto;
}
.ticket-table { width: 100%; border-collapse: collapse; }
.ticket-table th {
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--font-base);
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}
.ticket-table td {
  padding: 14px 18px;
  border-bottom: 1px solid var(--cell-line);
  vertical-align: middle;
}
.ticket-table tbody tr:last-child td { border-bottom: 0; }
.ticket-table tbody tr { cursor: pointer; }
.ticket-table tbody tr:hover { background: var(--surface-hover); }
#ticketsPage .ticket-table th:nth-child(3),
#ticketsPage .ticket-table td:nth-child(3) { text-align: center; vertical-align: middle; }
.ticket-id { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 主题两行：粗体标题 + 灰色类型副标题，和参考图一致。 */
.ticket-subject { display: block; color: var(--text); font-weight: 600; }
.ticket-subject-note { display: block; margin-top: 4px; color: var(--muted); font-size: var(--font-base); }
.ticket-time { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.ticket-status-open { color: #e0a33a; }
.ticket-status-answered { color: #3fb884; }
.ticket-status-closed { color: var(--muted); }
.ticket-priority { color: #4a9eea; }

.ticket-empty { padding: 56px 18px; color: var(--muted); text-align: center; }

/* 工单详情从列表底部移入对话框：上面尽量留给会话，下面固定为输入区。 */
.ticket-detail-dialog {
  width: min(820px, calc(100vw - 32px));
  height: min(720px, calc(100vh - 48px));
  max-height: calc(100vh - 48px);
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.ticket-detail-dialog { flex-direction: column; }
.ticket-detail-dialog[open] { display: flex; }
.ticket-detail-dialog::backdrop { background: rgba(0, 0, 0, .58); }
.ticket-detail-dialog .ticket-detail {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 4fr) minmax(0, 1fr);
}
.ticket-detail-dialog .ticket-conversation {
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 20px 22px 16px;
  overflow: hidden;
}
.ticket-detail-dialog .ticket-head h4 { overflow: hidden; font-size: 17px; text-overflow: ellipsis; white-space: nowrap; }
.ticket-head-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.ticket-head-actions .ticket-close { margin-left: 0; }
.ticket-dialog-dismiss { flex: none; }
.ticket-detail-dialog .ticket-thread {
  flex: 1;
  min-height: 0;
  max-height: none;
  margin-bottom: 0;
  padding-right: 4px;
  overflow-y: auto;
}
.ticket-detail-dialog .ticket-composer {
  position: relative;
  min-height: 0;
  padding: 14px;
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
}
.ticket-detail-dialog .ticket-editor {
  height: 100%;
  min-height: 0;
  max-height: none;
  resize: none;
  line-height: 1.6;
}
.ticket-detail-dialog .ticket-foot {
  position: absolute;
  right: 22px;
  bottom: 22px;
  margin: 0;
}
.ticket-detail-dialog .ticket-foot button { min-width: 88px; }

/* ---- 创建工单弹窗 ---- */
.ticket-dialog {
  width: min(600px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text);
}
.ticket-dialog::backdrop { background: rgba(0, 0, 0, .55); }
.ticket-dialog-head { display: flex; align-items: flex-start; gap: 16px; padding: 26px 28px 6px; }
.ticket-dialog-head h3 { margin: 0; font-size: 19px; font-weight: 700; }
.ticket-dialog-head p { margin: 6px 0 0; color: var(--muted); font-size: var(--font-base); }
.ticket-dialog-head .icon-command { margin-left: auto; }
.ticket-dialog-body { display: grid; gap: 20px; padding: 20px 28px 26px; }
.ticket-field { display: flex; flex-direction: column; gap: 8px; }
.ticket-field > span { color: var(--text); font-size: var(--font-base); font-weight: 600; }
.ticket-required { color: #e06666; font-style: normal; }
/* 参考图里两个下拉是药丸形，右边一个上下箭头。原生箭头关掉自己画。 */
.ticket-field select {
  width: max-content;
  min-width: 150px;
  height: 42px;
  padding: 0 38px 0 18px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background-color: var(--input-bg);
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b3b3b3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 9.5 12 5.5l4 4M8 14.5l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 15px 15px;
}
.ticket-field input,
.ticket-field textarea {
  box-sizing: border-box;
  width: 100%;
  padding: 13px 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--input-bg);
  color: var(--text);
  font: inherit;
}
.ticket-field textarea { min-height: 104px; line-height: 1.6; resize: vertical; }
.ticket-dialog-foot {
  display: flex;
  justify-content: flex-end;
  padding: 16px 28px 22px;
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
}
.ticket-submit-button {
  min-height: 42px;
  padding: 6px 26px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #2f7fe0;
  color: #fff;
  font-size: 15px;
  font-weight: 600;
}

/* ==================== 套餐订阅 / 我的订单 ==================== */
/* 按参考图还原：大圆角、细边框、面板比页底稍亮一层，数值用大号粗体。
   金额那一列在参考图里是红的，状态用橙（待确认）和绿（成功）。 */

/* ---- 顶部三格 ---- */
/* 邀请、设置和订阅保留统一最大宽度，并在页面可用区域内左右居中。首页（home.css 的 .home-inner）也是这一个数。
   2026-10-07 从 1240 放到 1440（用户：「首页内容区域的宽度可以再宽一点……套餐订阅、设置……那就都统一宽一点」）。 */
:root { --page-column-width: 1440px; }
#invitePage > .page-head,
#invitePage > .invite-grid,
#settingsPage > .page-head,
#settingsPage > .panel,
#billingPage > .stat-strip,
#billingPage > .buy-terms,
#billingPage > .buy-card { max-width: var(--page-column-width); margin-left: auto; margin-right: auto; }
/* 套餐订阅是一层全屏（用户 2026-10-07：「点击后出现一个全屏的页面，来显示套餐订阅的内容，右上角是关闭」）：盖住侧栏和顶栏，自己滚；
   右上角那颗关闭回到进来之前的那一页（state.billingReturn）。付款弹窗是 showModal、通知是 popover，都在浏览器顶层，盖不住它们。 */
#billingPage:not(.hidden) {
  position: fixed;
  inset: 0;
  z-index: 90;
  overflow-y: auto;
  padding: 72px 46px 32px;
  background: var(--bg);
}
.billing-close {
  position: fixed;
  top: 18px;
  right: 22px;
  z-index: 91;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
}
.billing-close .close-icon { width: 14px; height: 14px; background: currentColor; vertical-align: 0; }
@media (max-width: 767px) {
  #billingPage:not(.hidden) { padding: 64px 14px calc(20px + env(safe-area-inset-bottom)); }
  .billing-close { top: max(12px, env(safe-area-inset-top)); right: 12px; width: 36px; height: 36px; }
}

.stat-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: 16px;
  border: 1px solid var(--line);
  border-radius: 14px;
  /* 余额区比充值区暗一档，两块面板拉开层次。 */
  background: var(--surface);
  overflow: hidden;
}
.stat-cell { padding: 16px 24px; border-left: 1px solid var(--line); }
.stat-cell:first-child { border-left: 0; }
.stat-cell-head {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-size: 15px;
}
/* 图标底盘：圆角方块加一层淡色底，颜色区分三个口径。 */
.stat-icon {
  position: relative;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 9px;
}
.stat-icon::before {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: 17px 17px;
  mask-size: 17px 17px;
  background: currentColor;
}
.stat-icon-wallet { background: rgba(63, 184, 132, .16); color: #3fb884; }
.stat-icon-wallet::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='5.5' width='19' height='13' rx='2.5'/%3E%3Cpath d='M2.5 10h19M17 14.5h1.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='5.5' width='19' height='13' rx='2.5'/%3E%3Cpath d='M2.5 10h19M17 14.5h1.5'/%3E%3C/svg%3E");
}
.stat-icon-usage { background: rgba(90, 158, 224, .16); color: #5a9ee0; }
.stat-icon-usage::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Cpath d='M5 19V11M12 19V5M19 19v-6'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Cpath d='M5 19V11M12 19V5M19 19v-6'/%3E%3C/svg%3E");
}
.stat-icon-pulse { background: rgba(166, 132, 224, .16); color: #a684e0; }
.stat-icon-pulse::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 12h4l2.5-7 4.5 14 2.5-7h5.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 12h4l2.5-7 4.5 14 2.5-7h5.5'/%3E%3C/svg%3E");
}
.stat-value {
  display: block;
  margin: 10px 0 2px;
  font-size: 28px;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
/* 积分数值前面挂那颗闪电，和顶栏余额药丸同一个图标。 */
.credits-value { display: inline-flex; align-items: center; gap: 9px; }
.credits-value::before {
  content: "";
  flex: none;
  width: 24px;
  height: 24px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
/* ---- 充值卡 ---- */
.buy-card {
  --buy-panel-bg: var(--surface);
  --buy-control-bg: var(--surface-soft);
  --buy-control-hover: var(--surface-hover);
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--buy-panel-bg);
}
/* 浅色主题（用户 2026-09-10：「套餐订阅中的部分内容没有适配，还是深色」）：
   购买卡原来两套主题都是固定深色，可卡里的字跟着主题走 --text，浅色下成了深底深字，标题、价格、权益全看不见。
   浅色另给一套，和深色那套的明暗关系一样：卡片白底，档位 / 输入框 / 按钮底比卡片深一档，悬浮和选中再深一档。 */
:root:not([data-theme="dark"]) .buy-card {
  --buy-panel-bg: var(--surface);
  --buy-control-bg: var(--surface-soft);
  --buy-control-hover: var(--tab-rail-strong);
}
.buy-card-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  border-bottom: 1px solid var(--line);
}
/* 圆形绿底钱包，参考图里标题左边那颗。 */
.buy-card-badge {
  position: relative;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: rgba(63, 184, 132, .16);
  color: #3fb884;
}
.buy-card-badge::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='5.5' width='19' height='13' rx='2.5'/%3E%3Cpath d='M2.5 10h19M17 14.5h1.5'/%3E%3C/svg%3E") no-repeat center / 22px 22px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='5.5' width='19' height='13' rx='2.5'/%3E%3Cpath d='M2.5 10h19M17 14.5h1.5'/%3E%3C/svg%3E") no-repeat center / 22px 22px;
}
.buy-card-title h3 { margin: 0; font-size: 21px; font-weight: 700; }
.buy-card-title p { margin: 4px 0 0; color: var(--muted); font-size: var(--font-base); }
.buy-history-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  min-height: 38px;
  padding: 6px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--buy-control-bg);
}
.buy-history-button:hover { background: var(--buy-control-hover); }
.buy-history-icon {
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2'/%3E%3Cpath d='M7 9h10M7 13h6'/%3E%3C/svg%3E") no-repeat center / 16px 16px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2'/%3E%3Cpath d='M7 9h10M7 13h6'/%3E%3C/svg%3E") no-repeat center / 16px 16px;
}

/* 上下留白比原来收一档（用户 2026-09-12：整页高度收敛一点，排版不动）。 */
.buy-card-body { padding: 16px 24px 18px; }
.buy-label { margin: 0 0 10px; color: var(--muted); font-size: var(--font-base); }
.buy-label:not(:first-child) { margin-top: 16px; }

/* ---- 档位卡片 ---- */
/* 六个档位排三列正好两行；四列会剩下两个落单。 */
.buy-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 1100px) { .buy-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.buy-package {
  /* <button> 默认把内容垂直居中，卡片被拉高时几行字会浮在中间。
     改成纵向 flex 从顶部排。 */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  padding: 14px 20px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--buy-control-bg);
  text-align: left;
}
.buy-package:hover:not(:disabled) { background: var(--buy-control-hover); }
/* 选中不是直接下单，只是标记；出码要再点下面的付款方式。 */
.buy-package.selected {
  border-color: var(--green);
  background: var(--buy-control-hover);
  box-shadow: 0 0 0 1px var(--green);
}
.buy-package-credits {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.buy-package-credits::before {
  content: "";
  flex: none;
  width: 20px;
  height: 20px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
/* 购买卡：档位名 + 加赠角标 / 到账总数（带闪电图标）/ 充 X + 赠 Y / 价格 + 立省。
   总数放最大，那才是用户真正拿到手的。 */
.buy-package-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.buy-package-head strong { color: var(--text); font-size: 15px; }

.buy-package-detail { display: block; margin-top: 6px; color: var(--muted); font-size: 15px; }
.buy-package-detail em { font-style: normal; color: var(--green); }

.buy-package-price {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 10px;
  color: var(--muted);
  font-size: var(--font-base);
}
.buy-package-price strong { color: var(--text); font-size: 19px; }
.buy-package-saved { font-size: 12px; }

/* ---- 付款方式 ---- */
.buy-pay-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; max-width: 640px; }
.buy-pay {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 62px;
  padding: 0 20px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--buy-control-bg);
  font-size: 16px;
}
.buy-pay:hover:not(:disabled) { background: var(--buy-control-hover); }
/* 没选档位时点了也没用，先做成不可点的样子。 */
.buy-pay:disabled { opacity: .45; cursor: not-allowed; }
/* 官方图标本身就是圆形带底色的，不要再自己加圆角和底色。 */
.buy-pay-icon {
  flex: none;
  width: 26px;
  height: 26px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 26px 26px;
}
.buy-pay-alipay { background-image: url("/static/pay-alipay.png?v=1"); }
.buy-pay-wechat { background-image: url("/static/pay-wechat.png?v=1"); }
.buy-disabled-note { margin: 12px 0 0; color: var(--muted); font-size: var(--font-base); }

/* ---- 兑换码 ---- */
.buy-redeem { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
.buy-redeem-label { display: flex; align-items: center; gap: 9px; color: var(--text); }
.buy-gift-icon {
  flex: none;
  width: 20px;
  height: 20px;
  background: #e0a33a;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linejoin='round'%3E%3Crect x='3' y='9' width='18' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h20V9H2zM12 6.5V20'/%3E%3Cpath d='M12 6.5S10.5 3 8.5 3a2 2 0 0 0 0 3.5zM12 6.5S13.5 3 15.5 3a2 2 0 0 1 0 3.5z'/%3E%3C/svg%3E") no-repeat center / 20px 20px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linejoin='round'%3E%3Crect x='3' y='9' width='18' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h20V9H2zM12 6.5V20'/%3E%3Cpath d='M12 6.5S10.5 3 8.5 3a2 2 0 0 0 0 3.5zM12 6.5S13.5 3 15.5 3a2 2 0 0 1 0 3.5z'/%3E%3C/svg%3E") no-repeat center / 20px 20px;
}
.buy-redeem-row { display: flex; gap: 14px; }
.buy-redeem-row input {
  flex: 1;
  min-width: 0;
  height: 52px;
  padding: 0 18px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--buy-control-bg);
  color: var(--text);
  font: inherit;
  font-size: 16px;
}
.buy-redeem-button {
  flex: none;
  min-height: 52px;
  padding: 0 26px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--buy-control-bg);
  font-size: 16px;
  font-weight: 600;
}
/* 悬浮不变样：全站的按钮都只在按下时缩一下，其它状态一个样。 */
.buy-redeem-button:hover:not(:disabled),
.buy-redeem-button:focus,
.buy-redeem-button:focus-visible { background: var(--buy-control-bg); border-color: var(--line); }

/* ---- 我的订单 ---- */
/* ---- 积分明细 ---- */
.credits-head { margin-bottom: 16px; }
.credits-head h2 { margin: 0 0 8px; }
.credits-balance { display: flex; align-items: baseline; gap: 10px; margin: 0; color: var(--muted); font-size: var(--font-base); }
.credits-balance-value { display: inline-flex; align-items: center; gap: 4px; color: var(--text); }
.credits-balance-value strong { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.credits-bolt { width: 16px; height: 16px; background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain; }
.credits-tabs { border-bottom: 1px solid var(--line); }
/* 列宽固定：已获取 / 已消耗两栏切换时列不跟着内容跳；积分值那列离右边留一段。 */
.credits-table { min-width: 0; table-layout: fixed; }
.credits-table th:nth-child(1), .credits-table td:nth-child(1) { width: 180px; }
/* 「来源」这一列每一笔都带着有效期和到期日（生成任务 · 图片生成 · 有效期 31 天 · 2026.10.10到期），
   最长四段；放宽到 400px，「详情」也跟着往右挪到靠中间的位置。 */
.credits-table th:nth-child(2), .credits-table td:nth-child(2) { width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.credits-table th:nth-child(3), .credits-table td:nth-child(3) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.credits-table th:last-child, .credits-table td:last-child { width: 200px; text-align: right; padding-right: 72px; }
@media (max-width: 1100px) { .credits-table th:last-child, .credits-table td:last-child { width: 140px; padding-right: 24px; } }
.credits-note { color: var(--muted); }
.credits-col-amount { font-variant-numeric: tabular-nums; white-space: nowrap; }
.credits-plus { color: var(--green); }
.credits-minus { color: var(--text); }
.credits-free { color: var(--muted); text-decoration: line-through; }
.orders-head { margin-bottom: 16px; }
.orders-sub { margin: 6px 0 0; color: var(--muted); font-size: var(--font-base); }
.orders-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.orders-toolbar input[type="search"] {
  flex: 0 1 260px;
  height: 40px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
  color: var(--text);
  font: inherit;
}
.orders-toolbar select {
  width: 130px;
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}
.orders-range {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}
.orders-range-icon {
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E") no-repeat center / 16px 16px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E") no-repeat center / 16px 16px;
}

.orders-table-wrap {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  overflow: auto;
}
.orders-table { width: 100%; min-width: 1200px; border-collapse: collapse; }
.orders-table th {
  padding: 15px 20px;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--font-base);
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}
.orders-table td {
  padding: 16px 20px;
  border-bottom: 1px solid var(--cell-line);
  font-size: var(--font-base);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.orders-table tbody tr:last-child td { border-bottom: 0; }
.orders-table tbody tr:hover { background: var(--surface-hover); }
/* 号码和复制按钮同一行、贴着走。按钮 flex:none（见 .pay-copy-button），
   号码再长也不会把它挤下去。 */
.order-id {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 15px;
}
.order-id > span { min-width: 0; word-break: break-all; }
/* 参考图里实付金额是红的，状态待确认橙、成功绿。 */
.order-amount { color: #e06666; }
.order-status-pending { color: #e0a33a; }
.order-status-paid { color: #3fb884; }
.order-status-refunded { color: var(--muted); }
.order-status-cancelled { color: var(--muted); }
.order-credits { display: inline-flex; align-items: center; gap: 7px; }
.order-credits::before {
  content: "";
  flex: none;
  width: 15px;
  height: 15px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.orders-empty { padding: 56px 20px; color: var(--muted); text-align: center; }

/* 资产卡右上角的勾选框：决定「生成提示词」这次给哪几项写。
   尺寸必须写死：全局的 `input, select, textarea { width: 100% }` 和
   `input, select { height: 36px; padding: 7px 9px }` 也会套到勾选框上，
   把它撑成一条 300 多像素宽、36 像素高的横条，整个卡片头就被顶垮了。
   flex:none 只挡得住伸缩，挡不住 width:100%。 */
.asset-card-head .asset-pick {
  flex: none;
  align-self: center;
  /* 比删除按钮小一圈就够了，靠 align-self:center 和它对齐，不用同宽同高。
     勾选框和删除一起推到名字行最右边（用户 2026-09-12 要求重新排版；卡片尺寸不动，只挪这一行里的位置），
     勾用和选中绿边同一个绿。 */
  width: 18px;
  height: 18px;
  min-height: 0;
  margin: 0 2px 0 auto;
  padding: 0;
  accent-color: var(--green);
  cursor: pointer;
}
.asset-card-head .asset-pick:disabled { opacity: .35; cursor: not-allowed; }
/* 删除按钮原来靠 margin-left:auto 顶到右边，现在这个位置让给勾选框了。 */
.asset-card-head .asset-pick + .asset-delete { margin-left: 0; }

.asset-card-head .asset-prompt-generate {
  flex: none;
  align-self: center;
  min-height: 28px;
  padding: 0 9px;
  white-space: nowrap;
  font-size: 12px;
}
/* 「生成提示词」把右边那一组顶到头。「去生图」不在这一行了：提示词写好之后它在图框正中。 */
.asset-card-head .asset-prompt-generate { margin-left: auto; }
/* 「去生图」是蓝的（和「下载图片」同一档），左边那颗生成绿的是要花积分的，
   一眼分得开。颜色并在上面那条蓝色规则里，别在这儿另写一份。
   按钮规矩（悬浮不换色、只有按下缩小）由 `.asset-card-head button` 那条通用规则给。
   它不花积分，所以也不带价签。 */

/* 卡片头是按基线对齐的（为了 ID 和名字那两段文字），但勾选框和删除按钮是方块，
   没有文字基线可言，跟着基线走就会差几像素。这两个单独按中心对齐。 */
.asset-card-head .asset-pick,
.asset-card-head .asset-import,
.asset-card-head .asset-delete,
.asset-card-head .asset-more { align-self: center; }

/* 资产库卡片右上角的导入：把这一项加到当前选中的那一集。
   和删除按钮同一副外形，只是悬浮时是主色不是红色。 */
.asset-card-head .asset-import {
  margin-left: auto;
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
}

.asset-card-head .asset-import:hover:not(:disabled) {
  color: var(--green);
  border-color: var(--green);
  background: var(--green-soft);
}

.asset-card-head .asset-import:disabled { opacity: .35; cursor: not-allowed; }
.asset-card-head .asset-import + .asset-delete { margin-left: 0; }

/* 名字后面的改名铅笔：和右边的导入 / 更多同一尺寸。
   不写 min-height: 0 的话会被全局按钮的最小高度撑到 34px，比旁边高出一截。 */
.asset-card-head .rename-pen {
  width: 28px;
  height: 28px;
  min-height: 0;
  margin-left: 4px;
  align-self: center;
  border-radius: 6px;
}
.asset-card-head .asset-more {
  width: 28px;
  height: 28px;
  min-height: 0;
  margin-left: 0;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 6px;
  font-size: 11px;
  line-height: 1;
}

.asset-action-menu {
  position: fixed;
  z-index: 220;
  display: grid;
  gap: 4px;
  width: 136px;
  padding: 6px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--surface);
  box-shadow: 0 12px 32px var(--dialog-shadow);
}
.asset-action-menu-item {
  width: 100%;
  min-height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: 4px;
  background: var(--command-neutral-bg);
  color: var(--text);
  font: inherit;
  text-align: left;
  transform: scale(1);
  transition: transform .08s ease;
}
.asset-action-menu-item:hover:not(:disabled) {
  background: var(--command-neutral-bg);
  color: var(--text);
}
.asset-action-menu-item.danger,
.asset-action-menu-item.danger:hover:not(:disabled) {
  background: var(--red-soft);
  color: var(--red);
}
.asset-action-menu-item:active:not(:disabled) { transform: scale(.96); }
.asset-action-menu-item:disabled { opacity: .45; cursor: not-allowed; }

/* 分集里点「添加资产」先问一句：新建，还是从资产库挑。 */
.asset-source-dialog { max-width: 600px; }

/* ---- 资产库的竖版卡片 ---- */
/* 提示词挪进弹窗之后卡片只剩图和一行名字，于是图能占满整宽、按设定图真实的
   16:9 显示（原来是塞进 128×170 的竖框里，两侧被裁掉一大半），一行也摆得下更多。 */
.asset-drawer-body .asset-grid {
  /* 固定三列。auto-fill 会随抽屉宽度跳来跳去；写死列数，任何宽度下都是等宽的卡片。
     **不是四列**：卡片上多了名字行和「资产类型 / 文件大小 / 创建时间」那一条，
     四列里的一格只有 ~300 宽，日期那一格会被截断。 */
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.asset-card-library {
  display: flex;
  flex-direction: column;
  gap: 8px;
  height: auto;
  min-height: 0;
  padding: 10px;
}

.asset-card-library .asset-figure {
  width: 100%;
  display: block;
  grid-template-rows: none;
}

.asset-card-library .asset-figure a,
.asset-card-library .asset-figure img,
.asset-card-library .asset-placeholder {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
}

/* 设定图本来就是 16:9，contain 才是原样显示；cover 会把两侧的视图裁掉。 */
.asset-card-library .asset-figure img { object-fit: contain; background: var(--surface-soft); }

/* 上传图片、下载图片、提交审核并成一行——卡片高度就定死了，
   鼠标划过去不会再忽高忽低。 */
.asset-card-library .asset-figure-tools {
  width: 100%;
  margin-top: 2px;
  /* 三颗按钮平分整行宽度。 */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}
/* 按钮行末尾那颗「更多」按内容定宽，前面几颗平分剩下的。 */
.asset-card-library .asset-figure-tools.asset-tools-triple { grid-template-columns: repeat(3, minmax(0, 1fr)) auto; }
.asset-card-library .asset-figure-tools.asset-tools-pair { grid-template-columns: repeat(2, minmax(0, 1fr)) auto; }
.asset-card-library .asset-figure-tools .asset-more { width: 34px; min-width: 0; padding: 0; letter-spacing: 1px; }

/* 名字行最左边那枚类型图标。 */
.asset-card-library .asset-card-kind {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 8px; background: var(--accent-soft); color: var(--accent-text);
}
/* 右上角那枚药丸：还剩多久。过期了直接说不能下载——这时候报一个负数没意义。 */
.asset-retention {
  flex: none; margin-left: auto; padding: 1px 10px; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--blue) 34%, transparent);
  background: var(--blue-soft); color: var(--blue);
  font-size: 12px; line-height: 18px; white-space: nowrap; cursor: help;
}
.asset-retention.is-warn {
  border-color: color-mix(in srgb, var(--warn, #d9a441) 40%, transparent);
  background: rgba(217, 164, 65, .14); color: var(--warn, #d9a441);
}
.asset-retention.is-expired {
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  background: var(--red-soft); color: var(--red);
}
/* 过期了：框里写一句「资源已过期」。红一点，和「还没生成」那块灰的分得开。 */
.asset-placeholder-expired {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--red) 40%, transparent);
  color: var(--red);
}
/* 图下面那一条：资产类型 / 文件大小 / 创建时间，三格等宽、中间一道细线隔开。 */
.asset-meta {
  display: grid;
  /* 三格不等宽：日期最长，多分一点，不然「2026-09-08 11:12」会被截掉尾巴。字体换成 Inter 之后数字宽一成（2026-10-12），
     手机上日期又差十来个像素，再匀给它一点：.8 / .85 / 1.35 → .75 / .8 / 1.45。 */
  grid-template-columns: minmax(0, .75fr) minmax(0, .8fr) minmax(0, 1.45fr);
  padding: 8px 2px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-soft);
}
.asset-meta-item { display: flex; align-items: center; gap: 7px; min-width: 0; padding: 0 7px; color: var(--muted); }
.asset-meta-item + .asset-meta-item { border-left: 1px solid var(--line); }
.asset-meta-item > svg { flex: none; }
.asset-meta-text { display: grid; gap: 1px; min-width: 0; }
.asset-meta-label { font-size: 11px; line-height: 15px; color: var(--muted); }
.asset-meta-value {
  font-size: 12px; line-height: 17px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 控制台素材库用量：几行小字和两个清单，别铺成表格。 */
/* 省钱线路（控制台「其他视频设置」，2026-10-05）：型号名带供应商和上游型号名，下拉放宽一点；「会不会切」那一格按行写，顶对齐。 */
.console-cost-route-table td { vertical-align: top; }
.console-cost-route-table select { width: 100%; min-width: 240px; height: 34px; }
.console-cost-route-table input[type="number"] { width: 80px; height: 34px; }
.console-cost-route-preview { min-width: 320px; color: var(--muted); line-height: 1.6; white-space: normal; }
.console-asset-library-body ul { margin: 4px 0 8px 18px; padding: 0; color: var(--muted); font-size: var(--font-base); line-height: 1.6; }

/* 成本价格册（其他视频设置）：来源一行 + 价格表；导入弹窗里的预览表复用控制台表格样式。 */
.console-cost-book-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; min-width: 0; }
#consolePage .console-model-group.console-cost-book { padding: 0; border: 0; background: transparent; gap: 14px; min-width: 0; }
.cost-book-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.cost-book-toolbar h4 { font-size: 18px; }
.cost-book-toolbar .console-toolbar { margin: 0; flex-wrap: wrap; }
.cost-book-command { flex: none; min-height: 36px; height: 36px; padding: 0 16px; }
.console-cost-book-body > .panel-note { margin: 0; overflow-wrap: anywhere; }
/* 成本价格管理那一页整张表铺开，保存按钮自然落在最底下；弹窗里的预览表才限高。 */
.console-cost-book-body .table-wrap { max-height: none; }
.cost-book-preview .table-wrap { max-height: 320px; }
/* 成本价格管理那一页只有价格册，一列铺满，不跟其他视频设置那两列走。 */
#consoleModelCosts { grid-template-columns: minmax(0, 1fr); }
#consoleModelCosts:empty { display: none; }
/* 复用分镜表的行底色和表头，每个档位完整占一行。 */
#consoleModelCosts .console-cost-book-body .cost-book-table-wrap { min-width: 0; border: 1px solid var(--line-strong); border-radius: 6px; padding: 0 8px; background: var(--surface); }
.shot-table.cost-book-table { min-width: 620px; table-layout: fixed; }
.shot-table.cost-book-table th,
.shot-table.cost-book-table td { padding: 12px 16px; font-size: 14px; }
.shot-table.cost-book-table th { height: 48px; color: var(--table-header-text); }
.shot-table.cost-book-table td { height: 64px; font-variant-numeric: tabular-nums; }
.shot-table.cost-book-table tr { cursor: default; }
.shot-table.cost-book-table th + th,
.shot-table.cost-book-table td + td { border-left: 1px solid var(--cell-line); }
.cost-book-model { vertical-align: middle; }
.cost-book-model strong { display: block; }
.cost-book-model small { display: block; margin-top: 4px; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
.cost-book-price { font-variant-numeric: tabular-nums; }
.cost-book-unsupported { color: var(--muted); }
/* 全局 td 是一行省略号、控制台表是固定布局：这两张表的算式和模型名要看全，让它们换行。 */
.cost-change-table { table-layout: auto; }
.cost-book-table td, .cost-change-table td { white-space: normal; overflow: visible; }
.cost-change-table tr.cost-change-same td { color: var(--muted); }
.cost-book-dialog { width: min(1180px, calc(100vw - 32px)); }
.cost-book-head { align-items: flex-start; }
.cost-book-title { display: flex; align-items: flex-start; gap: 14px; min-width: 0; }
.cost-book-title-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent-text);
}
.cost-book-title h3 { font-size: 22px; }
.cost-book-body { gap: 14px; padding: 16px 28px 22px; }
/* 两块来源并排：网址宽一些，文档那块是一个虚线框。 */
.cost-book-sources { display: grid; grid-template-columns: 1.6fr 1fr; gap: 12px; }
.cost-book-source {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}
.cost-book-source h4 { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 14px; }
.cost-book-source h4 svg { color: var(--accent-text); }
.cost-book-source-formats { color: var(--muted); font-weight: 400; font-size: 13px; }
.cost-book-url-row { display: flex; gap: 8px; }
.cost-book-url-row input { flex: 1; min-width: 0; }
.cost-book-url-row button, .cost-book-drop-button, #costBookReparse, #costBookImportConfirm {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.cost-book-url-row button { flex: none; }
.cost-book-drop {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 46px;
  padding: 6px 10px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.cost-book-drop.dragging { border-color: var(--accent); background: var(--accent-soft); }
.cost-book-drop.disabled { opacity: .5; pointer-events: none; }
.cost-book-drop-button { padding: 0 6px; font-weight: 600; }
.cost-book-drop-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: 13px; }
/* 识别结果横幅：绿勾 + 两行字 + 右边「重新识别」。解析中勾变灰。 */
.cost-book-status {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}
.cost-book-status-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--green) 18%, transparent);
  color: var(--green);
}
.cost-book-status.busy .cost-book-status-icon { background: var(--surface); color: var(--muted); }
.cost-book-status-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cost-book-status-text strong { font-size: 15px; }
.cost-book-status-text .panel-note { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cost-book-status.busy .cost-book-status-text strong::after { content: "…"; }
#costBookReparse { flex: none; min-height: 34px; }
/* 对照表：表头一行，下面一行一张卡；列模板表头和行共用一条。 */
.cost-book-preview { display: grid; gap: 8px; }
.cost-change-list { --cost-change-cols: minmax(230px, 1.7fr) 1fr 1fr 1.5fr 128px; }
.cost-change-row {
  display: grid;
  grid-template-columns: var(--cost-change-cols);
  gap: 12px;
  align-items: center;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}
.cost-change-head { padding: 6px 14px; border: 0; background: transparent; color: var(--muted); font-size: 13px; font-weight: 600; }
.cost-change-body { display: grid; gap: 8px; max-height: 46vh; overflow: auto; padding-right: 2px; }
.cost-change-row.skipped > :not(.cost-change-actions) { opacity: .4; }
.cost-change-model { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cost-change-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent-text);
}
.cost-change-icon svg { width: 22px; height: 22px; }
.cost-change-model-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cost-change-model-text strong { font-size: 15px; }
.cost-change-model-text small { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cost-change-tags { display: flex; gap: 6px; margin-top: 4px; }
.cost-change-tag { padding: 1px 8px; border: 1px solid var(--line); border-radius: 999px; font-size: 11px; color: var(--muted); }
.cost-change-tag.warn { border-color: var(--red); color: var(--red); }
.cost-change-cell { display: flex; flex-direction: column; gap: 2px; }
.cost-change-cell strong { font-size: 16px; font-variant-numeric: tabular-nums; }
.cost-change-cell strong.edited { color: var(--accent-text); }
.cost-change-cell small { color: var(--muted); font-size: 12px; }
.cost-change-input { width: 120px; height: 32px; font-variant-numeric: tabular-nums; }
.cost-change-delta { display: inline-flex; align-items: center; gap: 2px; font-size: 13px; font-variant-numeric: tabular-nums; }
.cost-change-delta svg { width: 14px; height: 14px; }
.cost-change-delta.good { color: var(--green); }
.cost-change-delta.bad { color: var(--red); }
.cost-change-delta.muted { color: var(--muted); }
.cost-change-formula { display: flex; flex-direction: column; gap: 1px; font-size: 13px; color: var(--muted); }
.cost-change-formula span:first-child { color: var(--text); }
.cost-change-actions { display: flex; gap: 6px; justify-content: flex-end; }
.cost-change-actions button { min-height: 32px; padding: 0 12px; font-size: 13px; }
.cost-book-foot { align-items: center; gap: 10px; }
#costBookOverwriteField { margin: 0; }
.cost-book-foot-spacer { flex: 1; }
@media (max-width: 900px) {
  .cost-book-sources { grid-template-columns: 1fr; }
  .cost-change-list { --cost-change-cols: 1fr; }
  .cost-change-head { display: none; }
}
/* 供应商折扣：一条三格两行（名称 / 折扣 / 备注，开始 / 结束 / 删除）。卡片只有半屏宽，一行六格排不下。 */
.provider-discounts { display: flex; flex-direction: column; gap: 6px; }
.provider-discount-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px 8px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.provider-discount-row .field { margin: 0; min-width: 0; }
.provider-discount-row .field > span { font-size: 12px; }
.provider-discount-row .provider-card-remove { justify-self: end; align-self: end; }
/* 第三行：适用模型 / 适用档位（三个小开关一排）/ 删除；来自价格册的一条右下角带标签。 */
.provider-discount-tiers { grid-column: span 2; display: flex; align-items: center; gap: 10px; min-width: 0; }
.provider-discount-tiers > span:first-child { flex: none; white-space: nowrap; font-size: 12px; color: var(--muted); }
.provider-discount-tiers > label { margin: 0; }
.provider-discount-tiers > label > span { font-size: 12px; }
.provider-discount-origin { justify-self: start; align-self: center; padding: 1px 8px; border: 1px solid var(--accent); border-radius: 999px; color: var(--accent-text); font-size: 11px; }
.provider-discount-footer { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.provider-discount-footer .provider-discount-tiers { flex: 1 1 260px; flex-wrap: wrap; }
.provider-discount-actions { display: flex; flex: none; flex-wrap: nowrap; align-items: center; justify-content: flex-end; gap: 8px; margin-left: auto; }
.provider-discount-actions .provider-discount-origin { flex: none; white-space: nowrap; }
.provider-card-byok { margin-top: 10px; }
/* 上游不支持的档位成本格灰掉。描边那两条在 #consolePage input 那一段后面：
   那条带 id，写在这儿压不过它。 */
.pricing-cost:disabled { opacity: .55; }
/* 资产库顶上「全部提交审核」靠右。 */
.asset-review-all { margin-left: auto; }
/* 没图时图框正中那几颗：占位框本来只是一块状态，这时它就是个动作入口。 */
.asset-placeholder .asset-generate { height: 32px; min-height: 32px; padding: 0 18px; font-size: var(--font-base); }
/* 资产库里两颗叠着：先「生图提示词」再「去生成图片」。两颗一样长（用户原话）：
   网格一列、都拉满列宽，宽度由长的那颗定，短的那颗跟上。 */
.asset-placeholder-actions { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; justify-items: stretch; }
.asset-placeholder-actions > .asset-generate { width: 100%; justify-content: center; }
.asset-placeholder .asset-prompt-step { display: inline-flex; align-items: center; gap: 6px; }
/* 图框里那两颗（「生图提示词」「去生成图片」）灰掉时同一身：中性底、灰字、不透明
   （用户 2026-09-12：「把去生成图片和生图提示词在置灰时的样式统一」）。写好提示词的那颗、正在写的那颗、
   还不能点的「去生成图片」都是 disabled，一条规则管完，别再按 .done 单配。分集卡片那份走 --command-* 变量。 */
#mainView #assetDrawer .asset-placeholder .asset-generate:disabled {
  --asset-button-bg: var(--command-neutral-bg);
  --asset-button-text: var(--muted);
  opacity: 1;
}
#mainView #workflowView .asset-placeholder .asset-generate:disabled {
  --command-bg: var(--command-neutral-bg);
  --command-text: var(--muted);
  opacity: 1;
}
/* 只有一颗按钮时占满整行（分镜图的「下载图片」），两颗就平分——
   写死三列的话视频卡那两颗会在右边留一块空。 */
.asset-card-library .asset-tools-single { grid-template-columns: minmax(0, 1fr); }
.asset-card-library .asset-tools-duo { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 分镜图卡：一颗「下载图片」+ 右边一枚「•••」（用户 2026-09-14：和角色、场景、道具一样，点开是删除资源） */
.asset-card-library .asset-tools-single.has-more { grid-template-columns: minmax(0, 1fr) auto; }

/* 资产库的图只看不点：原来是 <a>，现在是 span / button，尺寸规则跟着走。 */
.asset-figure-image { display: block; width: 100%; }
.asset-card-library .asset-figure-image { height: auto; aspect-ratio: 16 / 9; }
/* 视频卡：封面按钮 + 中间的播放圆点 */
/* 资产库视频卡的封面是整块画面，不是独立的操作按钮：按下不缩（用户 2026-09-12），
   所以它在 styles.css 顶上那条 button:active 的排除名单里。 */
.asset-video-cover { position: relative; padding: 0; border: 0; border-radius: 4px; background: var(--surface-soft); cursor: pointer; overflow: hidden; }
.asset-video-cover img { width: 100%; height: 100%; object-fit: contain; }
/* 封面图拿不到时用视频自己的第一帧当封面（breakdown.js renderVideoCard）：和图同一个摆法，点击穿透给外面那颗按钮。 */
.asset-video-cover video { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
.asset-video-blank { display: grid; width: 100%; height: 100%; place-content: center; color: var(--muted); font-size: var(--font-base); }
.asset-video-play { position: absolute; left: 50%; top: 50%; width: 38px; height: 38px; margin: -19px 0 0 -19px; display: grid; place-content: center; border-radius: 50%; background: rgba(0, 0, 0, .55); color: #fff; font-size: 15px; padding-left: 3px; }
.asset-video-card .asset-body small { color: var(--muted); }

/* 视频播放弹窗：画面按自己的比例缩在里面，整块不出滚动条。
   用 [open] 选中：dialog 关闭时靠 UA 的 display:none，直接写 display:flex 会把它顶开。 */
.video-player-dialog { width: min(960px, 92vw); max-width: 92vw; height: auto; max-height: 92vh; padding: 16px; border: 0; border-radius: 14px; background: var(--surface); color: var(--text); overflow: hidden; }
.video-player-dialog { flex-direction: column; }
.video-player-dialog[open] { display: flex; }
.video-player-dialog::backdrop { background: rgba(0, 0, 0, .55); }
.video-player-dialog .dialog-header { flex: none; }
.video-player-dialog video { display: block; flex: 1; min-height: 0; width: 100%; max-height: calc(92vh - 104px); margin-top: 12px; border-radius: 10px; background: #000; object-fit: contain; }

.asset-card-library .asset-figure-tools > * {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.asset-card-library .asset-body {
  min-height: 0;
  height: auto;
  gap: 6px;
}

/* 竖版里名字要能换行显示全，卡片窄，一行放不下。 */
/* 四类卡片（角色 / 场景 / 道具 / 分镜图 / 视频）高度必须一模一样，否则来回切页签会跳。
   图是 16:9 由列宽决定，本来就一致；剩下两段在这里钉死：
   1) 名字行固定 28px —— 角色卡那一行里有 28px 的改名 / 导入 / 更多按钮，没有按钮的卡片
      光靠文字只有 20 出头，差的就是这一截；
   2) 名字只占一行，长名字用省略号截断 —— 分镜图的名字（S01-E01-… · 镜头07）比人名长得多，
      一换行整张卡就高一截。 */
.asset-card-library .asset-card-head { align-items: center; min-height: 28px; }
.asset-card-library .asset-card-head strong {
  display: flex;
  flex: 1;
  align-items: center;
  min-width: 0;
  font-size: 15px;
  line-height: 20px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* 角色卡的名字里还套着一个可点的文本 + 铅笔：截断落在文本上，铅笔不能被裁掉。 */
.asset-card-library .asset-card-head strong > .rename-text {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.asset-card-library .asset-card-head .asset-id { flex: none; font-size: 13px; }
/* 按钮行定高：一颗和两颗一样高。 */
.asset-card-library .asset-figure-tools { min-height: 30px; }
.asset-card-library .asset-figure-tools button { height: 30px; min-height: 30px; }

/* 库里的卡片不参与勾选，也不该被 .asset-card 那条固定高度按住。 */
.asset-drawer-body .asset-card-library { height: auto; }

/* 窄屏抽屉本身就只有 94vw，四列会挤成一条，降到两列。 */
@media (max-width: 1100px) {
  .asset-drawer-body .asset-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 用户时间线弹窗：一屏几百条，越大越好用。头和筛选固定，列表自己滚。
   加了「积分明细」一列之后用户要再宽一点（2026-09-10），从 1180 放到 1440；
   说明列写上「实扣」之后又截字（2026-09-11），再放到 1720；模型改写成「供应商·上游型号」
   （seedance官方·doubao-seedance-2-0-260128）之后再放到 1880，用户还嫌窄，上限放到 2200、
   两边只各留 12px——1920 宽的屏上它就是几乎铺满整个视口。
   **高度也定死**（用户 2026-09-11：「窗口大小要固定」）：原来只有 max-height，记录少的人
   弹窗矮一截、换个人或换个筛选整个窗口上下跳；现在始终占满视口高度减 40px，列表在里面滚。 */
.metrics-timeline-dialog { width: min(2200px, calc(100vw - 24px)); height: calc(100vh - 40px); max-height: calc(100vh - 40px); }

.metrics-timeline-dialog { flex-direction: column; }
.metrics-timeline-dialog[open] { display: flex; }

/* 标题那一行：账号名后面跟一枚「⚡余额」（用户 2026-09-11：「用户名后面显示用户的积分余额」）。
   h2 和徽标并排、按基线对齐；图标用全站那枚单色闪电，颜色跟着字走，不另画一个。 */
/* 徽标离名字远一点：10px 时两者看着像一个词（用户 2026-09-11：「积分和账号里的太近了」）。 */
.metrics-timeline-title-row { display: flex; align-items: baseline; gap: 22px; min-width: 0; }
.metrics-timeline-title-row h2 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 标题底下那行注册信息：灰一档、小一号，和「用户时间线」那个类型标签一个分量 */
/* 两列的 grid：第一列注册方式 / 注册于，第二列邀请码 / 最近活跃，上下两行按列对齐；项之间靠列距隔开，不画「·」
   （用户 2026-09-14：「用空格，可以多隔开一点」「邀请码和最近活跃左对齐」）。行只是分组，display: contents 让格子直接落进 grid。 */
.metrics-timeline-profile { display: grid; grid-template-columns: max-content max-content; gap: 2px 22px; margin-top: 2px; color: var(--muted); font-size: 13px; }
.metrics-timeline-profile-row { display: contents; }
.metrics-timeline-profile-note { grid-column: 1 / -1; white-space: pre-wrap; overflow-wrap: anywhere; }
.metrics-timeline-balance {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
  margin-top: 7px;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  --credits-icon-color: currentColor;
}
.metrics-timeline-balance .credits-bolt { width: 14px; height: 14px; }

.metrics-timeline-filters {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--line);
}

.metrics-timeline-filters .field { flex: none; width: 180px; }
.metrics-timeline-filters .metrics-range-field { width: auto; }

/* 起止各一个日期 + 一个时刻，中间一个破折号。日期框点开就是浏览器自带的
   日历，不用自己画一个。 */
.metrics-range { display: flex; align-items: center; gap: 6px; }
.metrics-range input[type="date"] { width: 148px; }
.metrics-range input[type="time"] { width: 104px; }
.metrics-range input { min-width: 0; }
/* 点一下就弹选择器，顺带把里面刷成蓝底很难看。这里有两层：
   user-select 管的是普通文本选区；而 date/time 输入框里的年、月、日、时、分
   是 shadow DOM 里的独立字段，聚焦时 Chrome 会给当前那一段刷上选中色，
   user-select 管不到，只能改它自己的伪元素。 */
.metrics-range input[type="date"],
.metrics-range input[type="time"] { user-select: none; -webkit-user-select: none; }

.metrics-range input[type="date"]::selection,
.metrics-range input[type="time"]::selection { background: transparent; }

/* :focus 必须挂在 input 上再接伪元素——写成 ::-webkit-...-field:focus 是匹配
   不上的（试过，一点效果没有）。UA 那条选中色优先级也高，得 !important。 */
.metrics-range input:focus::-webkit-datetime-edit-year-field,
.metrics-range input:focus::-webkit-datetime-edit-month-field,
.metrics-range input:focus::-webkit-datetime-edit-day-field,
.metrics-range input:focus::-webkit-datetime-edit-hour-field,
.metrics-range input:focus::-webkit-datetime-edit-minute-field,
.metrics-range input:focus::-webkit-datetime-edit-second-field,
.metrics-range input:focus::-webkit-datetime-edit-ampm-field {
  background-color: transparent !important;
  color: inherit !important;
  outline: none;
}
.metrics-range-dash { color: var(--muted); padding: 0 2px; }
.metrics-range button { min-height: 36px; padding: 0 12px; }
.metrics-timeline-filters .field span { display: block; margin-bottom: 4px; color: var(--muted); font-size: var(--font-base); }
/* 条数靠右，和「操作」下拉框、刷新在一条水平线上（用户 2026-09-12）：.panel-note 自带 16px 下边距，
   行是底对齐的，不清掉它就比控件高出一截。高度和控件一样、字竖着居中，中线才对得上。 */
.metrics-timeline-filters #metricsTimelineCount { display: inline-flex; align-items: center; min-height: 36px; margin: 0 0 0 auto; line-height: 1.2; }

/* 滚动条的位置常留着：记录少的人没有滚动条、多的人有，不留的话两人的列宽差一条滚动条，换人时整列挪一下。 */
.metrics-timeline-body { flex: 1; min-height: 0; overflow: auto; scrollbar-gutter: stable; padding: 0 20px 18px; }

/* 列表本身那条 max-height:520px 是它还在面板里时留的。搬进弹窗后由
   .metrics-timeline-body 负责滚动，再卡一层高度只会白留一大块空。 */
.metrics-timeline-dialog .metrics-timeline { max-height: none; overflow: visible; }

/* 描述弹窗：上下两块，上面图、下面描述，**两块等高**，四周和块间边距一律 20px。
   弹窗自己一格滚动条都没有，要滚只在描述框里滚——弹窗整体能滚的话，改着字页面会跟着跳。

   **宽度不能写死。** 三条要求联立：图要完整的 16:9、描述和图等高、边距一致，于是
     图高 = 描述高 = B，图宽 = B × 16/9，弹窗宽 = B × 16/9 + 40
     弹窗高 = 标题栏 + 底栏 + 三道间距 + 2B
   B 只能由**视口高度**倒推，弹窗宽跟着 B 走。宽度写死多少，图两边就空出多少。 */
/* **两个类一起写**：这个弹窗同时挂着 new-work-dialog，而那条 `.new-work-dialog
   { width; height }` 写在样式表更后面，同权重后来者胜，会把下面这些宽高整个盖掉
   （盖成 880 × 640，图就显示不全）。加一个类提权，别再被它压回去。 */
.new-work-dialog.asset-prompt-dialog {
  --prompt-pad: 20px;
  /* 减掉的是标题栏 + 底栏（约 154px）和三道间距，再留一点余量。
     封顶 500px：大屏上也别撑得离谱。 */
  --prompt-block: min(500px, calc((100vh - 48px - 154px - 3 * var(--prompt-pad) - 26px) / 2));
  width: min(calc(var(--prompt-block) * 16 / 9 + 2 * var(--prompt-pad)), calc(100vw - 32px));
  height: auto;
  max-height: calc(100vh - 48px);
  overflow: hidden;
}
.new-work-dialog.asset-prompt-dialog { flex-direction: column; }
.new-work-dialog.asset-prompt-dialog[open] { display: flex; }
/* 开合动画在最上面那条 dialog 规则里，全站一份，这里不再单写。 */
.asset-prompt-dialog > .dialog-header,
.asset-prompt-dialog > .dialog-footer,
.asset-prompt-dialog > .asset-prompt-figure { flex: none; }
/* 分镜行的「详情」借这个弹窗看镜头的生图提示词：样式一样，只是不放图、只留下半部分
   （用户 2026-09-12）。宽度照旧按图的 16:9 反推，文本框还是那么高。 */
.asset-prompt-dialog.is-shot > .asset-prompt-figure { display: none; }
/* 没有图就别让文本框也照着图那么高（用户 2026-09-12：「下面一大片空白」）：按内容取高，具体高度
   breakdown.js 的 fitShotPromptHeight 按 scrollHeight 写行内样式，这里只给下限。 */
.asset-prompt-dialog.is-shot #assetPromptText { height: auto; min-height: 120px; }
/* **弹窗本身也要按内容取高。** 上面那条 height: auto 压过了浏览器给 dialog:modal 的 height: fit-content，
   而 dialog 是 inset: 0 的定位盒，height 一旦是 auto 就被拉满到 max-height（安全验证弹窗踩过同一个坑，
   见 .captcha-dialog 那条注释）。资产那份看不出来，是因为图 + 文本框本来就设计成正好填满。
   （用户 2026-09-12 截图：底栏下面空出一大截。） */
.new-work-dialog.asset-prompt-dialog.is-shot { height: fit-content; }
/* 底栏上边距对齐成同一个 20px，四周就一致了。 */
.asset-prompt-dialog > .dialog-footer { padding-top: var(--prompt-pad); }
/* 底栏左边那一行小字：图片页任务的参考图匹配情况（2026-09-24）。占住按钮之间剩下的宽度，放不下省略，全文悬浮看；
   不另起一行——弹窗的高是按图和文本框等高倒推的，多一行底栏就被挤出屏幕。 */
.asset-prompt-dialog > .dialog-footer { align-items: center; }
.asset-prompt-note { flex: 1 1 auto; min-width: 0; overflow: hidden; color: var(--muted); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.asset-prompt-dialog #assetPromptText {
  display: block;
  flex: none;
  /* 和上面那张图等高 */
  height: var(--prompt-block);
  width: calc(100% - 2 * var(--prompt-pad));
  margin: var(--prompt-pad) var(--prompt-pad) 0;
  /* 长描述在框里自己滚，别把弹窗撑高 */
  overflow-y: auto;
  resize: none;
}

/* 左右两颗大按钮：新建 / 从资产库导入。
   按这屋子的规矩来：固定色块，平时 / 悬浮 / 聚焦一个样，只有按下缩小。 */
.asset-source-choices {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 18px 20px 22px;
}

.asset-source-choice,
.asset-source-choice:hover:not(:disabled),
.asset-source-choice:focus,
.asset-source-choice:focus-visible {
  display: grid;
  gap: 8px;
  align-content: center;
  justify-items: center;
  text-align: center;
  padding: 26px 20px;
  min-height: 128px;
  border: 0;
  border-radius: 14px;
  background: var(--surface-soft);
  color: var(--text);
  outline: none;
  box-shadow: none;
  transition: transform .08s ease;
}
.asset-source-choice:active:not(:disabled) { transform: scale(.96); }

/* 图标章：一个圆角方块，两颗各用一种色调，一眼能分出左右是两件事 */
.asset-source-glyph {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin-bottom: 2px;
  border-radius: 12px;
}
.asset-source-glyph.create { background: var(--green-soft); color: var(--green); }
.asset-source-glyph.import { background: var(--blue-soft); color: var(--blue); }

.asset-source-title { font-size: 16px; font-weight: 650; color: var(--text); }

/* 上传框：空着是一块带虚线内描边的底，选了图就把图铺满，底下压一条「点击更换」。
   内描边用 inset box-shadow 而不是 border，尺寸不会跟着变。 */
.field-aside { margin-left: 6px; color: var(--muted); font-size: 12px; font-weight: 400; }
.new-asset-image-box,
.new-asset-image-box:hover:not(:disabled),
.new-asset-image-box:focus,
.new-asset-image-box:focus-visible {
  position: relative;
  display: grid; place-items: center;
  width: 100%; height: 176px; padding: 0;
  border: 0; border-radius: 12px;
  box-shadow: inset 0 0 0 1px var(--line);
  background: var(--surface-soft); color: var(--muted);
  font: inherit; cursor: pointer; overflow: hidden;
  outline: none;
  transition: transform .08s ease;
}
.new-asset-image-box:active:not(:disabled) { transform: scale(.98); }
.new-asset-image-preview { width: 100%; height: 100%; object-fit: contain; background: var(--surface); }
.new-asset-image-hint { display: grid; gap: 4px; justify-items: center; line-height: 1.4; }
.new-asset-image-hint b { color: var(--text); font-size: var(--font-base); font-weight: 600; }
.new-asset-image-hint small { color: var(--muted); font-size: 12px; }
/* 「点击更换」只在真有图的时候压在底部 */
.new-asset-image-swap {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: none; padding: 6px 0;
  background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 12px; text-align: center;
}
.new-asset-image-box.has-image .new-asset-image-swap { display: block; }

/* 顶部搜索条：一个放大镜 + 一个输入框，整条是一块底色，不描边。
   input 自己不带背景和边框，边框画在外面这一条上，聚焦时整条亮起来。 */
.asset-import-search {
  display: flex; align-items: center; gap: 8px;
  flex: none;
  margin: 14px 20px 0;
  padding: 0 12px;
  height: 40px;
  border-radius: 10px;
  background: var(--surface-soft);
  color: var(--muted);
}
.asset-import-search-icon { display: grid; place-items: center; flex: none; }
.asset-import-search input,
.asset-import-search input:focus {
  flex: 1; min-width: 0;
  border: 0; background: transparent; color: var(--text);
  font: inherit; outline: none;
}
.asset-import-search input::placeholder { color: var(--muted); }
/* Chrome 给 type=search 自带的那颗叉是系统样式，和这套深色不搭 */
.asset-import-search input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

/* 导入弹窗里的资产网格：只列当前这一类。卡片同样是固定色块，只有按下缩小。 */
.asset-import-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(186px, 1fr));
  /* 行高必须写 max-content。留 auto 的话，网格在定高弹窗里是个会收缩的 flex 项，
     行轨道会被压到 136px，而卡片内容有 171px——名字那一行整条被 overflow 切掉，
     卡片上只剩一张图。量出来的，不是猜的。 */
  grid-auto-rows: max-content;
  align-content: start;
  flex: 1 1 auto; min-height: 0;
  padding: 14px 20px 16px;
  overflow-y: auto;
}
.asset-import-item,
.asset-import-item:hover:not(:disabled),
.asset-import-item:focus,
.asset-import-item:focus-visible {
  position: relative;
  display: grid; gap: 0; padding: 0;
  border: 0; border-radius: 12px;
  background: var(--surface-soft); color: var(--text);
  font: inherit; text-align: left; cursor: pointer;
  overflow: hidden;
  outline: none; box-shadow: none;
  transition: transform .08s ease;
}
.asset-import-item:active:not(:disabled) { transform: scale(.96); }
.asset-import-item:disabled { opacity: .5; cursor: not-allowed; }
/* 选中态：一圈绿描边 + 左上角一枚对勾。多选要看得见选了哪几张，
   所以这里必须和未选中不一样——那条「四态同色」的规矩管的是按钮，不是勾选。 */
.asset-import-item.picked,
.asset-import-item.picked:hover:not(:disabled),
.asset-import-item.picked:focus,
.asset-import-item.picked:focus-visible {
  box-shadow: inset 0 0 0 2px var(--green);
}
.asset-import-check {
  position: absolute; top: 8px; left: 8px;
  display: none; place-items: center;
  width: 22px; height: 22px; border-radius: 999px;
  background: var(--green); color: #06210f;
  font-size: 13px; font-weight: 800; line-height: 1;
}
.asset-import-item.picked .asset-import-check { display: grid; }
/* 图给死高度，不写 aspect-ratio。替换元素的 aspect-ratio 要先知道列宽、
   列宽又要先知道行高，浏览器解这个循环时会把下面名字那一行挤掉 6px，
   名字被 overflow:hidden 切掉半行。写死高度就没有这个循环，
   反正是 object-fit: cover，比例不会被拉变形。 */
.asset-import-item img {
  width: 100%; height: 132px; object-fit: cover;
  background: var(--surface);
}
.asset-import-name {
  padding: 9px 11px 11px;
  font-size: var(--font-base); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 「已在本集」压在图的右上角，不去挤名字那一行 */
.asset-import-badge {
  position: absolute; top: 8px; right: 8px;
  padding: 2px 8px; border-radius: 999px;
  background: rgba(0, 0, 0, .6); color: #fff;
  font-size: 12px; font-weight: 600;
}
.asset-import-empty {
  grid-column: 1 / -1;
  display: grid; gap: 6px; justify-items: center;
  padding: 48px 24px; color: var(--muted); text-align: center;
}

/* 卡片空白处也能勾选：给个手型和选中态，让人知道整张卡是可点的。 */
.asset-card-pickable { cursor: pointer; }
.asset-card-picked { border-color: var(--green) !important; }

/* ==================== API 视频工作台 ==================== */

.video-studio-view {
  min-height: 0;
  padding: 8px;
  overflow: hidden;
  background: var(--bg);
}

.video-studio {
  /* 「生成镜头描述」按钮和它正上方清晰度下拉的固定宽度（用户 2026-09-12 定的：按钮宽度写死，文案换了不动）。
     按最长的文案「重生镜头描述」6 个字 84 + 价签约 50 + 左右内边距 36 算，留几像素给两位数的价。 */
  --studio-prompt-width: 176px;
  display: grid;
  /* 上方固定三栏：素材 / 预览 / 文本；下方分镜台横向占满。 */
  grid-template-columns: minmax(280px, 300px) minmax(440px, 1fr) minmax(430px, 560px);
  grid-template-rows: minmax(0, 68fr) minmax(260px, 32fr);
  grid-template-areas:
    "stage stage inspector"
    "board board board";
  gap: 10px;
  width: 100%;
  height: 100%;
  min-height: 0;
  color: var(--text);
}

.video-studio.loading { cursor: progress; }

.studio-panel {
  min-width: 0;
  border: 0;
  border-radius: 8px;
  background: var(--surface);
}

.studio-section-head {
  display: flex;
  min-height: 36px;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px;
}

.studio-section-head h3 {
  min-width: 0;
  margin: 0;
  overflow: hidden;
  color: var(--text);
  font-size: 14px;
  line-height: 20px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.studio-section-head h3 small,
.studio-section-head > span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
}

.studio-icon-button {
  display: inline-grid;
  width: 30px;
  min-height: 30px;
  place-items: center;
  flex: none;
  border-color: var(--line);
  padding: 0;
  background: var(--surface-soft);
  color: var(--muted);
}

/* 剧集下拉 + 筛选，在分镜台标题行里（2026-09-15 起）。 */
.studio-episode-filter {
  display: flex;
  flex: none;
  align-items: center;
  gap: 6px;
}
.studio-episode-filter label { flex: none; color: var(--muted); font-size: var(--font-base); }
#videoView .studio-episode-filter select {
  flex: none;
  /* 两个下拉一样宽、钉死（用户 2026-09-22：「长度保持一致，不要太长，不要和后面的按钮重叠」）：
     全局给 select 的 width: 100% 在这儿按容器算出 210px 一个，第二个压在分隔线和三颗按钮上。
     先给了 100px，用户机器上「全部镜头」「生成失败」显示不全（「镜头状态的下拉框长点，现在字显示不全了」），放到 120；
     多季的「第 1 季 · 第 12 集」还是会被原生下拉截尾，少见，不为它加宽。 */
  width: 120px;
  min-width: 0;
  height: 28px;
  padding: 2px 26px 2px 8px;
  border-radius: 7px;
  background: var(--input-bg);
  color: var(--text);
  font-size: 12px;
}
/* 标题行全部靠左（用户 2026-09-15）；下拉和右边三颗按钮之间一道竖线，和按钮同高（28px）。 */
.studio-board-head { justify-content: flex-start; flex-wrap: wrap; gap: 8px 12px; }
.studio-board-head-divider { flex: none; width: 1px; height: 28px; background: var(--line-strong); }
.studio-board-head .studio-shot-manage { min-height: 28px; height: 28px; }
.studio-board-head .studio-play-all { height: 28px; min-height: 28px; margin-left: 0; }
.studio-board-head h3 small { margin-left: 6px; color: var(--muted); font-size: 12px; font-weight: 500; }
.studio-board-head #studioBoardSummary { color: var(--muted); font-size: 12px; }

.studio-shot-manage {
  min-height: 26px;
  padding: 2px 10px;
  color: var(--muted);
  font-size: var(--font-base);
  font-weight: 500;
}
.studio-shot-list-actions { display: flex; flex: none; align-items: center; gap: 6px; }
.studio-shot-manage.active { border-color: var(--green); color: var(--green); background: var(--green-soft); }
/* 首帧图到期被清理：镜头留着，缩略图那一格用提醒色写明白，和「首帧已更新」角标同一个颜色。 */
.studio-frame-expired { color: var(--warn, #d9a441); font-size: 11px; font-weight: 600; white-space: nowrap; }
.studio-shot-state { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; }
/* 实心圆 + 白勾：参考图那个样子，比描边的显眼。 */
.studio-shot-state-dot { width: 14px; height: 14px; flex: none; fill: currentColor; stroke: none; }
/* 勾的描边固定用成功色的浅档：原来选中后换成 --green-soft，那现在是半透明青，压在绿点上勾就没了（用户 2026-09-15 截图）。 */
.studio-shot-state-dot path { fill: none; stroke: var(--success-soft); stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.studio-shot-state.ready { color: var(--muted); }
.studio-shot-state.running { color: var(--blue); }
.studio-shot-state.complete { color: var(--success); }
.studio-shot-state.failed { color: var(--red); }
/* 贴在这一行的右上角。原来写的是 right: 118px，那是行里还有一列右侧信息时留下的
   数字——那一列早就没了，于是删除按钮孤零零地飘在行的中间偏右，看着不像是这一行的。
   -6px 让它压在边角上：列表左右各留了 10 / 14px 的内边距，压出去这点不会被裁掉。 */
.studio-shot-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  z-index: 1;
  display: grid;
  width: 22px;
  height: 22px;
  min-height: 0;
  place-items: center;
  padding: 0;
  /* 实心的红圆、白色的叉（全站那枚 .close-icon，用户 2026-09-15 定的），不再打「×」字符。
     **一直是正红**（用户：「这个关闭按钮要一直保持红色」）：--red 在深色主题是粉的 #ff9f9f，不能用它；
     悬浮 / 聚焦 / 按下都不换色，按下只按全局那条缩小。 */
  border: 0;
  border-radius: 50%;
  background: #e5484d;
  color: #fff;
  font-size: 15px;
  line-height: 1;
}
.studio-shot-remove .close-icon { width: 10px; height: 10px; }
.studio-shot-remove:hover,
.studio-shot-remove:focus-visible,
.studio-shot-remove:active { background: #e5484d; color: #fff; }

.studio-center {
  grid-area: stage;
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  min-width: 0;
  min-height: 0;
  gap: 10px;
}

.studio-resources {
  grid-column: 1;
  display: flex;
  min-width: 0;
  min-height: 0;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
.studio-resources .studio-resource-section { flex: 1 1 0; min-height: 0; height: auto; }
.studio-resources .studio-resource-strip { min-height: 0; }
.studio-preview-panel { grid-column: 2; }
.studio-board-panel { grid-area: board; }
.studio-inspector { grid-area: inspector; }

.studio-preview-panel,
.studio-board-panel { display: flex; min-height: 0; flex-direction: column; overflow: hidden; }
.studio-preview-head { flex: none; }
.studio-head-actions { display: flex; gap: 6px; }
.studio-preview {
  position: relative;
  display: grid;
  flex: 1 1 0;
  min-height: 0;
  margin: 0 12px 12px;
  /* 只有视频一格（2026-09-16 首帧那格和分隔条撤了） */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
  background: var(--preview-bg);
}
/* 浅色主题：--preview-bg 在浅色下是深灰绿（悬浮预览图要深底衬着），这个格子只露出圆角外那一圈，
   浅色下会是一道深色边（用户 2026-09-10）。这里改用面板底色。全屏播放那条（.studio-preview-stage:fullscreen）还是深底。 */
:root:not([data-theme="dark"]) .studio-preview { background: var(--surface); }
.studio-preview-pane {
  position: relative;
  display: grid;
  height: 100%;
  max-height: 100%;
  min-width: 0;
  min-height: 0;
  place-items: center;
  overflow: hidden;
  border-radius: 4px;
  background: var(--input-bg);
}
.studio-preview-pane img,
.studio-preview-pane video {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  min-width: 0;
  min-height: 0;
  object-fit: contain;
  border-radius: inherit;
  background: var(--input-bg);
}
.studio-preview-empty {
  display: grid;
  width: 100%;
  height: 100%;
  place-content: center;
  justify-items: center;
  gap: 8px;
  color: var(--muted);
  text-align: center;
}
.studio-preview-empty strong { color: var(--text); font-size: 14px; }
.studio-preview-empty span { max-width: 280px; font-size: 12px; }
/* 生成中的右格：转圈 + 阶段 + 已用时 */
.studio-preview-spinner { width: 26px; height: 26px; border-radius: 50%; border: 3px solid rgba(255, 255, 255, .15); border-top-color: var(--accent, var(--green)); animation: studio-spin 1s linear infinite; }
:root:not([data-theme="dark"]) .studio-preview-spinner { border-color: rgba(0, 0, 0, .1); border-top-color: var(--accent, var(--green)); }
@keyframes studio-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .studio-preview-spinner { animation: none; } }

.studio-player-controls {
  display: flex;
  min-height: 48px;
  flex: none;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-top: 1px solid var(--line);
  color: var(--text);
  font-size: 12px;
}
.studio-play-button { width: 32px; min-height: 32px; border: 0; padding: 0; background: transparent; }
.studio-player-controls > span { min-width: 86px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 画面区：全屏按钮压在右下角 */
.studio-preview-stage { position: relative; display: flex; flex: 1; min-height: 0; }
.studio-preview-stage > .studio-preview { flex: 1; min-width: 0; }
.studio-preview-stage:fullscreen { padding: 0; background: var(--preview-bg); }
/* 全屏挂在容器上（studio.js 的全屏按钮），而不是 <video> 上——连播切镜会重建 <video>，挂在它身上的全屏跟着没。
   有视频时黑底、铺满，看着和原生视频全屏一样。has-video 由 renderPreview 挂。 */
.studio-preview-stage:fullscreen.has-video > .studio-preview { margin: 0; background: #000; }
.studio-preview-stage:fullscreen.has-video .studio-video-preview { border-radius: 0; background: #000; }
.studio-preview-stage:fullscreen.has-video .studio-video-preview video { width: 100%; height: 100%; max-width: none; max-height: none; object-fit: contain; background: #000; }
/* 全屏：控制条最右边，排在倍速后面 */
.studio-fullscreen-button { flex: none; width: 32px; min-height: 28px; padding: 0; border: 0; background: transparent; color: var(--text); font-size: 14px; }
.studio-fullscreen-button:disabled { opacity: .35; }
/* 进度条：轨道 + 已播条 + 可拖的滑块。轨道留出上下的点击范围，细线只是画出来的。 */
.studio-progress { position: relative; flex: 1; height: 16px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
.studio-progress::before { content: ""; position: absolute; left: 0; right: 0; height: 4px; border-radius: 999px; background: var(--progress-track); }
.studio-progress-fill { position: absolute; left: 0; width: 0; height: 4px; border-radius: 999px; background: var(--progress-value); }
.studio-progress-thumb { position: absolute; left: 0; width: 12px; height: 12px; margin-left: -6px; border-radius: 50%; background: var(--progress-value); box-shadow: 0 1px 3px rgba(0, 0, 0, .4); pointer-events: none; }
.studio-progress.disabled { cursor: default; opacity: .5; }
.studio-progress:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 999px; }
/* 音量、倍速：进度条右边两颗小按钮，鼠标停上去朝上弹调节框 */
.studio-popover-host { position: relative; display: flex; flex: none; align-items: center; }
.studio-popover {
  position: absolute; left: 50%; bottom: calc(100% + 8px); z-index: 6;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 8px; border-radius: 10px; background: var(--surface-alt);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
  transform: translateX(-50%) translateY(4px);
  opacity: 0; visibility: hidden; transition: opacity .12s ease, transform .12s ease, visibility .12s;
}
/* 下边留一条透明的桥：鼠标从按钮挪到调节框上不会掉 */
.studio-popover::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 8px; }
.studio-popover-host:hover .studio-popover,
.studio-popover-host:focus-within .studio-popover { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.studio-popover-host:has(button:disabled) .studio-popover { display: none; }
.studio-volume-button, .studio-speed-button { width: 32px; min-height: 28px; padding: 0; border: 0; background: transparent; color: var(--text); font-size: 13px; }
.studio-volume-slider {
  /* 竖着放：现代浏览器认 writing-mode，旧的走 -webkit-appearance */
  writing-mode: vertical-lr; direction: rtl;
  -webkit-appearance: slider-vertical;
  width: 20px; height: 96px; min-height: 0; margin: 0; padding: 0;
  accent-color: var(--progress-value); background: transparent; cursor: pointer;
}
.studio-speed-option { min-width: 56px; min-height: 26px; padding: 2px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); font-size: 12px; cursor: pointer; }
.studio-speed-option.active { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }
/* 成片版本：和倍速同一套弹框；按钮字比「1×」长，宽度放开 */
.studio-take-button { width: auto; min-height: 28px; padding: 0 6px; border: 0; background: transparent; color: var(--text); font-size: 12px; white-space: nowrap; }
.studio-take-popover { align-items: stretch; min-width: 168px; }
/* 只有一条（或没有）成片时整格藏起：上面 .studio-popover-host 写了 display: flex，会盖过 hidden 的默认样式，
   只有一条成片的镜头上原来照样露出「版本 1/1」，点开是一块很矮的空面板（2026-10-04 用户报的） */
.studio-take-host[hidden] { display: none; }
.studio-take-option { display: flex; justify-content: space-between; gap: 12px; min-height: 26px; padding: 2px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); font-size: 12px; cursor: pointer; text-align: left; }
.studio-take-option.active { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }
.studio-take-option small { color: inherit; opacity: .8; font-size: 11px; }
.studio-icon-button.active { color: var(--green); background: var(--green-soft); }

/* 只横着滚：竖向明确关掉，不然 overflow-x: auto 会把 overflow-y 也算成 auto，卡片高一点就多一条竖滚动条。 */
/* 卡片从顶上排、万一还是放不下就竖着滚：原来 align-items: center + overflow-y: hidden，卡片一比分镜台高就两头各裁一截，
   编号那行被遮住（用户 2026-09-15 截图）。 */
.studio-board { display: flex; flex: 1; min-height: 0; align-items: flex-start; gap: 0; overflow-x: auto; overflow-y: auto; padding: 8px 12px; }
/* 分镜台是横排的 flex，空态那一块不撑开就缩成一小截贴在左边；撑满整行，文字才居中（用户 2026-09-22：「没有符合筛选的镜头 水平居中」）。 */
.studio-board > .studio-empty { flex: 1 1 100%; width: 100%; min-height: 0; align-self: stretch; }
.studio-board-item {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  gap: 12px;
}
/* 卡片 = 按钮（选中 / 拖动）+ 脚下一行：「首帧 | 直出」滑轨，管理模式换成 ×（贴卡片右上角）。 */
/* 整张卡一块底色（参考图里滑轨和两颗按钮都在卡里面）：卡片底色和按钮同一档，选中 / 悬浮时整张卡跟着变色。 */
.studio-board-card { position: relative; display: grid; gap: 6px; justify-items: stretch; border-radius: 12px; background: var(--surface-soft); opacity: 1; }
.studio-board-card:has(> .studio-board-shot:hover:not(:disabled)) { background: var(--command-neutral-bg); }
#videoView .studio-board-card:has(> .studio-board-shot.selected) { background: color-mix(in srgb, var(--green) 14%, var(--surface-soft)); }
/* 底色只由卡的壳画，里面那颗按钮一律透明：原来按钮自己也铺一层半透明青，和壳叠在一起，
   按钮那一段比脚下滑轨那一段深一截，卡底出一道色差（用户 2026-09-15 截图）。 */
#videoView .studio-board-card > .studio-board-shot,
#videoView .studio-board-card > .studio-board-shot:hover:not(:disabled),
#videoView .studio-board-card > .studio-board-shot.selected,
#videoView .studio-board-card > .studio-board-shot.selected:hover:not(:disabled) { background: transparent; }
#videoView .studio-board-lift:has(> .studio-board-shot.selected) { background: color-mix(in srgb, var(--green) 14%, var(--surface-soft)); }
.studio-board-card > .studio-board-shot { border-radius: 12px; }
/* 卡片右上角的「…」（用户 2026-09-20 的参考图）：出片方式「首帧 / 直出」收在它点开的浮层里，原来是卡片脚下一条滑轨。
   卡片本身是一颗 button，按钮不能套按钮，所以它在卡片按钮外面、绝对定位压在头一行的右端；头一行右边给它留出位置。 */
/* 2026-09-24 起这颗写当前的「首帧 / 直出」加下箭头（原来是三个点的圆钮），药丸形，字号和状态药丸同一档（11px）。
   **宽度是量出来的**：9:16 的项目卡片都是最窄的那一档，头一行最宽是「分镜 12 ｜ 15s ｜ ✓ 已生成」（实测 160），头一行的间隔因此从 10 收到 6。
   2026-10-09 出片方式改叫「首帧生成 / 直接生成」（用户定的，全站统一），按钮从 46 宽到 69：头一行右边留 80（按钮 + 离状态药丸 11），
   卡片最窄从 236 放到 268（12 + 160 + 4 的余量 + 80 + 12；不留余量的话别人机器上字宽一点就压到状态药丸上）。按钮再宽要一起放。 */
.studio-board-more {
  position: absolute; top: 9px; right: 12px; z-index: 2;
  display: inline-flex; height: 28px; min-height: 0; align-items: center; gap: 2px; padding: 0 5px 0 8px;
  border: 0; border-radius: 999px; background: var(--surface); color: var(--text);
  font: inherit; font-size: 11px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.studio-board-more-chevron { display: inline-flex; color: var(--muted); transition: transform .15s ease; }
.studio-board-more-chevron svg { width: 10px; height: 10px; }
.studio-board-more[aria-expanded="true"] .studio-board-more-chevron { transform: rotate(180deg); }
.studio-board-more:disabled { opacity: .48; cursor: not-allowed; }
.studio-board-card > .studio-board-shot .studio-board-head-row { padding-right: 80px; }
/* 浮层钉成 fixed（位置由 placeBoardMoreMenu 按「…」摆）：分镜台是横向滚动的一条，absolute 的浮层会被它裁掉。 */
.studio-board-more-menu {
  position: fixed; z-index: 40; display: grid; gap: 2px; min-width: 124px; padding: 6px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .28);
}
.studio-board-more-option {
  display: grid; grid-template-columns: 22px 1fr 16px; align-items: center; column-gap: 10px;
  min-height: 0; padding: 8px 10px; border: 0; border-radius: 8px; background: transparent;
  color: var(--text); font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.studio-board-more-option:hover { background: var(--tab-rail-strong); }
.studio-board-more-icon { display: grid; width: 22px; height: 22px; place-items: center; }
.studio-board-more-icon svg { width: 20px; height: 20px; }
.studio-board-more-check { display: grid; width: 16px; height: 16px; place-items: center; }
.studio-board-more-check svg { width: 16px; height: 16px; }
/* 管理模式的 ×：压在卡的右上角，滑轨照旧在，版式一点不动。 */
.studio-board-card > .studio-shot-remove { z-index: 2; }
/* 图角上的「查看大图」（用户 2026-09-15）：压在图格右下角的一颗半透明圆钮，悬浮到卡上才显出来。 */
.studio-board-view {
  position: absolute;
  /* top / right 由 placeBoardViewButtons 按图格的位置量出来写上 */
  z-index: 2;
  display: grid;
  width: 28px;
  height: 28px;
  min-height: 0;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  opacity: 0;
  transition: opacity .12s ease;
}
.studio-board-view svg { width: 16px; height: 16px; }
.studio-board-card:hover .studio-board-view,
.studio-board-view:focus-visible { opacity: 1; }
.studio-board-view:hover { background: rgba(0, 0, 0, .55); color: #fff; }
.studio-board-shot .studio-shot-state { font-size: 11px; }
.studio-board-index .studio-shot-badge { margin-left: auto; }
.studio-board-shot {
  position: relative;
  display: grid;
  /* 卡宽跟着封面比例走：图片高度由分镜台可用高度统一约束，宽度按封面比例计算；
     至少留出头部状态行和标题的空间，过宽的卡片交给分镜台横向滚动。 */
  width: max(268px, calc(var(--studio-board-cover-width, var(--studio-board-width, 184px)) + 24px));
  min-width: max(268px, calc(var(--studio-board-cover-width, var(--studio-board-width, 184px)) + 24px));
  min-height: 0;
  align-content: start;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  border: 0;
  padding: 12px;
  border-radius: 14px;
  background: var(--surface-soft);
  color: var(--text);
  text-align: left;
  cursor: grab;
  user-select: none;
}
.studio-board-shot:active { cursor: grabbing; }
.studio-board-shot:hover:not(:disabled) { border: 0; background: var(--command-neutral-bg); }
.studio-board-image {
  display: grid;
  grid-column: 1 / -1;
  justify-self: center;
  /* 图片格按实际封面比例取宽度；卡片太窄时由卡片最小宽度留下两侧空间。 */
  width: min(100%, var(--studio-board-cover-width, var(--studio-board-width, 184px)));
  height: var(--studio-board-height, 103.5px);
  place-items: center;
  overflow: hidden;
  /* 图片格保持封面比例，图片完整放入，不变形、不裁切。 */
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  /* 不能和卡片同色（--surface-soft）：「无首帧」那格会和卡融成一片（用户 2026-09-15）。滑轨槽那一档，两套主题都和卡分得开。 */
  background: var(--tab-rail-strong);
}
/* 卡片版式照用户 2026-09-15 给的参考图：「分镜 01 · 时长 5s」一行、状态药丸在右上角、大图、标题、脚下滑轨。不放别的按钮。 */
/* 头一行「分镜 02 ｜ 时长 15s ｜ ✓ 已生成」，下面一道分隔线（参考图） */
.studio-board-head-row { display: flex; grid-column: 1 / -1; align-items: center; gap: 6px; min-height: 24px; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.studio-board-no { display: inline-flex; flex: none; align-items: center; gap: 4px; color: var(--text); font-size: 14px; font-weight: 700; white-space: nowrap; }
.studio-board-no b { font-size: 16px; }
.studio-board-no::after { width: 1px; height: 14px; margin-left: 6px; background: var(--line-strong); content: ""; }
.studio-board-duration { flex: none; color: var(--muted); font-size: 13px; white-space: nowrap; }
.studio-board-pill { display: inline-flex; flex: none; align-items: center; gap: 4px; margin-left: auto; padding: 2px 8px; border-radius: 999px; background: var(--surface); color: var(--muted); font-size: 11px; font-weight: 600; white-space: nowrap; }
.studio-board-pill.running { color: var(--blue); }
.studio-board-pill.complete { color: var(--success); background: var(--success-soft); }
.studio-board-pill.failed { color: var(--red); }
.studio-board-pill .studio-shot-state-dot { width: 13px; height: 13px; }
/* 标题左边一道绿色竖条（参考图） */
.studio-board-title { grid-column: 1 / -1; overflow: hidden; color: var(--text); font-size: 14px; font-weight: 700; line-height: 1.3; text-align: center; text-overflow: ellipsis; white-space: nowrap; }
.studio-board-placeholder { display: grid; justify-items: center; gap: 6px; color: var(--muted); font-size: 12px; }
.studio-board-placeholder b { font-weight: 500; }
.studio-board-placeholder-icon { width: 36px; height: 36px; opacity: .55; }
.studio-board-placeholder.is-expired { color: var(--warn, #d9a441); }
.studio-board-shot.selected .studio-board-image { border-color: var(--green); box-shadow: inset 0 0 0 1px var(--green); }
/* 图整张放进格里、居中，不裁：图片和视频封面都保留自身比例。 */
.studio-board-image img { width: 100%; height: 100%; object-fit: contain; }
.studio-board-image .studio-board-video-cover { width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
.studio-board-image .studio-board-image-cover { width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
/* 箭头用图片而不是「→」这个字符：字符的粗细和字体绑死，换字体就变样。
   走 mask 而不是 <img>：给的那张图是纯白的，直接贴在浅色主题上会看不见；
   当遮罩用就跟着 var(--muted) 走，深浅两套主题都对。 */
/* 分镜台标题右边那颗「播放全部」。跟着标题走，不抢合计那一段的位置。 */
.studio-play-all {
  flex: none;
  height: 26px;
  min-height: 26px;
  margin-left: 10px;
  padding: 0 12px;
  border-radius: 999px;
  font-size: 12px;
}
.studio-play-all.playing { color: var(--accent-text); }

.studio-board-arrow {
  width: 24px;
  height: 21px;
  flex: none;
  background: var(--muted);
  -webkit-mask: url("/static/icon-arrow-right.png") center / contain no-repeat;
  mask: url("/static/icon-arrow-right.png") center / contain no-repeat;
}
.studio-board-item:not(:last-child) { margin-right: 12px; }
/* 拖动排序照 iPhone 挪图标那套：其他格子实时平移让位，不是在目标边上画一条线。
   这条过渡就是「让位」的动画；拖起来的那一帧 JS 会临时把它关掉，
   免得让位是从别处飞过来而不是从当前位置动。 */
.studio-board-item .studio-board-card { transition: transform .22s cubic-bezier(.22, .61, .36, 1); }
/* 被拖走的那格只留一个看不见的盒子——它就是缺口，跟着一起平移。
   不能用 display:none 或者把它摘出去：那样这一行会当场塌掉一格宽。 */
/* 只把整张卡（底色 + 按钮 + 滑轨）藏起来，item 的盒子和里面那个箭头都留着：盒子是缺口，箭头不该跟着走。 */
.studio-board-item.board-dragging .studio-board-card {
  /* 占位仍保留尺寸，但不要用 opacity 制造半透明合成层，避免边缘拖动时留下残影。 */
  visibility: hidden;
  opacity: 1;
}
.studio-board.board-dragging-active { cursor: grabbing; user-select: none; }
/* 触屏上横着一滑交给浏览器滚（用户 2026-10-03：「手机端的分镜台，想滑动的时候经常触发分镜卡片的拖拽」——原来这里是 touch-action: none，
   卡片铺满了条带，手指一动就是拖、根本滑不动）。拖动改成按住一会儿才拿起来（Board.tsx 的 BOARD_HOLD_MS），拿起来之后由脚本拦住滚动。
   长按时 iOS 别弹「存储图像」那份菜单、别选中字。图片也要禁掉浏览器的原生拖拽——从图上按下去拖，
   浏览器会接管这个手势去拖那张图，pointerup 永远等不到，之后就再也拖不动。 */
.studio-board-item { touch-action: pan-x pan-y; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.studio-board-shot img { -webkit-user-drag: none; user-select: none; }
.studio-board.board-dragging-active .studio-board-shot { cursor: grabbing; }
/* 跟着手走的那一份。固定定位，所以条带自动滚动时它不会跟着跑偏。 */
.studio-board-lift {
  position: fixed;
  z-index: 120;
  margin: 0;
  border-radius: 14px;
  box-shadow: 0 18px 42px rgba(0, 0, 0, .48);
  opacity: 1;
  pointer-events: none;
  transform: scale(1.05);
  transform-origin: center;
  will-change: transform;
  backface-visibility: hidden;
}
.studio-board-lift.settling { transition: transform .2s cubic-bezier(.22, .61, .36, 1); }
.studio-inspector { display: flex; min-width: 0; min-height: 0; flex-direction: column; overflow: hidden; }
.studio-inspector-content { display: flex; flex: 1; min-height: 0; flex-direction: column; overflow-y: auto; padding-right: 2px; scrollbar-width: thin; }
.studio-inspector-content > * + * { margin-top: 10px; }
/* 角色 / 道具 / 场景三个区块同高，卡片都是 16:9 的横图（角色设定图本来就是横幅多视图表）。 */
.studio-resource-section { height: 142px; min-height: 142px; overflow: hidden; }
.studio-resource-strip { display: flex; gap: 8px; overflow-x: auto; padding: 0 12px 12px; scrollbar-width: thin; }
.studio-resource-card {
  display: grid;
  width: 72px;
  min-width: 72px;
  min-height: 0;
  gap: 5px;
  justify-items: stretch;
  border: 0;
  padding: 0;
  background: transparent;
}
.studio-resource-card:hover:not(:disabled) { border: 0; background: transparent; }
/* 有图的卡片点了开查看器选参考图版本（2026-09-22，悬浮预览撤了）；图格右下角一枚小角标写钉的是哪一版。 */
.studio-resource-card.has-image { cursor: pointer; }
.studio-resource-card.has-image:focus-visible { outline: none; }
.studio-resource-card.has-image:focus-visible > span { outline: 2px solid var(--accent); outline-offset: 1px; }
.studio-resource-card > span {
  position: relative;
  display: grid;
  width: 72px;
  aspect-ratio: 1 / 1;
  place-items: center;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-soft);
  color: var(--muted);
  font-size: 20px;
}
/* 卡片右上角「×」移除这一项（2026-10-11）：卡片整张可点（开查看器），「×」不能放进去——外面包一层 .studio-resource-item 当定位框，
   「×」压在图格右上角，触屏上也一直看得见；那一层按卡片宽收（格子比卡片宽时「×」不会飘到卡片外面）。 */
.studio-resource-item { position: relative; flex: none; justify-self: start; min-width: 0; }
#mainView #videoView .studio-resource-remove {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 1;
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  cursor: pointer;
}
.studio-resource-remove::before {
  content: "";
  position: absolute;
  inset: 0;
  width: 8px;
  height: 8px;
  margin: auto;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 2l6 6M8 2 2 8' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 2l6 6M8 2 2 8' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
#mainView #videoView .studio-resource-remove:hover:not(:disabled) { background: rgba(0, 0, 0, .78); }
#mainView #videoView .studio-resource-remove:disabled { cursor: not-allowed; opacity: .45; }
/* 每一块标题上的「恢复默认」：这一类移除过、钉过版本才有，挨在计数左边 */
#mainView #videoView .studio-reference-restore {
  min-height: 0;
  margin-left: auto;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  line-height: 20px;
  cursor: pointer;
}
#mainView #videoView .studio-reference-restore:hover:not(:disabled) { color: var(--text); }
#mainView #videoView .studio-reference-restore:disabled { cursor: not-allowed; opacity: .45; }
.studio-resource-pick {
  position: absolute;
  right: 4px;
  bottom: 4px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .62);
  color: #fff;
  font-size: 10.5px;
  font-style: normal;
  font-weight: 600;
  line-height: 1.4;
  pointer-events: none;
}
.studio-role-section .studio-resource-card,
.studio-role-section .studio-resource-card > span,
.studio-prop-section .studio-resource-card,
.studio-prop-section .studio-resource-card > span,
.studio-scene-section .studio-resource-card,
.studio-scene-section .studio-resource-card > span {
  width: 112px;
  min-width: 112px;
}
.studio-role-section .studio-resource-card > span,
.studio-prop-section .studio-resource-card > span,
.studio-scene-section .studio-resource-card > span { aspect-ratio: 16 / 9; }
/* 角色 / 场景 / 道具按行排满、竖着滚（用户 2026-10-08：「滚动方向改为竖向滚动」；10-05 要过各排一行横着滚，大屏上一行卡片底下空着一大截）。
   一行放得下几张放几张（左栏现在各档都是两张），多的往下排；区块排成一列，条占标题下面剩下的高度、在里面滚——
   9-18 到 10-05 那版两列竖排没给条限高，条按内容长高、多出来的被区块裁掉，滚不动。这三条不挂「滚轮竖着滚就横着走」了，分镜台照旧挂。 */
.studio-role-section, .studio-scene-section, .studio-prop-section { display: flex; flex-direction: column; }
.studio-resources .studio-resource-strip {
  display: grid;
  flex: 1 1 auto;
  grid-template-columns: repeat(auto-fill, 112px);
  grid-auto-rows: max-content;
  align-content: start;
  align-items: start;
  overflow-x: hidden;
  overflow-y: auto;
}
/* 出镜资产卡片按固定列宽排布，避免随左侧栏宽度拉伸。 */
.studio-center .studio-resources .studio-resource-card,
.studio-center .studio-resources .studio-resource-card > span {
  width: 112px;
  min-width: 112px;
}
.studio-resource-card > span img { width: 100%; height: 100%; object-fit: cover; }
/* 3D 导演台（白模预演，2026-09-26）：左栏第四格，不和上面三格平分；只在桌面端显示。
   **高度固定**（用户 2026-09-27：切到 2.0、有没有白模视频都会让高度跳变）：没录过是撑满的一颗按钮，录过是缩略图卡片 + 底下一行说明，
   说明只占一行（放不下省略、悬停看全文），没有说明那一行空着——这一格一变高，上面三格跟着重新分，整栏都跳。 */
.studio-resources .studio-whitebox-section { flex: none; height: auto; min-height: 0; }
.studio-whitebox { height: 90px; display: flex; flex-direction: column; gap: 6px; padding: 0 12px 12px; }
.studio-whitebox-open {
  display: flex;
  flex: 1;
  width: 100%;
  min-height: 0;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px dashed var(--line-strong);
  border-radius: 8px;
  background: var(--surface-soft);
  color: var(--text);
  text-align: left;
  white-space: normal;
}
.studio-whitebox-open:hover:not(:disabled) { border-color: var(--line-strong); background: var(--surface-soft); }
.studio-whitebox-icon {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--accent-soft);
  position: relative;
}
.studio-whitebox-icon::after {
  content: "";
  position: absolute;
  inset: 8px;
  border: 2px solid var(--accent);
  border-radius: 3px;
  transform: rotate(45deg) scale(.8);
}
.studio-whitebox-open-text { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.studio-whitebox-open-text small { color: var(--muted); font-size: 12px; }
.studio-whitebox-card { flex: none; display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 10px; align-items: center; }
.studio-whitebox-thumb {
  position: relative;
  width: 112px;
  aspect-ratio: 16 / 9;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-soft);
}
.studio-whitebox-thumb video { display: block; width: 100%; height: 100%; object-fit: cover; }
.studio-whitebox-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 18px;
  text-shadow: 0 1px 4px rgba(0, 0, 0, .6);
  pointer-events: none;
}
.studio-whitebox-thumb.playing .studio-whitebox-play { display: none; }
.studio-whitebox-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.studio-whitebox-actions button { min-height: 28px; padding: 0 10px; }
.studio-whitebox-note { flex: none; margin: 0; overflow: hidden; color: var(--muted); font-size: 12px; line-height: 1.5; white-space: nowrap; text-overflow: ellipsis; }
/* 导演台的全屏层：fixed 不占 body 的格子；盖住横幅和侧栏。 */
.director-overlay { position: fixed; inset: 0; z-index: 200; background: var(--bg); }
.director-overlay iframe { display: block; width: 100%; height: 100%; border: 0; }
body.director-open { overflow: hidden; }
.studio-resource-pending {
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  text-align: center;
}
#videoView .studio-resource-card > span.pending {
  border: 1px dashed var(--line-strong);
  background: var(--input-bg);
}
.studio-resource-card > strong { min-width: 0; overflow: hidden; color: var(--text); font-size: 10px; font-weight: 600; line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; }
.studio-resource-card > strong small { color: var(--muted); font-size: 10px; }
.studio-copy-section { flex: none; }
/* 台词与镜头描述的高度对调：台词收回一档，镜头描述给更大的最低编辑空间。 */
.studio-dialogue-editor-wrap { display: flex; height: 142px; min-height: 142px; max-height: 142px; margin: 0 12px 12px; }
.studio-dialogue-editor { width: 100%; }
.studio-description-section { display: flex; flex: 1 0 165px; min-height: 165px; flex-direction: column; }
.studio-description-editor-wrap { position: relative; display: flex; flex: 1; min-height: 0; margin: 0 12px 12px; }
.studio-description-editor {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  border: 0;
  border-radius: 4px;
  padding: 8px 9px;
  background: var(--input-bg);
  color: var(--text);
  font-size: 13px;
  line-height: 1.9;
  outline: none;
  caret-color: var(--text);
  cursor: text;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.studio-description-editor:focus { outline: none; }
.studio-description-editor:empty::before { content: attr(data-placeholder); color: var(--muted); pointer-events: none; }
.studio-description-editor[aria-disabled="true"] { cursor: default; opacity: .65; }
.studio-mention {
  display: inline-flex;
  max-width: 100%;
  height: 20px;
  align-items: center;
  gap: 2px;
  margin: 0 1px;
  padding: 0 4px 0 1px;
  border: 1px solid currentColor;
  border-radius: 6px;
  vertical-align: text-bottom;
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  line-height: 16px;
  white-space: nowrap;
}
.studio-mention > span:last-child,
.studio-mention-name { font: inherit; }
.studio-mention-role { border-color: var(--blue); background: var(--blue-soft); color: var(--blue); }
.studio-mention-prop { border-color: var(--orange); background: var(--orange-soft); color: var(--orange); }
.studio-mention-scene { border-color: var(--green); background: var(--green-soft); color: var(--green); }
.studio-mention-avatar,
.studio-mention-option-avatar {
  display: grid;
  place-items: center;
  flex: none;
  overflow: hidden;
  border-radius: 50%;
  background: var(--surface);
  color: var(--text);
  font-size: 10px;
}
.studio-mention-avatar { width: 15px; height: 15px; }
.studio-mention-avatar img,
.studio-mention-option-avatar img { width: 100%; height: 100%; object-fit: cover; }
.studio-mention-menu {
  position: fixed;
  z-index: 120;
  display: flex;
  flex-direction: column;
  width: min(280px, calc(100vw - 16px));
  max-height: 220px;
  overflow: hidden;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--surface-elevated, var(--surface));
  box-shadow: 0 10px 28px rgba(0, 0, 0, .24);
}
.studio-mention-tabs {
  display: flex;
  flex: none;
  padding: 5px 5px 0;
  border-bottom: 1px solid var(--line);
}
.studio-mention-tab {
  flex: 1;
  min-height: 30px;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
}
.studio-mention-tab:hover { border-color: transparent; background: var(--surface-hover); color: var(--text); }
.studio-mention-tab.active { border-bottom-color: var(--green); color: var(--text); }
.studio-mention-options {
  display: grid;
  min-height: 48px;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  overflow-y: auto;
  padding: 5px;
}
.studio-mention-option {
  display: grid;
  width: 100%;
  min-width: 0;
  grid-template-columns: 30px minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  min-height: 42px;
  padding: 5px 8px;
  border-color: transparent;
  background: transparent;
  color: var(--text);
  text-align: left;
}
.studio-mention-option.active { border-color: var(--line); background: var(--surface-hover); }
button.studio-mention-option:hover:not(.active) { border-color: transparent; background: transparent; }
button.studio-mention-option.active:hover { border-color: var(--line); background: var(--surface-hover); }
.studio-mention-option-avatar { width: 28px; height: 28px; }
.studio-mention-option > span:last-child { display: grid; min-width: 0; }
.studio-mention-option small { color: var(--muted); font-size: 11px; }
.studio-mention-option strong { overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.studio-mention-empty { padding: 14px 8px; color: var(--muted); font-size: 12px; text-align: center; }
/* 自动保存状态：已保存 / 保存中 / 未保存，贴在标题行右边（旁边的生成状态签 2026-10-09 撤了） */
.studio-save-state { margin-left: auto; font-size: 12px; color: var(--muted); }
.studio-save-state.saving { color: var(--blue); }
.studio-save-state.dirty { color: var(--muted); }
.studio-save-state.saved { color: var(--muted); opacity: .8; }

.studio-actions {
  display: grid;
  flex: none;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  padding: 10px 0 0;
  background: var(--bg);
}
.studio-actions button { min-width: 0; }
/* 「生成镜头描述」宽度写死（用户 2026-09-12：三种文案 生成镜头描述 / 生成中... / 重生镜头描述 换来换去按钮不能动），
   数值在 .video-studio 的 --studio-prompt-width；它正上方的清晰度下拉用同一个数，两颗对齐。右边那颗吃掉剩下的。 */
.studio-action-row { display: grid; grid-template-columns: var(--studio-prompt-width) minmax(0, 1fr); gap: 6px; }
/* 生成选项两行：第一行 供应商 / 模型，第二行 清晰度 / 比例 / 时长 / 更多设置。
   两行各是自己的网格：列数不同，硬塞进一个网格上下也对不齐。第二行前三格以内容宽为下限、
   余量平分；「更多设置」按内容定宽。不常动的两项收进「更多设置」小面板。 */
.studio-generate-options { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; min-width: 0; }
.studio-generate-row { display: grid; align-items: center; gap: 6px; min-width: 0; }
/* 供应商那格原来 1fr，「火山引擎中转」这种名字挤得快看不全，放宽到 1.25fr（用户说稍微长一点）。 */
.studio-generate-row-model { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1.4fr); }
/* 清晰度那格和下面「生成镜头描述」同宽（--studio-prompt-width）；比例、时长两格平分剩下的，比原来三格平分时窄一截
   （用户 2026-09-12：「比例和秒数调整的下拉框宽度一起小一点」）。 */
.studio-generate-row-params { grid-template-columns: var(--studio-prompt-width) minmax(max-content, 1fr) minmax(max-content, 1fr) max-content; }
/* 原生下拉的箭头区要吃掉二十多像素，最窄的那格里字就剩十几像素——箭头自己画（8px 的小勾），右边只留 16。 */
#mainView .studio-generate-options select, .studio-generate-options select {
  width: 100%; min-width: 0; min-height: 32px; margin: 0; padding: 4px 16px 4px 8px;
  border-color: transparent; background-color: var(--input-bg); color: var(--text); font-size: var(--font-base);
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23b3b3b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 6px center; background-size: 8px 5px;
}
/* 面板里的下拉不用省：字号和内边距回到正常。 */
.studio-more-panel select { padding: 4px 26px 4px 10px; font-size: var(--font-base); background-position: right 10px center; }
/* 「更多设置」那颗：和旁边的下拉同一身（同高、同底色），悬浮不换色，只按下缩小。 */
.studio-more-host { position: relative; display: flex; align-items: center; min-width: 0; }
#mainView .studio-more-button, .studio-more-button {
  min-height: 32px; padding: 4px 12px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: var(--input-bg); color: var(--text); font-size: var(--font-base); white-space: nowrap;
  box-shadow: none; transform: scale(1); transition: transform .08s ease;
}
#mainView .studio-more-button:hover:not(:disabled), .studio-more-button:hover:not(:disabled),
#mainView .studio-more-button.active, .studio-more-button.active {
  border-color: transparent; background: var(--input-bg); color: var(--text); box-shadow: none;
}
#mainView .studio-more-button:active:not(:disabled), .studio-more-button:active:not(:disabled) { translate: 0 1px; scale: .98; }
/* 小面板往上弹、右边和按钮对齐：这一排贴着页面底边，往下没地方。
   **用 fixed 不用 absolute**：右栏是 overflow hidden，absolute 的面板会被裁掉上面一截。
   位置由 studio.js 的 placeMore 按按钮的位置现算。 */
.studio-more-panel {
  position: fixed; left: 0; top: 0; z-index: 6;
  display: grid; gap: 10px; width: 272px; padding: 12px;
  border-radius: 10px; background: var(--surface-alt);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
.studio-more-panel.hidden { display: none; }
/* 面板里的项：左边一个短标签，右边控件。一行里的下拉不挂标签，面板里得挂——
   收起来的三项没有上下文，光一个「跟随首帧」看不出是什么。 */
.studio-more-field {
  display: grid; grid-template-columns: 60px minmax(0, 1fr); align-items: center; gap: 8px;
  margin: 0; color: var(--muted); font-size: var(--font-base);
}
.studio-more-field > span { white-space: nowrap; }
.studio-generate-options select:disabled { opacity: .7; }
/* 焦点高亮画在框内：右栏是 overflow hidden，下拉贴着右边缘，外侧的 outline 会被裁掉一截 */
.studio-generate-options select:focus { outline-offset: -2px; }
.studio-actions #studioGenerate { display: flex; align-items: center; justify-content: center; gap: 6px; }
.studio-actions #studioGenerate .cost-chip { flex: none; margin-left: 2px; }
.studio-actions .primary-command { border-color: var(--primary-bg); background: var(--primary-bg); }
.studio-actions .primary-command:hover:not(:disabled) { border-color: var(--primary-hover); background: var(--primary-hover); }

.studio-mobile-tabs { display: none; }
.studio-empty { display: flex; align-items: center; justify-content: center; min-height: 240px; height: 100%; padding: 30px 12px; color: var(--muted); text-align: center; }
.image-failed { background: var(--surface-soft); color: transparent; }

/* ==================== 工作台色块层级 ====================
   三个制作页用背景明度区分区域；交互项靠整块悬浮色反馈，不再常驻描边。 */
.breakdown-col-episodes { background: var(--surface-alt); }
.breakdown-col-detail { background: var(--surface); }
#imageView > .control-band { border-bottom: 0; background: var(--surface-alt); }
#imageView > .image-type-bar { border-bottom: 0; background: var(--surface-soft); }
#imageView > .task-section { background: var(--surface); }
#imageView .pagination-bar { border: 0; }

.studio-board-panel { background: var(--surface-alt); }
.studio-preview-panel { background: var(--surface); }
.studio-inspector .studio-panel { background: var(--surface); }
.studio-inspector .studio-panel:nth-child(even) { background: var(--surface-alt); }
.studio-player-controls { border-top: 0; background: var(--surface-alt); }

/* .assistant-option 也排除在外：它是选择题的单选项，选中态就是那圈描边，
   被这条 (1,4,1) 的规则刷成 transparent 的话勾了等于没勾。 */
#mainView button:not(.primary-command):not(.danger-command):not(.studio-shot-remove):not(.reference-slot):not(.assistant-option),
#mainView select,
#mainView input:not([type="checkbox"]),
#mainView textarea {
  border-color: transparent;
}
/* 工作台里的按钮悬浮不换色（原来这里有一条 8 个 :not() 堆出来的高优先级 hover 规则，
   把所有按钮悬浮时都刷成 surface-hover，连 .script-settings-foot 的专用规则都压不过它）。 */

.stat-tile,
.episode-item,
.episode-add,
.asset-subtab,
.studio-board-image,
.studio-resource-card > span {
  border: 0;
}
.stat-tile,
.episode-item,
.episode-add { background: var(--surface-soft); }
.stat-tile:hover,
.episode-item:hover,
.episode-add:hover:not(:disabled),
.studio-board-shot:hover:not(:disabled),
.studio-resource-card:hover {
  border-color: transparent;
  background: var(--surface-hover);
}
.stat-tile.active,
.episode-item.active {
  border-color: transparent;
  background: var(--green-soft);
  box-shadow: none;
}
.asset-subtab { background: transparent; }
.asset-subtab:hover { border-color: transparent; background: var(--surface-hover); }
.asset-subtab.active { border-color: transparent; background: var(--green-soft); }
.studio-board-shot,
.studio-resource-card { border-radius: 6px; }
.studio-board-shot.selected .studio-board-image { border-color: transparent; box-shadow: none; background: var(--tab-rail-strong); }
#videoView .studio-board-shot:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-neutral-bg);
  box-shadow: none;
}
#videoView .studio-board-shot.selected,
#videoView .studio-board-shot.selected:hover:not(:disabled) {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
.studio-resource-card:hover > span { background: var(--surface-hover); }
.studio-shot-manage,
.studio-icon-button,
.studio-shot-manage.active,
.studio-icon-button.active { border-color: transparent; }

#taskTable th,
#taskTable td,
.shot-table th,
.shot-table td { border-bottom: 0; }

/* ---- 每行一块独立的色块 ----
   样板是剧本创作的剧集列表（.script-episode-row）：一行一块圆角底色，行与行之间留空。
   表格做不到直接给 tr 加圆角和背景，只能把底色画在每个 td 上，再让首尾两格切圆角；
   行距靠 border-spacing 撑开，所以 border-collapse 必须从 collapse 换成 separate。
   隔行斑马纹一并撤掉——每行都有自己的块之后，再深一档的隔行色只会看着脏。 */
#taskTable, .shot-table { border-collapse: separate; border-spacing: 0 6px; }
/* 图片生成那张表的行是一块块圆角色块，表格铺满容器就贴着容器左右边（用户 2026-09-12：「单条记录和背景
   是没有左右边距的，很奇怪」）。给滚动容器留左右内边距；上边距和左边一样 16px（用户：「上边距要和左边距保持一致」）。
   表头（状态 / 类型 / 任务那一栏）要钉死不动：
   - border-spacing 在表头上方也留了 6px，表头静止时在 16+6，吸顶却在 0，一滚就跳一下。表格 margin-top 抵掉那 6px，
     吸顶位置定在 16px，静止和吸住是同一个位置；
   - 表头上方那 16px 不用内边距，用一块同样高、面板色的吸顶垫片（::before）：静止时它就是那 16px 上边距，
     滚起来它钉在顶上、压在表头之上，行滚到那儿就被盖住，不会从缝里露出来。
     试过给表头加一层往上偏的投影去盖，外扩之后会压到相邻表头的底色上，表头之间出现竖条。
   分镜那张表在 .breakdown-col 里，本来就有 16px，不动。 */
#imageView > .task-section > .table-wrap { box-sizing: border-box; padding: 0 16px; }
#imageView > .task-section > .table-wrap::before { content: ""; position: sticky; top: 0; z-index: 2; display: block; height: 16px; background: var(--surface); }
#imageView #taskTable { margin-top: -6px; }
#imageView #taskTable thead th { top: 16px; }
/* 分镜那张表同一个病（用户 2026-09-15：「只要一动滚轮，标题行必定上移几个像素」）：border-spacing 在表头上方
   留的 6px 让表头静止时在 6px、吸顶在 0，一滚就跳 6px。表格 margin-top 抵掉，静止和吸住是同一个位置。 */
.shot-table-wrap > .table-wrap > .shot-table { margin-top: -6px; }
#taskTable tbody tr:not(.empty-row) > td,
.shot-table tbody tr:not(.empty-row) > td { background: var(--surface-soft); }
#taskTable tbody tr:not(.empty-row) > td:first-child,
.shot-table tbody tr:not(.empty-row) > td:first-child { border-radius: 10px 0 0 10px; }
#taskTable tbody tr:not(.empty-row) > td:last-child,
.shot-table tbody tr:not(.empty-row) > td:last-child { border-radius: 0 10px 10px 0; }
/* 表头那条也是一块底色，四角同样切圆角——不切的话它是唯一一个方角，看着像没做完。 */
#taskTable thead th:first-child,
.shot-table thead th:first-child { border-radius: 10px 0 0 10px; }
#taskTable thead th:last-child,
.shot-table thead th:last-child { border-radius: 0 10px 10px 0; }
#taskTable tbody tr:not(.empty-row):hover > td,
.shot-table tbody tr:not(.empty-row):hover > td { background: var(--surface-hover); }
/* 正在跑的那行是蓝的，压过块底色和悬浮色：它比"我鼠标在这儿"要紧。
   必须写在块底色后面——两条选择器权重一样，靠先后定胜负。 */
/* 排队中比运行中浅一档：还没轮到它，但已经在队里了。写在运行中前面，
   一条任务万一两个类都挂着时，让「正在跑」赢。 */
#taskTable tbody tr.queued > td,
#taskTable tbody tr.queued:hover > td { background: var(--blue-faint); }
/* **跑着的和排着的都不吃悬浮色。** 这两块底色在说「这一条正在发生什么」，
   比「我鼠标在这儿」要紧。原来只写了 tr.current，可上面那条悬浮规则多一个类的
   权重（:not(.empty-row) + :hover），一直压在它上面——鼠标划过去，正在跑的那行
   会变回普通的悬浮灰。补一条 :hover 变体把权重拉平，靠先后定胜负。 */
#taskTable tbody tr.current > td,
#taskTable tbody tr.current:hover > td,
.shot-table tbody tr.expanding > td,
.shot-table tbody tr.expanding:hover > td { background: var(--blue-soft); }
/* 表格坐在本集正文的底色上，和图片生成那张表一样——原来这里垫了一层
   --surface-soft，比行底色深一档，隔行那条规则就被衬成了斑马纹。 */
.shot-table-wrap > .table-wrap { border: 0; background: transparent; }
.detail-beats,
.detail-cta,
.asset-prompt { border: 0; background: var(--surface-soft); }
.band-commands { border-left: 0; }

/* 参考图是可替换的槽位，虚线是唯一保留的边界提示。 */
#mainView .reference-slot {
  border: 1px dashed var(--line-strong);
  background: var(--input-bg);
}
#mainView .reference-slot:hover:not(:disabled) {
  border-color: var(--muted);
  background: var(--surface-hover);
}

/* 操作列始终有可见色块，避免按钮和任务面板融成一片。 */
#imageView .action-cell .view-button {
  border-color: transparent;
  background: var(--surface-soft);
  color: var(--text);
}
#imageView .action-cell .generate-button {
  background: var(--green-soft);
  color: var(--green);
}
#imageView .action-cell .download-button {
  background: var(--blue-soft);
  color: var(--blue);
}
#imageView .action-cell .view-button:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  color: var(--command-text, var(--text));
}
#imageView .action-cell .generate-button:hover:not(:disabled) {
  background: var(--primary-bg);
  color: var(--on-primary);
}
#imageView .action-cell .download-button:hover:not(:disabled) {
  background: var(--blue);
  color: var(--surface);
}

/* 分集资产卡是上下结构：一张 16:9 的图，下面名字和按钮。
   **描述不在卡片里**——就地展开会把下面整片卡片往下挤，搬进弹窗之后网格纹丝不动，
   卡片也就能做大。一行固定四张；窄屏摆不下就退成两张、一张。 */
.episode-asset-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: start;
}
@media (max-width: 1500px) { .episode-asset-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1180px) { .episode-asset-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) { .episode-asset-grid { grid-template-columns: minmax(0, 1fr); } }
.episode-asset-grid .asset-card {
  height: auto;
  min-height: 0;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  cursor: pointer;
}
/* 图是可点的（看大图和描述），别处点了是勾选。 */
.asset-figure-openable { cursor: zoom-in; }
.episode-asset-grid .asset-figure {
  width: 100%;
  /* 图下面不再有按钮行，只剩图这一行。 */
  grid-template-rows: auto;
}
/* 上下结构里正文不再需要给左边那张竖图凑高度。 */
.episode-asset-grid .asset-body { min-height: 0; }
/* 名字行必须保持一行：标题收缩并省略，勾选 / 删除按钮始终留在右侧，不能被挤到下一行。 */
.episode-asset-grid .asset-card-head {
  flex-wrap: nowrap;
  align-items: center;
  min-width: 0;
  min-height: 28px;
  height: 28px;
  overflow: hidden;
}
.episode-asset-grid .asset-card-head .asset-id,
.episode-asset-grid .asset-card-head .asset-pick,
.episode-asset-grid .asset-card-head .asset-delete { flex: none; }
.episode-asset-grid .asset-card-head .asset-name {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  font-size: 15px;
  line-height: 20px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.episode-asset-grid .asset-card-head .asset-name > .rename-text {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.episode-asset-grid .asset-card-head .rename-pen { flex: none; }
.episode-asset-grid .asset-card-head .asset-stale {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 42%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 弹窗上半块：一张 16:9 的大图，**整张都要看得见**。
   原来写的是「满宽 + max-height 46% + overflow hidden」：满宽时 16:9 要 562px 高，
   高过那个上限，多出来的直接被裁掉——图就显示不全了。
   改成**框本身就是 16:9**，高度定死、宽度由比例反推，框内再 contain，怎么都不会缺。 */
.asset-prompt-figure {
  /* 弹窗宽就是按这个高度反推出来的，所以图正好铺满可用宽度，两边不留空。 */
  height: var(--prompt-block);
  aspect-ratio: 16 / 9;
  width: calc(100% - 2 * var(--prompt-pad));
  margin: var(--prompt-pad) var(--prompt-pad) 0;
  overflow: hidden;
  border-radius: 6px;
  background: var(--surface-soft);
}
.asset-prompt-figure > span {
  display: block;
  width: 100%;
  height: 100%;
}
/* **占位框不能跟着上面那条写成 display: block。** 它靠自己那条
   `display: grid; place-content: center` 把「未生成 / 待生成」摆在正中间；
   一旦被改成 block，那几个字就掉到左上角贴着虚线边框（线上就是这样）。
   宽高它自己已经是 100%，这儿什么都不用补。 */
.asset-prompt-figure > span > img {
  display: block;
  width: 100%;
  height: 100%;
  /* 框已经是 16:9 了，contain 只是给非 16:9 的图留条退路：宁可留黑边也不裁。 */
  object-fit: contain;
}
/* 设定图本来就是 16:9 的横版表：原来按 128/170 的竖框 cover，等于把它裁成中间一条，
   看不出这张设定图到底画了什么。 */
.episode-asset-grid .asset-figure > a,
.episode-asset-grid .asset-figure > a > img,
.episode-asset-grid .asset-figure > span,
.episode-asset-grid .asset-figure > span > img,
.episode-asset-grid .asset-placeholder {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
}
/* 「添加资产」那张跟着折起来的卡片一样高，一行看着才齐。 */
.episode-asset-grid .asset-card-add { height: auto; min-height: 286px; }

/* 执行按钮统一为稳定纯色色块。悬浮照 New API（用户 2026-09-28）：在底色上叠一层纱，主按钮款（生成色、黑白）不叠；
   按下往下沉 1 像素再缩到 .98。 */
#mainView #workflowView .one-click-command,
#mainView #workflowView .prompt-command,
#mainView #workflowView .subtab-pick-command,
#mainView #workflowView .storyboard-delete-command,
#mainView #workflowView .shot-action-command,
#mainView #workflowView .asset-figure-tools button,
#mainView #workflowView .asset-foot button,
#imageView .image-commands button,
#imageView .action-cell .view-button,
#videoView .studio-shot-manage,
#videoView #studioPrompt,
#videoView #studioGenerate,
#videoView #studioPlay,
#videoView #studioFullscreen {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  color: var(--command-text, var(--text));
  box-shadow: none;
  transition:
    background-color .15s var(--btn-ease), background-image .15s var(--btn-ease), color .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
#mainView #workflowView .one-click-command:hover:not(:disabled),
#mainView #workflowView .prompt-command:hover:not(:disabled),
#mainView #workflowView .subtab-pick-command:hover:not(:disabled),
#mainView #workflowView .storyboard-delete-command:hover:not(:disabled),
#mainView #workflowView .shot-action-command:hover:not(:disabled),
#mainView #workflowView .asset-figure-tools button:hover:not(:disabled),
#mainView #workflowView .asset-foot button:hover:not(:disabled),
#imageView .image-commands button:hover:not(:disabled),
#imageView .action-cell .view-button:hover:not(:disabled),
#videoView .studio-shot-manage:hover:not(:disabled),
#videoView #studioPrompt:hover:not(:disabled),
#videoView #studioGenerate:hover:not(:disabled),
#videoView #studioPlay:hover:not(:disabled),
#videoView #studioFullscreen:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  color: var(--command-text, var(--text));
  box-shadow: none;
  background-image: var(--command-hover-image, linear-gradient(var(--btn-hover-veil), var(--btn-hover-veil)));
}

/* 生成类一律白底黑字、浅色反过来（用户 2026-09-12：「生成类按钮（剧本、分集、正文、提示词、图片、视频）都改为白底黑字」）；
   保存和下载用蓝色；暂停与停止使用既有状态色。「生成视频提示词」原来退回普通色，现在也算生成类。 */
#mainView #workflowView .one-click-command,
#mainView #workflowView .prompt-command,
#mainView #workflowView .shot-action-command.is-prompt,
/* 本集分镜行尾的「去生成图片」原来是浅蓝，用户 2026-09-12 要和资产卡正中那颗一样改成这一身。 */
#mainView #workflowView .shot-action-command.is-image,
/* 直出的镜头行尾的「去出片」（2026-09-15）和它同一身。 */
#mainView #workflowView .shot-action-command.is-video,
#mainView #workflowView .asset-foot button,
#imageView #startButton,
#imageView .action-cell .generate-button,
#videoView #studioPrompt,
#videoView #studioGenerate {
  --command-bg: var(--command-create-bg);
  --command-text: var(--command-create-text);
  --command-hover-image: none;
}
/* 生成类按钮的字一律不加粗（用户 2026-09-12：「重新生成分镜、生图提示词、图片生成中的生成按钮、视频生成中的
   保存生成视频按钮等，这些生成类的按钮，都不要加粗字体」）。.primary-command 那份 650 是给控制台 / 设置页
   主按钮的，这里按 id 压回去；.generate-command / .one-click-command / .generate-button 各自那行已经改成 400。 */
#mainView #workflowView .one-click-command,
#mainView #workflowView .prompt-command,
#mainView #workflowView .shot-action-command.is-prompt,
#mainView #workflowView .shot-action-command.is-image,
#mainView #workflowView .asset-foot button,
#mainView #workflowView .asset-prompt-generate,
#mainView #workflowView .asset-placeholder .asset-generate,
#mainView #assetDrawer .asset-generate,
#imageView #startButton,
#imageView .action-cell .generate-button,
#videoView #studioPrompt,
#videoView #studioGenerate,
.asset-prompt-dialog #assetPromptRegenerateButton,
.script-settings-foot button,
.script-episode-edit {
  font-weight: 400;
}
#mainView #workflowView .asset-view,
#imageView .action-cell .download-button {
  --command-bg: var(--blue-soft);
  --command-text: var(--blue);
}
#mainView #workflowView .storyboard-delete-command,
#imageView #deleteStoryboardsButton,
#imageView #stopButton,
/* 被领走、撤不掉的那一档：中性灰。只靠 :disabled 的 .48 透明度是不够的，
   那是一块半透明的绿，不是「灰掉」。 */
#videoView #studioGenerate.is-busy {
  --command-bg: var(--command-neutral-bg);
  --command-text: var(--muted);
}
/* 视频那颗在排队中是「停止生成」，必须跟着变红。
   **选择器要带上那两个 id**：上面 `#videoView #studioGenerate` 把 --command-bg
   钉成了生成绿，光一个 .danger-command 类压不过两个 id——按钮会顶着
   「停止生成」四个字保持绿色。 */
#videoView #studioGenerate.danger-command {
  --command-bg: var(--red-soft);
  --command-text: var(--red);
}
#mainView #workflowView .one-click-command:active:not(:disabled),
#mainView #workflowView .prompt-command:active:not(:disabled),
#mainView #workflowView .subtab-pick-command:active:not(:disabled),
#mainView #workflowView .storyboard-delete-command:active:not(:disabled),
#mainView #workflowView .shot-action-command:active:not(:disabled),
#mainView #workflowView .asset-figure-tools button:active:not(:disabled),
#mainView #workflowView .asset-foot button:active:not(:disabled),
#imageView .image-commands button:active:not(:disabled),
#imageView .action-cell .view-button:active:not(:disabled),
#videoView .studio-shot-manage:active:not(:disabled),
#videoView #studioPrompt:active:not(:disabled),
#videoView #studioGenerate:active:not(:disabled),
#videoView #studioPlay:active:not(:disabled),
#videoView #studioFullscreen:active:not(:disabled) {
  translate: 0 1px;
  scale: .98;
}

/* 视频页「保存并生成视频」：和图片生成的「全部生成」一样——
   平时、悬浮、聚焦都是同一块纯色，不提亮、不描边、不投影，只有按下时缩小。 */
#videoView #studioGenerate,
#videoView #studioPrompt:hover:not(:disabled),
#videoView #studioGenerate:hover:not(:disabled),
#videoView #studioGenerate:focus,
#videoView #studioGenerate:focus-visible {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  background-image: none;
  color: var(--command-text, var(--text));
  filter: none;
  outline: none;
  box-shadow: none;
  transition: transform .08s ease;
}

/* 剧本拆解里剩余的小按钮也遵循同一交互：固定色块，悬浮叠一层纱（主按钮款不叠），按下下沉再缩一点。 */
#mainView #workflowView .episode-manage,
#mainView #workflowView .episode-add,
#mainView #workflowView .episode-remove,
#mainView #workflowView .episode-script-foot .primary-command,
#mainView #workflowView .asset-placeholder .asset-generate,
#mainView #workflowView .asset-card-head button {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  color: var(--command-text, var(--text));
  box-shadow: none;
  transition:
    background-color .15s var(--btn-ease), background-image .15s var(--btn-ease), color .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
#mainView #workflowView .episode-manage:hover:not(:disabled),
#mainView #workflowView .episode-add:hover:not(:disabled),
#mainView #workflowView .episode-remove:hover:not(:disabled),
#mainView #workflowView .episode-script-foot .primary-command:hover:not(:disabled),
#mainView #workflowView .asset-placeholder .asset-generate:hover:not(:disabled),
#mainView #workflowView .asset-card-head button:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-bg, var(--command-neutral-bg));
  color: var(--command-text, var(--text));
  box-shadow: none;
  background-image: var(--command-hover-image, linear-gradient(var(--btn-hover-veil), var(--btn-hover-veil)));
}
#mainView #workflowView .episode-script-foot .primary-command {
  --command-bg: var(--command-generate-bg);
  --command-text: var(--command-generate-text);
  --command-hover-image: none;
}
/* 图框正中那颗「生图提示词」「去生成图片」：生成类同一身，和资产库里那颗一个样。 */
#mainView #workflowView .asset-prompt-generate,
#mainView #workflowView .asset-placeholder .asset-generate {
  --command-bg: var(--command-create-bg);
  --command-text: var(--command-create-text);
  --command-hover-image: none;
}
#mainView #workflowView .episode-manage.active,
#mainView #workflowView .episode-manage.active:hover:not(:disabled) {
  --command-bg: var(--green-soft);
  --command-text: var(--green);
}
#mainView #workflowView .episode-remove,
#mainView #workflowView .asset-delete {
  --command-bg: var(--red-soft);
  --command-text: var(--red);
}
#mainView #workflowView .episode-manage:active:not(:disabled),
#mainView #workflowView .episode-add:active:not(:disabled),
#mainView #workflowView .episode-remove:active:not(:disabled),
#mainView #workflowView .episode-script-foot .primary-command:active:not(:disabled),
#mainView #workflowView .asset-placeholder .asset-generate:active:not(:disabled),
#mainView #workflowView .asset-card-head button:active:not(:disabled) {
  translate: 0 1px;
  scale: .98;
}

/* 左侧分集与正文页签复用视频镜头列表的状态优先级：选中态永远压过悬浮态。 */
#mainView #workflowView .episode-item,
#mainView #workflowView .asset-subtab {
  border-color: transparent;
  background: var(--surface-soft);
  box-shadow: none;
}
#mainView #workflowView .episode-item:hover:not(:disabled),
#mainView #workflowView .asset-subtab:hover:not(:disabled) {
  border-color: transparent;
  background: var(--surface-hover);
  box-shadow: none;
}
#mainView #workflowView .episode-item.active,
#mainView #workflowView .episode-item.active:hover:not(:disabled),
#mainView #workflowView .asset-subtab.active,
#mainView #workflowView .asset-subtab.active:hover:not(:disabled) {
  border-color: transparent;
  background: var(--green-soft);
  box-shadow: none;
}
/* 选中的那一集要一眼看出来（用户 2026-09-14：「分集列表的选中效果不明显」）：浅色主题里 --green-soft 和面板底
   几乎一个色（#e5f3ed 压在 #f0f2f1 上），光靠底色看不出来。底色换成深一档的 --green-selected，左侧再压一道 4px 绿条。 */
#mainView #workflowView .episode-item.active,
#mainView #workflowView .episode-item.active:hover:not(:disabled) {
  background: var(--green-selected);
  box-shadow: inset 4px 0 0 var(--green);
}

/* 最左侧主导航也与镜头列表统一：默认、悬浮、选中三态分明，选中态优先。
   三态的字一样亮（--sidebar-text，2026-10-12 照 LibTV），分别靠底色：没底 / 中性灰 / 强调色滑块。 */
.sidebar .sidebar-item:not(.sidebar-item-inert) {
  background: transparent;
  color: var(--sidebar-text);
}
.sidebar .sidebar-item:not(.sidebar-item-inert):hover {
  background: var(--surface-hover);
  color: var(--sidebar-text);
}
.sidebar .sidebar-item.active:not(.sidebar-item-inert),
.sidebar .sidebar-item.active:not(.sidebar-item-inert):hover {
  background: transparent;
  color: var(--sidebar-text);
}
.sidebar .sidebar-thumb { background: var(--green-soft); }
.sidebar .sidebar-item-inert,
.sidebar .sidebar-item-inert:hover {
  background: transparent;
  color: var(--text-muted);
}

/* 资产库挂在工作流容器之外，不能继承上面的执行按钮规则。抽屉内所有按钮都用
   固定色块和按压反馈：鼠标移入不换色，避免按钮与卡片的悬浮底色融成一块。 */
#mainView #assetDrawer .asset-subtab,
#mainView #assetDrawer .asset-figure-tools button,
#mainView #assetDrawer .asset-placeholder .asset-generate,
#mainView #assetDrawer .asset-card-head button,
#mainView #assetDrawer .pagination-controls button,
#mainView #assetDrawer .primary-command,
#mainView #assetDrawer .asset-drawer-toggle {
  border-color: transparent;
  background: var(--asset-button-bg, var(--command-neutral-bg));
  color: var(--asset-button-text, var(--text));
  box-shadow: none;
  transition:
    background-color .15s var(--btn-ease), background-image .15s var(--btn-ease), color .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
#mainView #assetDrawer .asset-subtab:hover:not(:disabled),
#mainView #assetDrawer .asset-figure-tools button:hover:not(:disabled),
#mainView #assetDrawer .asset-placeholder .asset-generate:hover:not(:disabled),
#mainView #assetDrawer .asset-card-head button:hover:not(:disabled),
#mainView #assetDrawer .pagination-controls button:hover:not(:disabled),
#mainView #assetDrawer .primary-command:hover:not(:disabled),
#mainView #assetDrawer .asset-drawer-toggle:hover:not(:disabled) {
  border-color: transparent;
  background: var(--asset-button-bg, var(--command-neutral-bg));
  color: var(--asset-button-text, var(--text));
  box-shadow: none;
  background-image: var(--asset-hover-image, linear-gradient(var(--btn-hover-veil), var(--btn-hover-veil)));
}
#mainView #assetDrawer .asset-view,
#mainView #assetDrawer .asset-download {
  --asset-button-bg: var(--blue-soft);
  --asset-button-text: var(--blue);
}
/* 子页签是页签，悬浮不叠纱。 */
#mainView #assetDrawer .asset-subtab { --asset-hover-image: none; }
/* 图框正中的「生图提示词」「去生成图片」是生成类：白底黑字、浅色反过来（用户 2026-09-12）。
   导入、主按钮、选中的子页签不是生成，照旧生成绿；旁边的「下载图片」是蓝的，一眼分得开。 */
#mainView #assetDrawer .asset-generate {
  --asset-button-bg: var(--command-create-bg);
  --asset-button-text: var(--command-create-text);
  --asset-hover-image: none;
}
#mainView #assetDrawer .asset-import,
#mainView #assetDrawer .primary-command,
#mainView #assetDrawer .asset-subtab.active {
  --asset-button-bg: var(--command-generate-bg);
  --asset-button-text: var(--command-generate-text);
  --asset-hover-image: none;
}
/* 「提交审核」是动作，同一身生成绿；「审核中」「已审核」是状态不是操作：
   压成中性色，免得看着像一颗还能点、只是暂时变淡的按钮。审核中的字用蓝，
   和占位框里「生成中」同一套「在跑」的颜色。 */
#mainView #assetDrawer .asset-review {
  --asset-button-bg: var(--command-generate-bg);
  --asset-button-text: var(--command-generate-text);
  --asset-hover-image: none;
}
#mainView #assetDrawer .asset-review:disabled {
  --asset-button-bg: var(--command-neutral-bg);
  --asset-button-text: var(--muted);
}
#mainView #assetDrawer .asset-review.is-pending:disabled { --asset-button-text: var(--blue); }

#mainView #assetDrawer .asset-card-add,
#mainView #assetDrawer .asset-card-add:hover:not(:disabled) {
  border-color: var(--line-strong);
  background: var(--surface-soft);
  color: var(--muted);
  box-shadow: none;
  transform: scale(1);
  transition: transform .08s ease;
}
#mainView #assetDrawer .asset-figure-tools button:active:not(:disabled),
#mainView #assetDrawer .asset-placeholder .asset-generate:active:not(:disabled),
#mainView #assetDrawer .asset-card-head button:active:not(:disabled),
#mainView #assetDrawer .asset-card-add:active:not(:disabled),
#mainView #assetDrawer .pagination-controls button:active:not(:disabled),
#mainView #assetDrawer .primary-command:active:not(:disabled),
#mainView #assetDrawer .asset-drawer-toggle:active:not(:disabled) {
  translate: 0 1px;
  scale: .98;
}
/* 把手的描边和投影：上面那套规则把 border-color 抹成透明、box-shadow 清成 none，
   把手要和抽屉连成一体，得在它后面再补回来——描边和抽屉那道左边框同色，投影同一份。 */
#mainView #assetDrawer .asset-drawer-toggle,
#mainView #assetDrawer .asset-drawer-toggle:hover:not(:disabled) {
  border-color: var(--glass-line, var(--line));
  box-shadow: -12px 0 28px rgba(0, 0, 0, .28);
}

/* 资产库卡片没有悬浮底色；图片本身的悬浮预览仍由 JS 单独处理。 */
#mainView #assetDrawer .asset-card:not(.asset-card-add),
#mainView #assetDrawer .asset-card:not(.asset-card-add):hover {
  border-color: var(--card-border);
  background: var(--surface);
  box-shadow: none;
}

/* 邀请、设置、控制台与工作台共用无描边色块层级：页面底、主面板、内部控件逐级提亮。 */
:is(#invitePage, #settingsPage, #consolePage) .panel {
  border: 0;
  border-radius: 8px;
  background: var(--surface);
  box-shadow: none;
}
#invitePage .invite-grid > .panel:nth-child(even),
#settingsPage > .panel:nth-of-type(even),
#consolePage .console-pane > .panel:nth-child(even) { background: var(--surface-alt); }

#invitePage .panel-callout { border: 0; background: var(--green-soft); }
#invitePage .invite-quota > div,
#settingsPage .settings-account-details > div {
  min-width: 0;
  padding: 12px 14px;
  border: 0;
  border-radius: 6px;
  background: var(--surface-soft);
}
#invitePage .invite-quota { gap: 8px; }
#invitePage .invite-quota > div { flex: 1 1 0; }
#invitePage .invite-row {
  border: 0;
  border-radius: 6px;
  background: var(--surface-soft);
  transition: background .12s ease;
}
#invitePage .invite-row:hover { background: var(--surface-hover); }
#invitePage .invite-tag.used { background: var(--command-neutral-bg); }

#settingsPage .settings-account-details {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 0;
  margin-bottom: 14px;
}
#settingsPage .settings-account-details > div { padding: 12px 14px; }

:is(#settingsPage, #consolePage) input:not([type="checkbox"]):not([type="radio"]),
:is(#settingsPage, #consolePage) select,
:is(#settingsPage, #consolePage) textarea {
  border: 0;
  background: var(--surface-soft);
  box-shadow: none;
}

:is(#invitePage, #settingsPage, #consolePage) button,
#consolePage .button-link {
  border-color: transparent;
  box-shadow: none;
  transition:
    background-color .15s var(--btn-ease), background-image .15s var(--btn-ease), color .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
:is(#invitePage, #settingsPage, #consolePage) button:hover:not(:disabled),
#consolePage .button-link:hover {
  border-color: transparent;
  box-shadow: none;
}
/* 用户时间线弹窗挂在控制台外面（body 下的 dialog），它里面的「刷新」要和运营数据那颗一个样子（用户 2026-09-14），
   所以也归这一套：原来那颗是普通按钮，是因为弹窗外面拿到的主按钮是另一套渐变。 */
:is(#invitePage, #settingsPage, #consolePage, .metrics-timeline-dialog) .primary-command {
  background: var(--command-generate-bg);
  color: var(--command-generate-text);
  /* 主题层给 .primary-command 的那圈紫色投影是渐变那套的：控制台里被别的规则抹掉了，弹窗里没有，这里一并抹平 */
  box-shadow: none;
}
:is(#invitePage, #settingsPage, #consolePage, .metrics-timeline-dialog) .primary-command:hover:not(:disabled) {
  background: var(--command-generate-bg);
  color: var(--command-generate-text);
}
:is(#invitePage, #settingsPage, #consolePage) .danger-command {
  background: var(--btn-danger-bg);
  color: var(--danger);
}
:is(#invitePage, #settingsPage, #consolePage) .danger-command:hover:not(:disabled) { background: var(--btn-danger-hover); }

/* 顶部那一条冻住：内容长的几页（运营数据、模型配置、消耗定价…）滚下去之后
   还能直接换页签，不用先滚回顶上。底色要不透明，否则内容从槽的半透明缝里透出来。
   高的那几页（工单 / 用户 / 订单…）本来就 overflow: hidden、头部不跟着滚，
   这条对它们是空操作。 */
/* 控制台这一页（它自己就是滚动容器）顶上不留内边距，那 12px 由下面冻住的头自己带。
   原来是容器留 12px、头用 top: -12px 去补：负偏移的 sticky 在带内边距的滚动容器里，
   Chrome 合成线程和主线程算出的位置差一截，滚动时来回修正——头一直小幅上下抖
   （用户 2026-09-11）。容器不留内边距，头就能老老实实 top: 0，不抖也不留缝。 */
#consolePage { padding-top: 0; }
#consolePage > .page-head {
  position: sticky;
  top: 0;
  z-index: 3;
  padding-top: 12px;
  padding-bottom: 10px;
  background: var(--bg);
}
#consolePage .metrics-tile,
#consolePage .console-model-group,
#consolePage .console-order-total,
#consolePage .console-detail {
  border: 0;
  border-radius: 6px;
  background: var(--surface-soft);
  box-shadow: none;
}
#consolePage .console-model-group:nth-child(even),
#consolePage .console-order-total:nth-child(even) { background: var(--surface-hover); }
#consolePage .table-wrap { border: 0; border-radius: 6px; background: transparent; box-shadow: none; }
#consolePage :is(.metrics-table, .ticket-table) {
  border-collapse: separate;
  border-spacing: 0 4px;
}
#consolePage :is(.metrics-table, .ticket-table) th,
#consolePage :is(.metrics-table, .ticket-table) td { border: 0; }
#consolePage :is(.metrics-table, .ticket-table) tbody tr { background: var(--surface-soft); }
#consolePage :is(.metrics-table, .ticket-table) tbody tr:hover { background: var(--surface-hover); }
#consolePage :is(.metrics-table, .ticket-table) tbody tr.active,
#consolePage :is(.metrics-table, .ticket-table) tbody tr.active:hover { background: var(--green-soft); }
#consolePage :is(.metrics-table, .ticket-table) tbody td:first-child { border-radius: 6px 0 0 6px; }
#consolePage :is(.metrics-table, .ticket-table) tbody td:last-child { border-radius: 0 6px 6px 0; }
#consolePage .pagination-bar {
  border: 0;
  border-radius: 6px;
  background: var(--surface-soft);
}
#consolePage #consoleTicketDetail .ticket-head,
#consolePage #consoleTicketDetail .ticket-composer { border: 0; }
#consolePage #consoleTicketDetail .ticket-composer { background: var(--surface-hover); }
#consolePage .console-permission-item {
  padding: 7px 9px;
  border-radius: 6px;
  background: var(--surface-soft);
}
#consolePage .notice-image-preview { border: 0; background: var(--surface-soft); }
#consolePage .console-copy-button,
#consolePage .console-copy-button:hover:not(:disabled) { border: 0; background: transparent; }

@media (max-width: 680px) {
  #settingsPage .settings-account-details { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1440px) {
  .video-studio { grid-template-columns: minmax(240px, 260px) minmax(360px, 1fr) minmax(390px, 490px); }
  .studio-role-section .studio-resource-card,
  .studio-role-section .studio-resource-card > span,
  .studio-prop-section .studio-resource-card,
  .studio-prop-section .studio-resource-card > span,
  .studio-scene-section .studio-resource-card,
  .studio-scene-section .studio-resource-card > span { width: 96px; min-width: 96px; }
}

@media (max-width: 1200px) {
  /* 3D 导演台只做桌面端。 */
  .studio-resources .studio-whitebox-section { display: none; }
  .video-studio-view { padding-top: 0; }
  .studio-mobile-tabs {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 2px;
    padding: 5px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
  }
  .studio-mobile-tabs button { min-height: 34px; border: 0; background: transparent; color: var(--muted); }
  .studio-mobile-tabs button.active { background: var(--green-soft); color: var(--green); }
  .video-studio { display: block; height: calc(100% - 45px); }
  /* 上面预览、下面分镜台 62 : 38。2026-09-18 写成了 minmax(0, 1fr) : 38fr，预览只分到 1/39：平板竖着拿、1100×900 这类比 820 高的屏上
     预览窗被分镜台挤成一条（矮屏那条 74 : 26 盖过了它，所以 iPad 横着看不出来；2026-10-09 修） */
  .video-studio { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 62fr) minmax(240px, 38fr); grid-template-areas: "stage" "board"; }
  .video-studio > [data-studio-panel] { display: none; width: 100%; height: 100%; grid-area: stage; }
  .video-studio > [data-studio-panel].mobile-active { display: grid; }
  .video-studio > .studio-inspector.mobile-active { display: flex; padding-bottom: 12px; }
  .video-studio > .studio-board-panel { display: flex; min-height: 0; }
  .studio-center { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); }
  .studio-actions { padding-bottom: max(0px, env(safe-area-inset-bottom)); }
}

@media (max-width: 620px) {
  .studio-center { grid-template-rows: minmax(340px, 62fr) minmax(210px, 38fr); }
}



/* ---- 剧本创作（工作台第四个模式） ---- */
/* 和工作台其它面板一个做法：靠色块分层，不用一圈圈描边。 */
.works-subhead { display: flex; align-items: baseline; gap: 10px; margin: 0 0 12px; font-size: 16px; font-weight: 600; }
/* 新建项目：单独一行，但和普通卡片一样大、靠左。 */
.works-new-grid { padding-bottom: 20px; }
.works-recent { padding-bottom: 24px; }
.draft-card { min-height: 0; }
.draft-monogram { font-size: 15px; }
/* 剧本创作页和图片生成页同一套节奏：外层 8px 留白，内容整块放在一个面板背景里。
   面板内的小块（剧名块、提示语、剧集行、脚栏、设定面板）退一档用 surface-soft，才分得开。 */
/* 剧本创作页整页不滚：只有页签下面的内容区和右侧对话框各自滚。 */
#scriptView { padding: 8px; background: var(--bg); overflow: hidden; }
#scriptView .script-page-body { height: calc((100vh - var(--top-banner-height, 0px)) - 122px); min-height: 0; }
#scriptView .script-layout { height: 100%; align-items: stretch; }
#scriptView .script-main { box-sizing: border-box; height: 100%; min-height: 0; padding: 8px 16px 14px; border-radius: 8px; background: var(--surface); }
/* 助手那一栏和左栏一样高：面板撑满这一行，不再自己按视口算高度 */
#scriptView .script-side { height: 100%; min-height: 0; }
#scriptView .script-side .script-assistant-panel { height: 100%; min-height: 0; }
#scriptView .script-main > .script-title-block, #scriptView .script-main > .script-tabs-bar { flex: none; }
#scriptView .script-tab-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
#scriptView .script-main-foot { display: flex; flex: none; justify-content: center; padding: 10px 0 2px; }
/* 分镜生成页：正在跑的那一步显示在本集标题下面 */
/* ================= 滑轨页签（全站统一，改这里就是改全部） =================
   底轨 var(--tab-rail-strong)、滑块 var(--primary-bg)、选中字 var(--on-primary)、
   未选中字 var(--text)。
   选中态由滑块画，按钮自己不换背景——这样「悬浮 / 未选中 / 选中 / 聚焦一律
   同一样式，只有按下缩小」那条全站规矩才成立。

   现在有七处在用：控制台顶部页签、控制台的收款方式、控制台的模型配置、剧本创作、
   新建项目、发票弹窗、分镜生成里的「分镜 / 资产预览」；后来又登记了运营数据的日期范围、每一镜的「首帧 | 直出」、
   画风类别、图片生成页的「角色 / 场景 / 道具 / 分镜」（2026-09-20）、Minimo3D 页上的几条（m3d-rail，2026-09-25）、
   图片生成页画廊的状态页签（ig-tabs，2026-10-09）、视频生成页剪辑台的「首帧生成 | 直接生成」（reel-seg，2026-10-09）。
   **再加一处就把类名加进下面这四串选择器，不要另抄一份**——抄一份的下场是
   三处各飘各的：发票那两个页签一度用的是 --surface-soft 底轨 + --surface 滑块，
   和另外两处底色滑块颜色全不一样。
   付款弹窗的收款方式页签是**故意**的例外：它的滑块跟着支付宝蓝 / 微信绿走，
   一眼看出现在扫的是哪家的码，不并进来。 */
.m3d-rail,
.model-family-rail,
.pay-channel-rail,
.console-tab-rail,
.script-tabs-bar,
.invoice-tabs,
.new-work-modes,
.metrics-range-rail,
.episode-rail,
.shot-mode-rail,
.image-type-buttons,
.mobile-pane-rail,
.ig-tabs,
.reel-seg,
.product-rail,
.visual-style-tabs {
  position: relative;
  display: flex;
  gap: 6px;
  padding: 4px;
  border-radius: 999px;
  /* 实心浅灰。槽压在面板(--surface-soft)、页面(--bg)、弹窗(--surface)三种底上，
     这一档比三者都亮一截（深色）/ 都深一截（浅色），所以一个值三处都看得见。
     **仍旧不许写死色值**：要调就调 --tab-rail-strong 那两档，别在这儿写十六进制。 */
  background: var(--tab-rail-strong);
}
.m3d-rail-tab,
.model-family-tab,
.pay-channel-tab,
.console-tab,
.script-main-tab,
.invoice-tab,
.new-work-mode,
.episode-rail-tab,
.metrics-range-tab,
.shot-mode-tab,
.image-type-button,
.mobile-pane-tab,
.ig-tab,
.reel-seg-tab,
.product-tab,
.visual-style-tab {
  position: relative;
  z-index: 1;
  min-height: 0;
  padding: 8px 12px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  /* 三颗字都要能读，不只是选中那颗——原来未选中用 --muted，压在实心槽上偏灰。 */
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: color .2s;
}
.m3d-rail-tab.active,
.model-family-tab.active,
.pay-channel-tab.active,
.console-tab.active,
.script-main-tab.active,
.invoice-tab.active,
.episode-rail-tab.active,
.shot-mode-tab.active,
.image-type-button.active,
.mobile-pane-tab.active,
.ig-tab.active,
.reel-seg-tab.active,
.product-tab.active,
.metrics-range-tab.active,
.visual-style-tab.active,
.new-work-mode.active { background: transparent; color: var(--on-primary); }
.m3d-rail-tab:active,
.model-family-tab:active,
.pay-channel-tab:active,
.console-tab:active,
.script-main-tab:active,
.invoice-tab:active,
.episode-rail-tab:active,
.metrics-range-tab:active,
.shot-mode-tab:active,
.image-type-button:active,
.visual-style-tab:active,
.mobile-pane-tab:active,
.ig-tab:active,
.reel-seg-tab:active,
.product-tab:active,
.new-work-mode:active { transform: scale(.97); }
.m3d-rail-thumb,
.model-family-thumb,
.pay-channel-thumb,
.console-tab-thumb,
.script-tab-thumb,
.invoice-tab-thumb,
.new-work-mode-thumb,
.metrics-range-thumb,
.episode-rail-thumb,
.shot-mode-thumb,
.image-type-thumb,
.mobile-pane-thumb,
.ig-tab-thumb,
.reel-seg-thumb,
.product-thumb,
.visual-style-tab-thumb {
  position: absolute;
  z-index: 0;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: 0;
  border-radius: 999px;
  /* 主按钮那套实色，配 --on-primary 的白字：选中那颗一眼看得出。 */
  background: var(--primary-bg);
  box-shadow: 0 2px 10px var(--accent-soft);
  pointer-events: none;
  will-change: transform;
}
/* 第一次摆位不要过渡，否则滑块会从最左边飞进来。 */
.thumb-ready .m3d-rail-thumb,
.thumb-ready .model-family-thumb,
.thumb-ready .pay-channel-thumb,
.thumb-ready .console-tab-thumb,
.thumb-ready .script-tab-thumb,
.thumb-ready .invoice-tab-thumb,
.thumb-ready .new-work-mode-thumb,
.thumb-ready .metrics-range-thumb,
.thumb-ready .episode-rail-thumb,
.thumb-ready .shot-mode-thumb,
.thumb-ready .image-type-thumb,
.thumb-ready .mobile-pane-thumb,
.thumb-ready .ig-tab-thumb,
.thumb-ready .reel-seg-thumb,
.thumb-ready .product-thumb,
.thumb-ready .visual-style-tab-thumb {
  transition: transform .28s cubic-bezier(.22, .61, .36, 1), width .28s cubic-bezier(.22, .61, .36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .thumb-ready .m3d-rail-thumb,
  .thumb-ready .model-family-thumb,
  .thumb-ready .pay-channel-thumb,
  .thumb-ready .console-tab-thumb,
  .thumb-ready .script-tab-thumb,
  .thumb-ready .invoice-tab-thumb,
  .thumb-ready .new-work-mode-thumb,
  .thumb-ready .metrics-range-thumb,
  .thumb-ready .episode-rail-thumb,
  .thumb-ready .shot-mode-thumb,
  .thumb-ready .image-type-thumb,
  .thumb-ready .mobile-pane-thumb,
  .thumb-ready .ig-tab-thumb,
  .thumb-ready .reel-seg-thumb,
  .thumb-ready .product-thumb,
  .thumb-ready .visual-style-tab-thumb { transition: none; }
}
/* 分镜表每一行的「首帧 | 直出」（2026-09-15）：形状走上面那份，只是**黑白的**（用户定的：
   「颜色用黑白的，自动适配主题」）。不写死色值——把这一条槽里的滑块色 / 选中字色两个 token 换成
   正文色 / 页面底色，浅色主题就是黑滑块白字，深色主题反过来，主题一换自己跟着换。
   表格一行一条、一屏十几条，尺寸收小一档，滑块不投影。 */
/* isolation：滑轨里那两颗字是 z-index 1 的定位元素，和吸顶表头（全局 th，z-index 1）同层、后画的压前画的，
   往下滚时滑轨会盖在表头上（用户 2026-09-15）。把滑轨圈成自己的层叠上下文，里面的 z-index 出不来，就老老实实钻到表头底下。 */
.shot-mode-rail { --primary-bg: var(--text); --on-primary: var(--bg); isolation: isolate; width: fit-content; flex: none; gap: 2px; padding: 3px; vertical-align: middle; }
.shot-mode-tab { padding: 4px 11px; font-size: 12px; line-height: 1.2; }
.shot-mode-tab:disabled { cursor: not-allowed; opacity: .6; }
.shot-mode-thumb { top: 3px; bottom: 3px; box-shadow: none; }
/* 运营数据的日期范围那条（用户 2026-09-11）：按字定宽、挨着标题，颜色形状全走上面那份。 */
.metrics-range-rail { width: fit-content; }
.metrics-range-tab { padding: 7px 16px; }
/* 三处只在排布上有差别，颜色和形状一律走上面那份。 */
.model-family-rail { margin: 10px 0 6px; }
/* 收款那条槽不撑满整行：两个页签撑满一整行太夸张，居左、按内容宽。
   父级是普通的 section 不是 flex，align-self 在这儿不起作用——实拍量出来 600 多宽，
   要用 width: fit-content 才真的按内容收。 */
.pay-channel-rail { width: fit-content; margin: 4px 0 10px; }
.pay-channel-tab { padding: 8px 26px; }
/* 三枚开关横着排。关闭支付勾上后另外两枚变灰，形状不在这儿定——那份药丸开关的样式是共用的。 */
.console-pay-switches { display: flex; flex-wrap: wrap; gap: 6px 28px; margin: 4px 0 10px; }
.console-pay-switches .console-switch-field.is-disabled { opacity: .45; }
/* 收款参数两列排：一格一行的话十几个输入框拉满整屏，又长又空。标题和说明横跨两列。
   藏起来的那一家的格子是 display: none，网格里不占位。 */
#consolePayChannelForm .console-model-group {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 18px;
  max-width: 1120px;
}
#consolePayChannelForm .console-model-group > h4,
#consolePayChannelForm .console-model-group > .panel-note { grid-column: 1 / -1; margin: 0; }
/* 控制台那条原来 flex: 1 撑满整行，用户嫌太长：按字定宽、靠左，和剧本创作那条一样。 */
.model-family-rail { align-self: flex-start; width: fit-content; }
.model-family-tab { padding: 8px 26px; }
.invoice-tabs { align-self: center; }                    /* 发票那两个居中，不撑满 */
/* 剧本创作那三个原来也撑满整行，一颗占三分之一宽，用户嫌太长：改成按字定宽、靠左，
   内边距照发票那两个。颜色形状全走上面那份共用的，这里只剩排布。 */
.script-tabs-bar { align-self: flex-start; }
.invoice-tab, .script-main-tab { padding: 8px 26px; }
/* 图片生成页的类型那条（2026-09-20）：带图标所以是 inline-flex，按字定宽、靠左、不换行——滑块只按 offsetLeft / offsetWidth
   横着摆，换成两排它就对不上了。窄屏下四颗平分整行（原来是两排各两颗）。 */
.image-type-buttons { width: fit-content; flex: none; flex-wrap: nowrap; }
.image-type-button { display: inline-flex; align-items: center; gap: 8px; padding: 8px 20px; font-size: 14px; white-space: nowrap; }
@media (max-width: 640px) {
  .image-type-buttons { width: 100%; }
  .image-type-button { flex: 1 1 0; justify-content: center; gap: 5px; padding: 8px 6px; }
}
/* Minimo3D 页上的几条（生成 / 我的模型、模型版本、生成类型、模型精度、材质、筛选）：参数栏里的撑满一行、各颗平分，
   顶上和「我的模型」里的按字定宽。颜色形状全走上面那份。 */
.m3d-rail-tab { flex: 1 1 0; padding: 7px 10px; font-size: 13px; white-space: nowrap; }
.m3d-top-rail .m3d-rail, #m3dFilterRail .m3d-rail { width: fit-content; }
.m3d-top-rail .m3d-rail-tab, #m3dFilterRail .m3d-rail-tab { flex: none; padding: 7px 18px; }
/* 工作台的分栏页签（mobile-panes.tsx）：手机、平板（1200 宽以下）露出来、撑满一行、各颗等分。 */
.mobile-pane-rail { display: none; }
@media (max-width: 1199px) {
  .mobile-pane-rail { display: flex; flex: none; margin: 0 0 8px; }
  .mobile-pane-tab { flex: 1 1 0; padding: 7px 8px; text-align: center; }
}
/* 视频生成页剪辑台镜头面板右上角的「首帧生成 | 直接生成」（用户 2026-10-09：「要有滑动效果」）：两段等宽、按字定宽，
   滑块不靠脚本量，按 is-direct 平移一格（两段字数一样；和分镜页清单那一组一个做法）。生成中的那一镜另一颗灰掉、不让切 */
.reel-seg { flex: none; width: fit-content; gap: 2px; padding: 3px; }
.reel-seg-tab { flex: 1 1 0; height: 26px; padding: 0 12px; font-size: 12px; white-space: nowrap; }
.reel-seg-tab:disabled { cursor: not-allowed; opacity: .5; }
.reel-seg-thumb { top: 3px; bottom: 3px; left: 3px; width: calc((100% - 8px) / 2); box-shadow: none; }
.reel-seg.is-direct .reel-seg-thumb { transform: translateX(calc(100% + 2px)); }
/* 侧栏顶部的产品切换（2026-10-27，用户：「分 3 个大板块，可以让用户切换：Minimo TV、Minimo 3D、Minimo Design」）：
   logo 下面一条，各颗平分侧栏宽。3D、Design 跟着各自的功能开关走（内测时只给管理员露出来），露几颗就几颗平分。
   颜色形状全走上面那份；滑块按选中那颗量（新版 RailTabs、旧页面 positionProductThumb）。平板那一档在「屏幕适配」里竖着排。 */
.product-switch { flex: none; margin: 12px 0 0; }
.product-rail { gap: 2px; padding: 3px; }
.product-tab { flex: 1 1 0; height: 28px; padding: 0 6px; font-size: 12.5px; white-space: nowrap; }
.product-thumb { top: 3px; bottom: 3px; box-shadow: none; }
/* ======================= 滑轨页签到此为止 ======================= */

/* ---------- 模型配置：供应商卡片 ---------- */

/* 三类模型各一页，页内一张张卡片。卡片本体沿用 .console-model-group
   （控制台里所有配置块都是它），这里只加卡头和模型清单那张表。 */
/* 卡片两列排。一列排下来一张卡就占满整屏宽，一个下拉框拉成一整行、
   右边全是空白。用 auto-fit + minmax 按**容器**实际宽度决定几列，
   不猜断点：控制台的可用宽度还要减去侧栏，视口断点对不上。 */
#consoleModelForm.console-models {
  display: grid;
  /* 固定四列。用 auto-fit 的话只有一张卡时它会横向撑满整行——
     卡片宽度必须始终是「四列里的一格」，有几张就占几格，剩下的留白。 */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* 卡片等高：grid-auto-rows: 1fr 让每一行等分，stretch 让同一行里的卡片
     长到一样高。**不要写 align-items: start**——那会让每张卡各按自己的
     内容收，两张并排就一高一低。 */
  grid-auto-rows: 1fr;
  align-items: stretch;
  gap: 12px;
}
/* 供应商清单（2026-09-24）：一列，一家一行，按住把手拖动排序，排第一的是默认供应商。
   原来两列卡片一高一低、每张卡顶上都写着「模型信息」、默认供应商是另一个下拉，用户嫌「不合理且丑陋」。
   一列才拖得顺：两列拖动时卡片会在左右之间跳。宽度封顶，不然一个下拉框拉成一整行。 */
#consoleModelForm.console-models.console-provider-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 1400px;
}
.provider-list { display: flex; flex-direction: column; gap: 8px; }
.provider-list.is-sorting { user-select: none; }
.provider-list.is-sorting, .provider-list.is-sorting .provider-drag-handle { cursor: grabbing; }
#consoleModelForm > .panel-note { margin: 0; }
.console-model-intro { margin: 2px 2px 10px; }

/* 清单的标题行：左边标题和「排第一的是默认」那句，右边「新增供应商」。 */
.console-model-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  max-width: 1400px;
  margin: 18px 0 10px;
}
.console-model-head-copy { min-width: 0; }
.console-model-head-copy h4 { margin: 0 0 4px; font-size: 15px; font-weight: 600; color: var(--text); }
.console-model-head-copy .panel-note { margin: 0; }
.console-model-head-copy .panel-note:empty { display: none; }
#consoleProviderAdd { flex: none; }
@media (max-width: 600px) {
  .console-model-head { flex-direction: column; align-items: flex-start; }
}
/* 分步供应商：文本模型页清单下面那张表，每个步骤一行一个下拉。颜色全走主题变量。 */
.console-stage-providers { margin: 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-soft); }
.console-stage-providers-title { margin: 0 0 4px; font-size: 15px; font-weight: 600; color: var(--text); }
.console-stage-providers .panel-note { margin: 0 0 12px; }
.console-stage-providers-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px 14px; }
.console-stage-provider { margin: 0; }

.provider-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 2px;
}
/* 名字就是卡片标题，所以按标题的字号排，不按输入框的。 */
.provider-card-name {
  flex: 1;
  min-width: 0;
  height: 34px;
  font-size: 15px;
  font-weight: 600;
}
.provider-card-remove {
  flex: none;
  min-height: 30px;
  padding: 0 10px;
  color: var(--muted);
  font-size: 13px;
}
/* 视频卡的两个名字各带标题，并排两格；删除按钮和输入框底边对齐。 */
.provider-card-head.has-names { align-items: flex-end; }
.provider-card-names {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 10px;
  min-width: 0;
}
.provider-card-names .field > span { font-size: 12px; }
.provider-card-names .provider-card-name { height: 34px; font-size: 14px; font-weight: 500; }
/* 「允许自动切换」跟着名字排在第一行。开关本身的尺寸不在这儿定——
   那份药丸开关的样式是共用的，这里只管别让文字换行。 */
.provider-card-failover, .provider-card-enabled, .provider-card-standby, .provider-card-admin-only { flex: none; }
.provider-card-enabled > span, .provider-card-standby > span, .provider-card-admin-only > span { white-space: nowrap; font-size: 12px; }
/* 停用的那家整卡压暗：一眼看出它现在不对用户开放，配置照样能改。 */
.provider-card.provider-disabled > :not(.provider-card-head) { opacity: .45; }
.provider-card.provider-disabled .provider-card-head .provider-card-title { opacity: .6; }
.provider-card-failover > span { white-space: nowrap; font-size: 12px; }

/* 第二行：地址和密钥并排。卡片才 340 宽，两格各 ~160，
   长地址看不全但能滚——比各占一行省下一整行的高度。 */
.provider-card-conn {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 10px;
}
.provider-card-conn .field > span { font-size: 12px; }
.provider-card-remove:hover { color: var(--danger, #e07a6a); }

/* 一个模型一张小卡：自己的底色（比供应商卡深一档），标识一行、显示名一行、上游模型名一行、价格三行。
   小卡两列排：供应商卡已经是半屏宽，一列一张会拉得很长。 */
.provider-model-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 6px; }
.provider-model-card {
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.provider-model-card-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.provider-model-card-head .provider-card-remove { margin-left: auto; }
/* 「设为默认」贴在删除左边：它是这一行右半边的头一个，自动外边距归它。 */
.provider-model-default-pick { margin-left: auto; flex: none; white-space: nowrap; }
.provider-model-default-pick > span { font-size: 12px; }
.provider-model-card-head .provider-model-default-pick + .provider-card-remove { margin-left: 0; }
/* 「自定义上游模型名」开关：和「设为默认」同一档字号，靠左、不撑满那一行。 */
.provider-model-custom-name { justify-self: start; white-space: nowrap; }
.provider-model-custom-name > span { font-size: 12px; }
/* 显示名在上、上游模型名在下：小卡只有四分之一屏宽，并排两格放不下上游那串长名字。 */
.provider-model-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  margin-top: 8px;
}
.provider-model-fields .field > span { font-size: 12px; }
.upstream-model-picker { position: relative; min-width: 0; }
.upstream-model-picker::after { content: ""; position: absolute; right: 12px; top: 50%; width: 10px; height: 10px; background: var(--muted); mask: url("/static/icon-chevron-right.png") center / contain no-repeat; transform: translateY(-50%) rotate(90deg); pointer-events: none; }
.provider-card-catalog .upstream-model-picker input { width: 100%; min-width: 0; padding-right: 32px; font-size: 12px; }
.upstream-model-menu { position: fixed; inset: auto; margin: 0; padding: 6px; box-sizing: border-box; overflow: auto; overscroll-behavior: contain; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--surface); color: var(--text); box-shadow: 0 8px 24px var(--dialog-shadow); z-index: 1000; }
.upstream-model-menu::backdrop { background: transparent; }
.upstream-model-option { display: flex; align-items: center; gap: 10px; min-height: 36px; padding: 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }
.upstream-model-option > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.upstream-model-option > small { flex: none; color: var(--muted); font-size: 11px; }
.upstream-model-option.active, .upstream-model-option:hover { background: var(--surface-hover); }
.upstream-model-option[aria-selected="true"] { color: var(--accent-text); }
.upstream-model-option[aria-disabled="true"] { color: var(--muted); cursor: not-allowed; }
.upstream-model-empty { padding: 12px 8px; color: var(--muted); font-size: 12px; }
.provider-model-card .provider-model-prices { margin-top: 8px; }
/* 按供应商的实际宽度降为单列，避免模型卡片过窄把价格表挤出横向滚动条。 */
.provider-card { container-type: inline-size; }
@container (max-width: 610px) {
  .provider-model-list { grid-template-columns: minmax(0, 1fr); }
}
.provider-model-add { margin-top: 8px; min-height: 32px; font-size: 13px; }
/* 「测试连接」那一行：按钮在左，结果跟在后面。 */
.provider-probe { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.provider-probe .panel-note { margin: 0; }

/* 三类供应商共用连接信息的排版，各自保留分步设置或型号参数。
   卡片自己不留内边距：卡头一行和卡身各管各的，收起来就只剩卡头。 */
.provider-card-catalog { padding: 0; gap: 0; min-width: 0; }
.provider-card-catalog .provider-card-head { margin: 0; }
.provider-card-catalog .field { margin: 0; }
/* 名字那一行和下面地址那一行用同一套列宽：后台名称压在 Base URL 上面，前端显示名称压在 API Key 上面。 */
.provider-card-catalog .provider-card-names { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 132px; gap: 14px; }
.provider-card-catalog[data-family="text"] .provider-card-names { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.3fr); }

/* 卡头一行：把手 · 名次 · 名字和摘要（点它展开）· 开关 · 删除 · 展开箭头。 */
.provider-card-catalog > .provider-card-head { min-height: 60px; padding: 8px 10px 8px 6px; gap: 12px; }
/* 把手：两列三行的小圆点，颜色跟主题（--muted）。按住就能拖，touch-action 关掉触屏的滚动手势。 */
.provider-drag-handle {
  flex: none;
  width: 26px;
  height: 40px;
  min-height: 40px;
  padding: 9px 6px;
  border: 0;
  border-radius: 6px;
  background: radial-gradient(circle, var(--muted) 1.4px, transparent 1.9px) 0 0 / 7px 7px;
  background-origin: content-box;
  background-clip: content-box;
  cursor: grab;
  touch-action: none;
}
.provider-rank { flex: none; display: inline-flex; align-items: center; gap: 8px; min-width: 112px; }
.provider-rank-no {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--tab-rail-strong);
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
}
.provider-rank-tag {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--tab-rail-strong);
  color: var(--muted);
  font-size: 12px;
  line-height: 18px;
  white-space: nowrap;
}
.provider-rank-tag.is-default { background: var(--accent-soft); color: var(--accent-text); }
.provider-rank-tag.is-backup { color: var(--text); }
/* 名字和摘要是一颗按钮（点它展开），不画按钮的底和边。 */
.provider-card-title {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  padding: 2px 4px;
  border: 0;
  background: transparent;
  color: var(--text);
  text-align: left;
  white-space: normal;
}
.provider-card-title strong,
.provider-card-summary { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.provider-card-title strong { font-size: 15px; font-weight: 600; }
.provider-card-summary { color: var(--muted); font-size: 12px; }
/* 供应商健康（NA-3）：卡头第三行。前面一颗小圆点：有调用没问题是绿，巡检查出问题整行标红，没调用是灰。 */
.provider-card-health { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: 12px; }
.provider-card-health::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  vertical-align: 1px;
  background: var(--muted);
}
.provider-card-health.is-ok::before { background: var(--success); }
.provider-card-health.is-alert { color: var(--red); }
.provider-card-health.is-alert::before { background: var(--red); }
.provider-card-chevron { position: relative; flex: none; width: 34px; height: 34px; min-height: 34px; padding: 0; border: 0; background: transparent; }
.provider-card-chevron::before {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 12px;
  height: 12px;
  background: var(--muted);
  mask: url("/static/icon-chevron-right.png") center / contain no-repeat;
  transform: rotate(90deg);
  transition: transform .2s ease;
}
.provider-card.is-open .provider-card-chevron::before { transform: rotate(-90deg); }
/* 卡身：点开才有。和卡头之间一道线。 */
.provider-card-body { display: flex; flex-direction: column; gap: 14px; min-width: 0; padding: 16px 20px 20px; border-top: 1px solid var(--line); }
.provider-card:not(.is-open) > .provider-card-body { display: none; }
/* 拖着的那张浮起来，压在别的卡上面跟着指针走。 */
.provider-card.is-dragging { position: relative; z-index: 5; border-color: var(--accent); box-shadow: 0 14px 36px var(--dialog-shadow); }
.provider-card-catalog .provider-card-names .field > span { font-size: 12px; }
.provider-card-catalog .provider-card-name { flex: none; min-height: 38px; }
.provider-card-catalog .provider-card-head > .provider-card-name { flex: 1; }
.provider-card-catalog .field input,
.provider-card-catalog .field select { height: 38px; font-size: 14px; }
.provider-card-catalog .provider-card-enabled,
.provider-card-catalog .provider-card-standby,
.provider-card-catalog .provider-card-admin-only { min-height: 38px; }
.provider-card-catalog .provider-card-head > .provider-card-remove { min-height: 38px; }
.provider-card-catalog .provider-card-conn {
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 132px;
  align-items: end;
  gap: 14px;
}
.provider-card-catalog .provider-probe { align-self: end; min-width: 0; }
.provider-card-catalog .provider-probe .model-test-button { flex: 1; height: 38px; }
.provider-card-catalog .provider-probe .panel-note:empty { display: none; }
/* 文本那一类：地址 · 密钥 · 模型类型（带测试连接）一行三格。 */
.provider-card-catalog[data-family="text"] .provider-card-conn { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.3fr); }
.provider-card-catalog .provider-card-interface {
  display: grid;
  grid-template-columns: auto minmax(140px, 220px) 1fr;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.provider-billing-label { color: var(--muted); font-size: 13px; justify-self: end; }
.provider-cost-source {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.provider-cost-source-copy { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; min-width: 0; flex: 1; }
.provider-cost-source-copy strong { font-size: 14px; }
.provider-cost-source-status { color: var(--muted); font-size: 13px; }
.provider-cost-source.has-book .provider-cost-source-status { color: var(--green); }
.provider-cost-source-url { grid-column: 1 / -1; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
.provider-cost-source > button { flex: none; min-height: 36px; }
.provider-discount-panel { border-bottom: 1px solid var(--line); padding-bottom: 12px; }
.provider-discount-panel > summary { cursor: pointer; font-size: 14px; line-height: 1.8; }
.provider-discount-panel > summary > span:first-child { margin-right: 14px; font-weight: 600; }
.provider-discount-badges { display: inline-flex; flex-wrap: wrap; gap: 6px; color: var(--muted); font-size: 12px; vertical-align: middle; }
.provider-discount-badges > span { border: 1px solid var(--line); border-radius: 4px; padding: 1px 8px; color: var(--accent-text); }
.provider-discount-panel .provider-discounts { margin-top: 12px; }
.provider-discount-panel .provider-discounts > .text-pane-subtitle { display: none; }
.provider-discount-panel .provider-discounts > .panel-note { max-width: 900px; }
.provider-model-section { min-width: 0; }
.provider-model-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 4px 0 12px; }
.provider-model-section-head .text-pane-subtitle { margin: 0; font-size: 16px; }
.provider-model-section-head .panel-note { margin: 0; white-space: nowrap; }
.provider-card-catalog .provider-model-list { gap: 14px; margin: 0; }
.provider-card-catalog .provider-model-card { padding: 16px; }
.provider-model-icon { width: 34px; height: 34px; flex: none; padding: 8px; box-sizing: border-box; border-radius: 8px; background: var(--accent-strong); }
.provider-model-title { flex: 1; min-width: 0; font-size: 16px; line-height: 1.4; overflow-wrap: anywhere; }
.provider-card-catalog .provider-model-fields { margin-top: 12px; }
.provider-card-catalog .provider-model-fields .field > span { font-size: 12px; }
.provider-card-catalog .provider-model-fields select { font-size: 12px; }
.provider-card-catalog .provider-model-prices {
  --pricing-cols: minmax(40px, .65fr) minmax(44px, .8fr) minmax(54px, 1fr) minmax(54px, 1fr) minmax(40px, .65fr);
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.provider-card-catalog .provider-model-prices .pricing-row { gap: 6px; padding: 7px 0; }
.provider-card-catalog .provider-model-prices .pricing-row-head { font-size: 12px; padding-top: 0; }
.provider-card-catalog .provider-model-prices .pricing-input { font-size: 13px; height: 32px; }
.provider-card-catalog .provider-model-prices .pricing-margin { font-size: 12px; }
.provider-card-catalog .provider-model-add { align-self: start; min-height: 34px; margin-top: 12px; }
.provider-card-catalog .provider-card-byok { padding-top: 12px; border-top: 1px solid var(--line); margin: 0; }
/* 清单一列、卡片整行宽：宽的时候分步模型排四列、模型小卡排三列，不然一格拉得老长。 */
@container (min-width: 1000px) {
  /* 多一个类压过文件后面那条 .provider-card .text-pane-stages（两列）。 */
  .provider-card.provider-card-catalog .text-pane-stages { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .provider-card-catalog .provider-model-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@container (max-width: 900px) {
  .provider-card-catalog[data-family="text"] .provider-card-conn,
  .provider-card-catalog[data-family="text"] .provider-card-names { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .provider-card-catalog[data-family="text"] .provider-card-conn > .field:last-child { grid-column: 1 / -1; }
}
@container (max-width: 740px) {
  .provider-card-catalog .provider-model-list { grid-template-columns: minmax(0, 1fr); }
  .provider-card-catalog .provider-card-conn,
  .provider-card-catalog .provider-card-names { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .provider-card-catalog .provider-probe { grid-column: 1 / -1; justify-self: end; min-width: 132px; }
}
@container (max-width: 620px) {
  .provider-card-catalog > .provider-card-head { flex-wrap: wrap; }
  .provider-card-catalog .provider-card-summary { display: none; }
  .provider-card-catalog .provider-rank { min-width: 0; }
  .provider-card-catalog .provider-cost-source { flex-wrap: wrap; gap: 12px; }
  .provider-card-catalog .provider-cost-source-copy { flex-basis: 100%; }
}
@media (max-width: 600px) {
  .provider-card-catalog > .provider-card-body { padding: 12px; }
  .provider-card-catalog .provider-card-head { flex-wrap: wrap; }
  .provider-card-catalog .provider-card-names { flex-basis: 100%; grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .provider-card-catalog .provider-card-conn { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .provider-card-catalog[data-family="text"] .provider-card-conn { grid-template-columns: minmax(0, 1fr); }
  .provider-card-catalog .provider-probe { grid-column: auto; justify-self: stretch; }
  .provider-card-catalog .provider-card-interface { grid-template-columns: auto minmax(0, 1fr); padding: 10px 0; gap: 8px; }
  .provider-billing-label { grid-column: 2; justify-self: start; }
  .provider-cost-source { flex-wrap: wrap; gap: 12px; }
  .provider-cost-source-copy { flex-basis: 100%; grid-template-columns: minmax(0, 1fr); }
  .provider-cost-source-url { grid-column: auto; }
  .provider-card-catalog .provider-model-card { padding: 10px; }
  .provider-model-card-head { flex-wrap: wrap; }
  .provider-model-title { font-size: 14px; }
  .provider-model-default-pick { margin-left: 0; }
  .provider-card-catalog .provider-model-prices .pricing-row-head { font-size: 11px; }
  .provider-discount-panel .provider-discount-row { grid-template-columns: minmax(0, 1fr); }
  .provider-discount-panel .provider-discount-tiers { grid-column: auto; flex-wrap: wrap; }
}
/* 卡片是 flex 列（.console-model-group），被拉高之后多出来的空间自然落在
   最下面——内容照旧从顶上排，不用再写 justify-content。 */

/* 控制台视频区块里的小说明 */
.model-group-note { margin: 8px 0 2px; color: var(--muted); font-size: 12px; line-height: 1.5; }
/* 页签标题行里的「重新生成」小按钮：低存在感的小药丸，靠标题右边 */
#scriptView .script-list-head { display: flex; align-items: center; gap: 12px; }
#mainView .script-regen, .script-regen { display: inline-flex; align-items: center; gap: 6px; min-height: 0; padding: 4px 12px; border: 0; border-radius: 999px; background: rgba(255, 255, 255, .08); color: var(--muted); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; }
/* 浅色：基础那条带 #mainView 前缀，特异性更高，原来这里只写 .script-regen 永远输给它。 */
:root:not([data-theme="dark"]) #mainView .script-regen, :root:not([data-theme="dark"]) .script-regen { background: var(--surface-soft); }
.script-regen:disabled { opacity: .5; cursor: not-allowed; }
/* 底部的生成按钮和左上角画风药丸一样的全圆角 */
/* 胶囊形的生成按钮里的字不加粗（用户 2026-09-12）：.generate-command 那份 650 是给方角那几颗的，这里收回。 */
#scriptView .script-main-foot .generate-command { min-width: 0; width: auto; padding-left: 22px; padding-right: 22px; justify-content: center; border-radius: 999px; font-weight: 400; }
/* 单集正文：编辑器把左栏撑满，正文框自己滚；右侧助手不动。 */
#scriptView .script-main > .script-episode { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; }
#scriptView .script-episode .episode-script { flex: 1 1 auto; min-height: 0; }
#scriptView .script-episode .episode-script-body { flex: 1 1 auto; min-height: 0; }
#scriptView .script-side { position: static; height: 100%; min-height: 0; }
#scriptView .script-assistant-panel { height: 100%; min-height: 0; }
/* 剧本创作页照分镜生成页的骨架（用户 2026-09-14：「剧本创作改成和分镜生成一样，Minimo 助手作为独立的区域」）：
   外面不再套一张大卡，左栏内容和右栏助手各是独立的一块，落在页面底上，中间留和分镜页一样的 10px。 */
#scriptPageBody { box-sizing: border-box; min-height: 100%; padding: 0; border-radius: 0; background: transparent; }
@media (max-width: 800px) { #scriptPageBody { padding: 12px; } }
#scriptView .script-title-block,
#scriptView .script-list-hint,
#scriptView .script-episode-row,
#scriptView .script-list-foot,
#scriptView .script-settings,
#scriptView .script-empty,
#scriptView .script-episode-plan,
#scriptView .script-episode .episode-script,
#scriptView .script-genre { background: var(--surface-soft); }
#scriptView .script-episode-row:hover { background: var(--surface-hover); }

/* 「新建项目」弹窗：加宽，顶部一条两段式切换。
   高度固定：上传剧本和 AI 创作两页内容高矮不一，切换时弹窗不该跟着跳，多出来的部分在页内滚。 */
.new-work-dialog { width: min(880px, calc(100vw - 32px)); height: min(640px, calc(100vh - 48px)); }
.new-work-dialog { flex-direction: column; }
.new-work-dialog[open] { display: flex; }
.new-work-dialog > .dialog-header, .new-work-dialog > .new-work-modes, .new-work-dialog > .dialog-footer { flex: none; }
.new-work-dialog > #newWorkUploadPane, .new-work-dialog > .new-work-ai { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.new-work-header { align-items: center; }
.new-work-header h2 { margin: 0; font-size: 22px; font-weight: 700; }
/* 标题左边那个圆角方框里的文件夹图标 */
.new-work-heading { display: flex; align-items: center; gap: 12px; min-width: 0; }
.new-work-heading-icon {
  flex: none; display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid color-mix(in srgb, var(--green) 45%, transparent);
  border-radius: var(--radius-md); background: color-mix(in srgb, var(--green) 12%, transparent);
  color: var(--green);
}
/* 两列等宽：项目名和风格设定并排，窄屏折成一列。 */
.new-work-fields { display: grid; grid-template-columns: 1fr 1fr; align-items: start; gap: 16px; padding: 16px 20px 8px; }
@media (max-width: 720px) { .new-work-fields { grid-template-columns: 1fr; } }
.new-work-dialog .new-work-fields input { min-height: 44px; }
/* 底栏跟内容之间拉一道线，和参考稿一致 */
.new-work-dialog > .dialog-footer { border-top: 1px solid var(--line); }
.new-work-modes { margin: 16px 20px 6px; }
.new-work-mode { display: flex; flex: 1; align-items: center; justify-content: center; gap: 8px; padding: 10px 12px; font-size: 15px; }
.new-work-mode-icon { flex: none; }
/* 「上传剧本」后面那枚 i（和首页创作卡同一个 CopyrightInfo，2026-10-11）：跟着页签的字色、淡一点，选中那颗压在滑块上也看得见 */
.new-work-mode .home-create-tab-info { color: inherit; opacity: .6; }
.new-work-mode .home-create-tab-info:hover { color: inherit; opacity: 1; }
.new-work-ai { display: flex; flex-direction: column; gap: 12px; padding: 12px 20px 8px; }
.new-work-style-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.new-work-style { display: grid; gap: 3px; padding: 10px 12px; border: 0; border-radius: 10px; background: var(--surface-soft); color: var(--text); text-align: left; font: inherit; cursor: pointer; }
.new-work-style small { color: var(--muted); font-size: 12px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 画面风格总表里的风格卡只有选中样式、没有悬浮样式（用户 2026-09-24），新建项目那几张卡照旧。 */
.new-work-style:not(.visual-style-option):hover:not(.active) { background: var(--surface-hover); }
.new-work-style.active { background: var(--green-soft); color: var(--green); }
.new-work-style.more:not(.active) strong::after { content: " ›"; color: var(--muted); }
/* 「更多」弹窗：整张画面风格总表，色块 + 名字 + 一句描述。 */
/* 带 [open] 提高优先级，压过 .new-work-dialog 的定高：这个弹窗按内容取高，最多到视口。
   <dialog> 是 position: fixed + inset: 0，height: auto 会被上下 inset 撑满，所以要 fit-content。 */
/* 居中显示（用户 2026-09-12：钉在顶上「偏高了」，要居中）。高度按内容取、最多 84vh；
   切类别时整块不能上下跳，所以打开时 script.js 的 lockVisualStyleDialogHeight 量出几个类别里最高的那一档、
   把高度锁成那个数（内联 height），条目少的类别栅格顶上对齐、底下留空，不定死 620 那种拉变形的数。 */
.visual-style-dialog, .visual-style-dialog[open] { width: min(760px, calc(100vw - 32px)); height: fit-content; max-height: 84vh; margin: auto; display: flex; flex-direction: column; }
.visual-style-dialog:not([open]) { display: none; }

/* 加资产那三个弹窗的尺寸也得写在这儿：上面 .new-work-dialog 的 880×640 在文件更后面，
   写在前面会被它压掉——一个「名字 + 一张图」的表单撑成 880 宽 640 高，底下空一大片。
   这三个都按内容取高。 */
.new-asset-dialog, .new-asset-dialog[open] {
  width: min(520px, calc(100vw - 32px));
  height: fit-content; max-height: calc(100vh - 48px);
  display: flex; flex-direction: column;
}
.new-asset-dialog:not([open]) { display: none; }

.asset-source-dialog, .asset-source-dialog[open] {
  width: min(600px, calc(100vw - 32px));
  height: fit-content; max-height: calc(100vh - 48px);
  display: flex; flex-direction: column;
}
.asset-source-dialog:not([open]) { display: none; }

/* 导入是横版面板：宽 1280 定高 680，永远是宽大于高的一块。
   不能再写 fit-content——资产一多它就顺着长高，变成一块竖条；
   定高之后多出来的资产在网格里滚，弹窗形状不跟着资产数量变。 */
.asset-import-dialog, .asset-import-dialog[open] {
  width: min(1280px, calc(100vw - 32px));
  height: min(680px, calc(100vh - 48px));
  display: flex; flex-direction: column;
}
.asset-import-dialog > .dialog-footer { flex: none; }
.asset-import-dialog:not([open]) { display: none; }
.visual-style-grid { display: grid; flex: 1 1 auto; align-content: start; min-height: 0; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; padding: 16px 20px 20px; overflow-y: auto; }
.visual-style-option { grid-template-columns: 34px minmax(0, 1fr); grid-template-rows: auto auto; column-gap: 10px; row-gap: 2px; align-items: center; }
.visual-style-option .visual-style-swatch { grid-row: 1 / span 2; display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 8px; color: #fff; font-weight: 700; }
.visual-style-option small { white-space: normal; }
/* 新表的风格名本身就是那句描述，卡片里没有 small 那一行，两行栅格就收成一行，
   否则名字会被顶到上半格去。 */
.visual-style-option:not(:has(small)) { grid-template-rows: auto; }
/* 类别页签：真人 / 3D / 2D，2026-09-12 起是全站那份滑轨页签（颜色、形状、滑块全走共用块，
   别在这儿写槽色滑块色）。这里只管摆位：离顶上那条分割线 14px，按字定宽。 */
.visual-style-tabs { flex: none; width: fit-content; margin: 14px 20px 0; }
.visual-style-tab { padding: 7px 16px; }
.visual-style-popover .visual-style-tabs { margin: 10px 10px 0; }
@media (max-width: 800px) { .visual-style-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.new-work-style.active small { color: var(--green); opacity: .85; }
/* 弹窗高度固定，梗概框不让拖：拖大了只会把自己顶出可视区。 */
.new-work-brief { width: 100%; box-sizing: border-box; min-height: 160px; padding: 12px 14px; border: 0; border-radius: 10px; background: var(--surface-soft); color: var(--text); font: inherit; line-height: 1.6; resize: none; }
@media (max-width: 800px) { .new-work-style-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.new-work-preview { margin: 0 20px; padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); color: var(--muted); font-size: var(--font-base); line-height: 1.6; display: grid; gap: 4px; }
.new-work-preview.warn { border-color: color-mix(in srgb, var(--warn, #d9a441) 45%, transparent); }
.new-work-preview-title { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--text); font-weight: 600; }
/* 集数和字数比周围亮一档：这一行用户只扫这两个数。 */
.new-work-preview-title b { color: var(--green); font-weight: 700; }
/* 打勾的圆点：纯 CSS 画，勾是一个旋转 45 度的直角。 */
.new-work-preview-mark { flex: none; position: relative; width: 18px; height: 18px; border-radius: 50%; background: var(--green); }
.new-work-preview-mark::after {
  content: ""; position: absolute; left: 6px; top: 3px; width: 4px; height: 8px;
  border: solid var(--surface); border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.new-work-preview.warn .new-work-preview-mark { background: var(--warn, #d9a441); }
.new-work-preview-warning { margin: 0; color: var(--warn, #d9a441); }
.new-work-preview-note { margin: 0; }
/* 小说预检那行里的「改编成 N 集」输入框：嵌在句子里，别撑成一整行。 */
.new-work-novel-episodes { width: 4.2em; height: 28px; padding: 0 6px; margin: 0 2px; text-align: center; }
/* 和下面的风格卡、梗概框同一左右边线：父级 .new-work-ai 已经有 20px 内边距，这里不再另加。 */
.new-work-name-field { margin: 0; }
.new-work-name-field input { width: 100%; box-sizing: border-box; }
/* ---- 新建项目 · 上传区 ----
   一块虚线框，中间一颗按钮加一行说明；文件拖进来时整块高亮。传完换成文件卡片。
   颜色全走变量，深浅两套主题共用这一份。 */
/* 整块框就是上传入口：不摆按钮，框里任意一处可点，所以整块给 cursor 和焦点环。
   选过文件之后也还能点——再点是换一份，所以卡片和提示上下叠着一起显示。 */
.new-work-drop {
  margin: 18px 20px 4px; padding: 26px 20px; min-height: 200px; box-sizing: border-box;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-lg); background: var(--surface-alt);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  text-align: center; cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.new-work-drop.dragging { border-color: var(--accent); background: var(--accent-soft); }
/* role=button 的 div 吃不到 .new-work-dialog button:focus-visible 那条，单独给一个焦点环。 */
.new-work-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 正在上传：框整块不响应，光标也别再装成可点。 */
.new-work-drop.busy { opacity: .6; pointer-events: none; cursor: default; }
.new-work-drop-idle { display: grid; justify-items: center; gap: 6px; }
.new-work-drop-title { margin: 0; color: var(--muted); font-size: 15px; }
/* 那行格式说明要在一行里读完（弹窗有 880px），别折成两行把框撑高。 */
.new-work-drop-hint { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.6; opacity: .75; white-space: nowrap; }
@media (max-width: 720px) { .new-work-drop-hint { white-space: normal; } }

/* ---- 框里那张文件卡片：类型角标 + 文件名 / 大小 + 一个叉 ---- */
.new-work-file {
  display: flex; align-items: center; gap: 12px; max-width: 100%;
  padding: 12px 10px 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); text-align: left;
}
.new-work-file-type {
  flex: none; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px;
  background: var(--accent); color: #ffffff; font-size: 12px; font-weight: 700; letter-spacing: .04em;
}
.new-work-file-meta { min-width: 0; display: grid; gap: 2px; }
.new-work-file-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 15px; font-weight: 600; }
.new-work-file-size { color: var(--muted); font-size: 13px; }
.new-work-file-clear {
  flex: none; width: 28px; height: 28px; min-height: 0; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--muted); font-size: 18px; line-height: 1; cursor: pointer;
}
/* 页面骨架：左栏列表 / 正文，右栏设定面板，右栏吸顶。 */
.script-page-body { display: flex; flex-direction: column; gap: 14px; }
.script-layout { display: grid; grid-template-columns: minmax(0, 1fr) 540px; gap: 10px; align-items: start; }
.script-main { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.script-side { position: sticky; top: 0; min-width: 0; }
@media (max-width: 1180px) { .script-layout { grid-template-columns: minmax(0, 1fr); } .script-side { position: static; } }
/* 剧本创作页顶部那条运行横幅撤了（2026-09-13）：进度 / 已用时 / 停止在助手消息尾部的 .assistant-progress。 */
@keyframes script-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.script-back { flex: none; padding: 6px 10px; border: 0; border-radius: 8px; background: var(--surface-soft); }
.script-back:hover { background: var(--surface-hover); }
.script-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 22px 24px; border-radius: 12px; background: var(--surface); }
.script-empty h2 { margin: 0; font-size: 20px; }
.script-empty p { margin: 0; max-width: 640px; line-height: 1.6; }
.script-empty .works-subhead { margin-top: 10px; }
.script-empty .works-grid { width: 100%; padding-bottom: 0; }

/* 左栏顶部：剧名、标签、简介 */
.script-title-block { display: flex; flex-direction: column; gap: 10px; padding: 16px 18px; border-radius: 12px; background: var(--surface); }
.script-title-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.script-title-row h2 { margin: 0; font-size: 22px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.script-title-edit { flex: none; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 8px; background: var(--surface-soft); color: var(--muted); font-size: 14px; cursor: pointer; }
.script-title-edit:hover { background: var(--surface-hover); color: var(--text); }
.script-subtitle { color: var(--muted); font-size: var(--font-base); }
.script-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.script-tag { padding: 2px 9px; border-radius: 6px; background: var(--surface-soft); color: var(--muted); font-size: var(--font-base); font-variant-numeric: tabular-nums; }
.script-intro { position: relative; display: flex; flex-direction: column; gap: 8px; padding-top: 4px; }
.script-intro-text { margin: 0; padding-right: 64px; color: var(--muted); font-size: var(--font-base); line-height: 1.6; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.script-intro-text strong { color: var(--text); font-weight: 600; }
.script-intro.expanded .script-intro-text { display: block; -webkit-line-clamp: unset; }
.script-cast { display: none; flex-wrap: wrap; gap: 6px; }
.script-intro.expanded .script-cast { display: flex; }
.script-person { padding: 2px 9px; border-radius: 6px; background: var(--surface-soft); font-size: var(--font-base); }
.script-intro-toggle { position: absolute; right: 0; top: 4px; padding: 2px 6px; border: 0; background: transparent; color: var(--muted); font: inherit; font-size: var(--font-base); cursor: pointer; }
.script-intro-toggle:hover { color: var(--text); }

/* 左栏：剧集列表 */
.script-episodes { display: flex; flex-direction: column; gap: 10px; }
.script-list-head { margin: 4px 0 0; font-size: 16px; font-weight: 600; }
.script-list-hint { margin: 0; padding: 12px 14px; border-radius: 10px; background: var(--surface); color: var(--muted); font-size: var(--font-base); line-height: 1.6; }
.script-episode-table { display: flex; flex-direction: column; gap: 6px; }
/* 最后一列原来是 32px 的「···」，现在是「编辑正文」，得放得下四个字。 */
.script-episode-row { position: relative; display: grid; grid-template-columns: 18px 22px 64px minmax(80px, 150px) minmax(0, 1fr) 84px 96px; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 10px; background: var(--surface); box-shadow: inset 0 0 0 1px transparent; cursor: pointer; font-size: var(--font-base); }
.script-episode-row:hover { background: var(--surface-hover); }
.script-episode-row.selected { box-shadow: inset 0 0 0 1px var(--green); }
.script-episode-row input { margin: 0; accent-color: var(--green); }
.script-episode-index { color: var(--muted); font-variant-numeric: tabular-nums; text-align: right; }
.script-episode-num { white-space: nowrap; }
.script-episode-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.script-episode-beats { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.script-episode-status { color: var(--muted); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.script-episode-status.done { color: var(--success); }
.episode-nav .long { color: var(--warn, #d9a441); }
.script-style-current { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; padding: 8px 12px; border-radius: 10px; background: var(--surface); color: var(--muted); font-size: var(--font-base); }
.script-style-current .link-command { padding: 0; border: 0; background: none; color: var(--accent, #9d8cff); cursor: pointer; font-size: inherit; }
.studio-empty-reload { flex-direction: column; gap: 12px; }
.studio-empty-reload strong { color: var(--text); font-weight: 600; }
/* 行尾那颗「编辑正文」。不写 :hover——全站按钮悬浮/聚焦/选中都是同一个样式，
   唯一的反馈是按下缩小（全局 button:active）。
   它是跳转（进这一集的正文页），2026-09-12 起和「去生成图片」一个待遇：生成类那一身 + 跳转图标。 */
.script-episode-edit { display: inline-flex; align-items: center; justify-content: center; padding: 5px 10px; border: 0; border-radius: 8px; background: var(--command-create-bg); color: var(--command-create-text); font: inherit; white-space: nowrap; cursor: pointer; }
@media (max-width: 900px) { .script-episode-row { grid-template-columns: 18px 22px 64px minmax(0, 1fr) 84px 96px; } .script-episode-beats { display: none; } }
.script-list-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding: 12px 14px; border-radius: 10px; background: var(--surface); font-size: var(--font-base); }
.script-select-all { display: flex; flex: none; align-items: center; gap: 6px; white-space: nowrap; cursor: pointer; }
.script-select-all input { margin: 0; accent-color: var(--green); }
.script-list-stats { display: flex; gap: 18px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* 可用后会多出积分价签；固定外框，价签出现不能把按钮宽高撑大。 */
#scriptView .script-list-foot .generate-command {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 190px;
  min-width: 190px;
  height: 36px;
  min-height: 36px;
  padding: 0 18px;
}
.script-list-foot .script-send { margin-left: auto; }

/* 右栏：设定面板 */
.script-settings { display: flex; flex-direction: column; border-radius: 12px; background: var(--surface); }
/* 这里原来还有一套 .script-tabs / .script-tab：方角、选中换成绿底、悬浮还换背景。
   页签早就改成上面那份滑轨了，这套没有任何地方在用——留着的问题不是多几行 CSS，
   是它违反「悬浮不换样式」那条规矩，被照抄出去就又飘一处。已删。 */
/* 面板贴着视口顶部吸住，高度不超过视口：正文在中间滚，「保存设置 / 生成剧本设定」始终钉在底部。 */
.script-settings { max-height: calc((100vh - var(--top-banner-height, 0px)) - 138px); }
/* 左栏最上面两枚浅底药丸：左边只有画风名字，右边是集数 / 秒 / 比例三个直接填的框。 */
.script-spec-row { display: flex; align-self: flex-start; align-items: center; gap: 10px; }
.script-spec-pill { display: inline-flex; align-items: center; box-sizing: border-box; height: 36px; min-height: 0; padding: 0 14px; border: 0; border-radius: 999px; background: rgba(255, 255, 255, .08); color: var(--text); font: inherit; font-size: 14px; font-weight: 600; }
:root:not([data-theme="dark"]) .script-spec-pill { background: rgba(0, 0, 0, .05); }
#mainView .script-spec-visual, .script-spec-visual { gap: 6px; cursor: pointer; white-space: nowrap; }
/* 药丸右侧的下箭头：点开时翻上去 */
.spec-chevron { display: inline-flex; align-items: center; color: var(--muted); transition: transform .15s ease; }
.open > .spec-chevron { transform: rotate(180deg); }
.script-spec-visual:disabled { opacity: .6; cursor: not-allowed; }
.script-spec-inputs { gap: 0; padding: 0 8px; }
.script-spec-input { position: relative; display: inline-flex; align-items: center; gap: 4px; padding: 0 10px; }
.script-spec-input + .script-spec-input::before { content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 1px; background: rgba(255, 255, 255, .12); }
.script-spec-input span { color: var(--muted); font-weight: 500; }
#mainView .script-spec-input input, .script-spec-input input { min-height: 0; margin: 0; padding: 3px 4px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); font: inherit; font-size: 14px; font-weight: 600; text-align: center; }
#mainView .script-spec-input input:focus, .script-spec-input input:focus { outline: none; }
/* 浅色：分隔线原来是半透明白，浅色底上看不见。 */
:root:not([data-theme="dark"]) .script-spec-input + .script-spec-input::before { background: var(--line); }
.script-spec-input input:disabled { opacity: .6; }
/* 画面比例：药丸里的第三段是按钮，点开下面的竖排选项 */
#mainView .script-spec-ratio-button, .script-spec-ratio-button { display: inline-flex; align-items: center; gap: 6px; min-height: 0; padding: 3px 4px; border: 0; border-radius: 6px; background: transparent; color: var(--text); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
/* 画风是药丸里的第一段。名字比集数长，给它一个上限省略，别把药丸撑出屏幕。 */
.script-spec-visual .script-spec-ratio-button { max-width: 15em; }
.script-spec-visual .script-spec-ratio-button > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.script-spec-visual .visual-style-popover { top: calc(100% + 10px); left: -8px; }
.script-spec-ratio-button:disabled { opacity: .6; cursor: not-allowed; }
/* 药丸锁住（不在剧本创作里）：同一颗按钮、同样宽，点不开。别按「禁用」那档变淡——它不是不可用，是在别处改；
   但要换个颜色（用户 2026-09-24：「药丸栏不可点击的时候，要换个颜色」）：值和下箭头一起退到 --muted，一眼看出这里点不了。 */
.script-spec-input.locked input:disabled { opacity: 1; cursor: default; color: var(--muted); }
.script-spec-ratio-button.locked { cursor: default; color: var(--muted); }
/* 视频台直出模式（2026-09-15）：预览格没有首帧，写清按什么出；切镜摘要小字、最多几行。 */
/* 直出那格：标题 + 副标题钉在上方，下面是完整的镜头描述，多了在格内滚（用户 2026-09-15）。 */
.ratio-popover { position: absolute; z-index: 6; top: 40px; left: 0; display: flex; flex-direction: column; gap: 2px; min-width: 232px; padding: 8px; border-radius: 14px; background: #303030; box-shadow: 0 12px 36px rgba(0, 0, 0, .35); }
:root:not([data-theme="dark"]) .ratio-popover { background: #ffffff; box-shadow: 0 12px 36px rgba(0, 0, 0, .18); }
/* 每一行：取景框占 34px 的槽（居中），比例文字，右边勾 */
#mainView .ratio-option, .ratio-option { display: grid; grid-template-columns: 34px 1fr auto; align-items: center; column-gap: 10px; min-height: 0; padding: 9px 12px; border: 0; border-radius: 10px; background: transparent; color: var(--text); font: inherit; font-size: 15px; font-weight: 500; text-align: left; white-space: nowrap; cursor: pointer; }
.ratio-option.active { font-weight: 600; }
.ratio-option .ratio-frame { box-sizing: border-box; justify-self: center; border: 2px solid currentColor; border-radius: 4px; }
.ratio-option-check { display: inline-flex; margin-left: 8px; }
/* 叙事方式那颗的下拉借的是画面比例这一身，可前面没有比例小框：照三列排，字挤进 34 宽的第一列、勾落在第二列正好压住字
   （2026-10-03 用户报的：「后面的勾选的icon和文字重叠了」）。只排「字 | 勾」两列，和分镜页那两个下拉一样。 */
#mainView .script-spec-narrative .ratio-option, .script-spec-narrative .ratio-option { grid-template-columns: 1fr auto; }
/* 画风浮层：钉在画风药丸下面、左对齐，内容同新建项目里的总表弹窗。 */
.visual-style-popover { position: absolute; z-index: 6; top: 44px; left: 0; width: min(720px, calc(100vw - 300px)); max-height: 60vh; border-radius: 12px; background: #303030; box-shadow: 0 12px 36px rgba(0, 0, 0, .35); overflow-y: auto; }
:root:not([data-theme="dark"]) .visual-style-popover { background: #ffffff; box-shadow: 0 12px 36px rgba(0, 0, 0, .18); }
.visual-style-popover .visual-style-grid { padding: 10px; }

/* 简介下面的三个页签：形状和颜色走上面那份「滑轨页签」，这里没有额外的。 */

/* 剧本设定页签 */
.script-outline, .script-beats { display: flex; flex-direction: column; gap: 12px; }
.script-outline-meta { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 14px; margin: 0; padding: 14px 18px; border-radius: 12px; background: var(--surface); font-size: var(--font-base); line-height: 1.6; }
.script-outline-meta dt { color: var(--muted); font-weight: 650; }
.script-outline-meta dd { margin: 0; color: var(--text); }
/* 剧本简介：和上面那张设定卡同一档底色，整段可换行 */
.script-synopsis { margin: 0; padding: 14px 18px; border-radius: 12px; background: var(--surface); font-size: var(--font-base); line-height: 1.8; white-space: pre-wrap; }
.script-cast-list { display: flex; flex-direction: column; gap: 10px; }
.script-person-card { display: flex; flex-direction: column; gap: 5px; padding: 14px 18px; border-radius: 12px; background: var(--surface); font-size: var(--font-base); line-height: 1.7; }
.script-person-card h4 { margin: 0 0 4px; font-size: 16px; font-weight: 700; }
.script-person-card p { margin: 0; color: var(--text); }
.script-person-card p strong { color: var(--muted); font-weight: 650; }
.script-outline-foot { display: flex; justify-content: flex-end; }

/* 分集大纲页签 */
.script-beat-card { display: flex; flex-direction: column; gap: 4px; padding: 12px 16px; border-radius: 12px; background: var(--surface); font-size: var(--font-base); line-height: 1.6; }
.script-beat-card h4 { margin: 0 0 2px; font-size: 15px; }
.script-beat-card p { margin: 0; color: var(--text); }
.script-beat-card p strong { color: var(--muted); font-weight: 650; }
.script-beat-open { align-self: flex-start; min-height: 0; margin-top: 6px; padding: 5px 10px; border: 1px solid var(--line); border-radius: 8px; background: transparent; color: var(--text); font: inherit; font-size: 12px; cursor: pointer; }
.script-beat-open:disabled { opacity: .5; cursor: default; }

/* ---- 短剧助手 ---- */
/* 整个对话栏就是一整块浅色底，里面不再套第二层色块。 */
.script-assistant-panel { display: flex; flex-direction: column; height: calc((100vh - var(--top-banner-height, 0px)) - 138px); min-height: 480px; border: 0; border-radius: 14px; background: rgba(255, 255, 255, .06); box-shadow: none; overflow: hidden; }
:root:not([data-theme="dark"]) .script-assistant-panel { background: rgba(0, 0, 0, .04); }
.assistant-head { border-bottom-color: rgba(255, 255, 255, .08); }
.assistant-head { display: flex; flex: none; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.assistant-brand { display: flex; align-items: center; gap: 10px; font-size: 15px; }
.assistant-logo { width: 26px; height: 26px; border-radius: 50%; background: var(--accent); box-shadow: inset 0 0 0 6px var(--surface), inset 0 0 0 8px var(--accent); }
.assistant-tools { display: flex; gap: 6px; }
.assistant-tool { min-height: 0; padding: 5px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font: inherit; font-size: var(--font-base); cursor: pointer; }
.assistant-tool:hover { background: var(--surface-soft); color: var(--text); }
/* 历史记录：钉在「历史记录」按钮下面的一个弹窗，不挤开对话内容。 */
.script-assistant-panel { position: relative; }
.assistant-history { position: absolute; z-index: 5; top: 52px; right: 12px; display: flex; flex-direction: column; gap: 6px; width: 320px; max-height: 320px; padding: 8px; border-radius: 12px; background: #303030; box-shadow: 0 12px 36px rgba(0, 0, 0, .35); overflow-y: auto; }
:root:not([data-theme="dark"]) .assistant-history { background: #ffffff; box-shadow: 0 12px 36px rgba(0, 0, 0, .18); }
.assistant-history-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.assistant-history-head .assistant-history-item { flex: 1; }
.assistant-history-manage { flex: none; min-height: 0; padding: 5px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer; }
.assistant-history-manage.active { background: var(--accent-soft); color: var(--accent-text); }
.assistant-history-row { position: relative; display: flex; }
.assistant-history-row .assistant-history-item { flex: 1; min-width: 0; }
/* 管理态：每条右上角一个红叉（用户给的图标资源），点了删掉这条对话 */
.assistant-history-remove { position: absolute; top: -4px; right: -4px; z-index: 1; width: 20px; height: 20px; min-height: 0; padding: 0; border: 0; border-radius: 50%; background: url("/static/icon-close.png") no-repeat center / contain; cursor: pointer; }
.assistant-history-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 0; padding: 9px 12px; border: 0; border-radius: 10px; background: rgba(255, 255, 255, .06); color: var(--text); font: inherit; font-size: var(--font-base); text-align: left; cursor: pointer; }
:root:not([data-theme="dark"]) .assistant-history-item { background: rgba(0, 0, 0, .04); }
.assistant-history-head .assistant-history-item.new { background: transparent; }
.assistant-history-item span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assistant-history-item small { flex: none; color: var(--muted); font-size: 12px; }
.assistant-history-item.active { background: var(--accent-soft); }
.assistant-history-item.new { color: var(--accent-text); font-weight: 600; }
.assistant-stream { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; gap: 14px; padding: 16px; overflow-y: auto; }
/* 助手说的话要能框选复制：body 全局关掉了选中（拖选表格会整片变蓝），这里单独开回来。
   气泡里的按钮不跟着开，免得拖过去把按钮上的字也选进去。 */
.assistant-stream { user-select: text; -webkit-user-select: text; caret-color: auto; }
.assistant-stream button { user-select: none; -webkit-user-select: none; }
.assistant-message { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; max-width: 100%; }
.assistant-message.user { align-items: flex-end; }
.assistant-bubble { max-width: 92%; color: var(--text); font-size: 14px; line-height: 1.7; }
.assistant-message.user .assistant-bubble { padding: 10px 16px; border-radius: 16px 16px 4px 16px; background: var(--accent-soft); color: var(--text); }
.assistant-bubble p { margin: 0 0 8px; white-space: pre-wrap; }
.assistant-bubble p:last-child { margin-bottom: 0; }
.assistant-bubble ul { margin: 0 0 8px; padding-left: 18px; }
.assistant-bubble li { margin: 2px 0; }
.assistant-applied { padding: 3px 10px; border-radius: 999px; background: var(--success-soft); color: var(--success); font-size: 12px; }
/* 助手那几颗胶囊形的生成按钮字不加粗（用户 2026-09-12：「生成类的胶囊按钮，里面的文字不要加粗」）。 */
.assistant-action { min-height: 0; padding: 10px 20px; border: 0; border-radius: 14px; background: var(--command-create-bg); color: var(--command-create-text); font: inherit; font-size: 14px; font-weight: 400; cursor: pointer; }
/* 按钮不做悬浮样式：悬浮 / 未选中 / 选中 / 聚焦同一个样子，反馈只有按下缩小。 */
.assistant-action:hover { filter: none; }
.assistant-typing { flex-direction: row; gap: 5px; padding: 6px 2px; }
.assistant-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); animation: assistant-typing 1.2s ease-in-out infinite; }
.assistant-typing span:nth-child(2) { animation-delay: .2s; }
.assistant-typing span:nth-child(3) { animation-delay: .4s; }
@keyframes assistant-typing { 0%, 100% { opacity: .25; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-3px); } }
.assistant-error { margin: 0; color: #e05252; font-size: var(--font-base); }
.assistant-chips { display: flex; flex: none; flex-wrap: wrap; gap: 8px; padding: 0 16px 10px; }
/* 用户 2026-09-14：「对话框上方的药丸按钮可以小一点，现在太大了」——只收字号和内边距，排版不动（原来 40px 高，现在约 30px）。 */
.assistant-chip { display: inline-flex; align-items: center; gap: 5px; min-height: 0; padding: 5px 11px; border: 1px solid transparent; border-radius: 999px; background: var(--command-create-bg); color: var(--command-create-text); font: inherit; font-size: 13px; font-weight: 400; cursor: pointer; }
.assistant-chip .cost-chip { margin-left: 2px; padding: 0 6px 0 4px; font-size: 12px; }
.assistant-chip .cost-chip::before { width: 10px; height: 10px; }
/* 悬浮不换色（用户原话「不要悬浮颜色，保持固定的颜色样式」），照全站按钮规矩只有按下缩小。
   悬浮态要显式写回默认底色：全局那条 button:hover 会给它换底。 */
.assistant-chip:hover:not(:disabled),
.assistant-chip:focus-visible { background: var(--command-create-bg); border-color: transparent; color: var(--command-create-text); }
.assistant-chip:active:not(:disabled) { transform: scale(.97); }
.assistant-chip:disabled { opacity: .5; cursor: not-allowed; }
.assistant-composer { flex: none; margin: 0 12px 12px; padding: 10px 12px 8px; border-radius: 14px; background: transparent; border: 1px solid rgba(255, 255, 255, .1); }
/* 浅色主题：上面那道 10% 的白边压在浅灰的助手栏上等于没有（用户 2026-10-02：「浅色模式下，Minimo助手的输入框看不清边框」），换成全站浅色的深一档线色 */
:root:not([data-theme="dark"]) .assistant-composer { border-color: var(--line-strong); }
.assistant-input { width: 100%; box-sizing: border-box; min-height: 88px; padding: 4px 2px; border: 0; background: transparent; color: var(--text); font: inherit; font-size: 14px; line-height: 1.6; resize: none; }
.assistant-input:focus { outline: none; }
.assistant-composer-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.assistant-composer-hint { color: var(--muted); font-size: 12px; }
/* 额度用完那句要看得见：这时输入框已经禁掉了，一行灰字说不清为什么。 */
.assistant-composer-hint.spent { color: var(--red); font-weight: 600; }
.assistant-send { width: 34px; height: 34px; min-height: 0; padding: 0; border: 0; border-radius: 50%; background: url("/static/icon-send.png") no-repeat center / contain; color: transparent; font-size: 0; cursor: pointer; }
.assistant-send:disabled { opacity: .35; cursor: not-allowed; }
/* 发送键忙着时变停止（用户 2026-09-14）：同一颗圆底（和发送图标同色），中间直接画一个黑色圆角方块，不用图片；停止中灰掉。 */
.assistant-send.stop { display: inline-flex; align-items: center; justify-content: center; background: #1296db; }
.assistant-send.stop::after { content: ""; width: 12px; height: 12px; border-radius: 3px; background: #000; }
/* 浅色主题方块改白（用户 2026-09-14） */
:root:not([data-theme="dark"]) .assistant-send.stop::after { background: #fff; }
@media (max-width: 1180px) { .script-assistant-panel { height: auto; min-height: 420px; } }
.script-settings-body { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; gap: 14px; padding: 16px 16px 10px; overflow-y: auto; }
.script-basic, .script-assistant { display: flex; flex-direction: column; gap: 14px; }
.script-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.script-field-label { color: var(--muted); font-size: var(--font-base); font-weight: 650; }
.script-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.script-field-row.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* 输入框要和右侧设置面板分得开：面板是 surface，输入框在它上面再亮一层；聚焦时显示一圈边框而不是外描边。 */
.script-field input, .script-field select, .script-field textarea, .script-custom-style { width: 100%; box-sizing: border-box; padding: 8px 11px; border: 1px solid transparent; border-radius: 8px; background: var(--script-field-bg, rgba(255, 255, 255, .06)); color: var(--text); font: inherit; transition: border-color .15s; }
:root:not([data-theme="dark"]) .script-field input, :root:not([data-theme="dark"]) .script-field select, :root:not([data-theme="dark"]) .script-field textarea, :root:not([data-theme="dark"]) .script-custom-style { background: var(--surface-soft); }
.script-field textarea, .script-custom-style { resize: vertical; min-height: 64px; line-height: 1.55; }
/* 带 #mainView 是为了压过它那条「所有输入框 border-color 透明」的规则。 */
#mainView .script-field input:focus, #mainView .script-field select:focus, #mainView .script-field textarea:focus, #mainView .script-custom-style:focus,
.script-field input:focus, .script-field select:focus, .script-field textarea:focus, .script-custom-style:focus { outline: none; }
.script-textarea { position: relative; }
.script-textarea textarea { padding-bottom: 22px; }
.script-counter { position: absolute; right: 10px; bottom: 6px; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.script-genres { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-height: 40px; padding: 6px 8px; border-radius: 8px; background: var(--surface-soft); }
.script-genre { display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px 2px 9px; border-radius: 6px; background: var(--surface); font-size: var(--font-base); }
.script-genre button { width: 18px; height: 18px; padding: 0; border: 0; border-radius: 4px; background: transparent; color: var(--muted); font: inherit; cursor: pointer; }
.script-genre button:hover { background: var(--surface-soft); color: var(--text); }
.script-field .script-genre-input { flex: 1; min-width: 80px; padding: 4px 6px; background: transparent; }
.script-field .script-genre-input:focus { outline: none; }
.script-genre-suggest { display: flex; flex-wrap: wrap; gap: 6px; }
.script-genre-suggest button { padding: 2px 8px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer; }
.script-genre-suggest button:hover { background: var(--surface-soft); color: var(--text); }
.script-visual { display: flex; flex-direction: column; gap: 8px; }
.script-visual-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.script-visual-card { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 6px; border: 0; border-radius: 10px; background: var(--surface-soft); color: var(--muted); font: inherit; font-size: var(--font-base); cursor: pointer; box-shadow: inset 0 0 0 1px transparent; }
.script-visual-card:hover { color: var(--text); }
.script-visual-card.active { color: var(--green); box-shadow: inset 0 0 0 1px var(--green); background: var(--green-soft); }
.script-visual-thumb { display: grid; place-items: center; width: 100%; aspect-ratio: 4 / 3; border-radius: 8px; color: rgba(255, 255, 255, .85); font-size: 20px; font-weight: 700; }
.script-field .script-visual-text { font-size: 12px; }
.script-settings-foot { display: grid; flex: none; grid-template-columns: 1fr 1.4fr; gap: 10px; padding: 12px 16px 16px; border-top: 1px solid var(--line); }
.script-settings-foot button { padding: 10px 12px; }

/* 创作助手 */
.script-style-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.script-style-card { display: grid; gap: 3px; padding: 9px 11px; border: 0; border-radius: 10px; background: var(--surface-soft); color: var(--text); text-align: left; font: inherit; cursor: pointer; }
.script-style-card small { color: var(--muted); font-size: 12px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.script-style-card:hover:not(.active) { background: var(--surface-hover); }
.script-style-card.active { background: var(--green-soft); color: var(--green); }
.script-style-card.active small { color: var(--green); opacity: .85; }
.script-custom-style { margin-top: 8px; }
.script-assist-actions, .script-rewrite-presets { display: flex; flex-wrap: wrap; gap: 8px; }
.script-rewrite-presets button { padding: 6px 10px; border: 0; border-radius: 8px; background: var(--surface-soft); color: var(--text); font: inherit; cursor: pointer; }
.script-rewrite-presets button:hover:not(:disabled) { background: var(--surface-hover); }
.script-history-list { display: flex; flex-direction: column; gap: 6px; }
.script-history-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 10px; border-radius: 8px; background: var(--surface-soft); font-size: var(--font-base); }

/* 单集正文 */
.script-episode { display: flex; flex-direction: column; gap: 12px; }
.script-episode-head { display: flex; align-items: center; gap: 12px; }
/* 「第 N 集」+ 集名 + 一支改名笔。名字长了省略号收在名字上，别把笔挤没了。 */
.script-episode-head h2 { flex: 1; min-width: 0; margin: 0; display: flex; align-items: center; gap: 6px; font-size: 20px; }
.script-episode-head h2 .script-episode-number { flex: none; }
.script-episode-head h2 .rename-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.script-episode-head h2 .rename-pen { flex: none; margin-left: 0; }
/* 全局的 input{width:100%} 会让改名框把整行撑满，一直顶到「上一集」跟前。
   集名没那么长，给个能缩的固定宽度就够。 */
.script-episode-head h2 .rename-input { flex: 0 1 320px; width: auto; }
.script-episode-nav { display: flex; gap: 6px; }
.script-episode-plan { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; border-radius: 10px; background: var(--surface); font-size: var(--font-base); }
.script-episode-plan:empty { display: none; }
.script-episode-plan p { margin: 0; color: var(--muted); line-height: 1.55; }
.script-episode-plan p strong { color: var(--text); font-weight: 600; }
.script-episode .episode-script { padding: 12px; border-radius: 12px; background: var(--surface); }
.script-episode .episode-script-body { min-height: 460px; border: 0; border-radius: 8px; background: var(--surface-soft); }
/* 单集正文页：整页只有编辑器，宽度铺满，高度撑到视口底。 */
.script-layout.single { grid-template-columns: minmax(0, 1fr); }

.script-episode .episode-script-foot { flex-wrap: wrap; }

.episode-pill.written { border-color: var(--green); color: var(--green); }

/* 顶部模式条：一条导轨槽 + 一个滑块。卡片本身透明，选中态由滑块画出来。
   圆角跟着滑轨页签那套走，一律药丸（用户要的「相同的圆角」）。 */
.mode-switch { position: relative; gap: 2px; padding: 3px; border: 0; border-radius: 999px; background: rgba(0, 0, 0, .28); }
.mode-switch .mode-card,
.mode-switch .mode-card.active,
.mode-switch .mode-card:not(.active):hover:not(:disabled) { position: relative; z-index: 1; border: 1px solid transparent; border-radius: 999px; background: transparent; }
.mode-switch .mode-card:not(.active):hover:not(:disabled) { background: rgba(255, 255, 255, .06); }
.mode-thumb {
  position: absolute; top: 3px; bottom: 3px; left: 0; z-index: 0; width: 0; box-sizing: border-box;
  border: 1px solid var(--card-active); border-radius: 999px; background: var(--card-active);
  pointer-events: none;
  transition: transform .32s cubic-bezier(.22, .61, .36, 1), width .32s cubic-bezier(.22, .61, .36, 1);
}
.mode-switch:not(.thumb-ready) .mode-thumb { transition: none; }
@media (prefers-reduced-motion: reduce) { .mode-thumb { transition: none; } }

/* ======================================================================
   主题层：工作台跟着登录页的视觉走——深色、色块分层、紫→粉的强调色。
   这里只引用上面的语义化变量，改主题改变量就行，别在别处再写死颜色。
   ====================================================================== */

/* 控件：色块、统一圆角，不描边。 */
button, .button-link {
  min-height: var(--control-height);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--surface-soft);
  color: var(--text);
}
button:focus-visible, .button-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 自动弹出来的框把焦点收在框自己身上（app.js 的 showModalQuietly）：框本身不画焦点环。 */
dialog:focus, dialog:focus-visible { outline: none; }
/* 主按钮默认就是跟着主题走的纯色：深色白底黑字、浅色黑底白字，不渐变、不发光
   （用户 2026-09-27：「已经不需要这个全站通用的按钮了，现在默认就是白底黑字，或者黑底白字，根据深浅的主题切换」）。
   原来这里是青蓝渐变加一圈青色投影，各页面一处处压成纯色，没压到的新页面（3D 导演台）就露出了渐变。
   悬浮不换样式，只有按下缩小。 */
.primary-command,
.primary-command:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-create-bg);
  color: var(--command-create-text);
  box-shadow: none;
  filter: none;
}
.danger-command { border-color: transparent; background: var(--red-soft); color: var(--red); }
.danger-command:hover:not(:disabled) { border-color: transparent; background: var(--red-soft); filter: brightness(1.1); }

input, select, textarea {
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--input-bg);
}
/* 文本框、下拉框聚焦不高亮（用户 2026-10-10：「文本框和下拉框都不需要高亮，你检查一下整个项目，都去掉」）：边框、底色、阴影一律照旧，不画外框线（浏览器自带的那圈也压掉），光标就是焦点；可编辑的文字框（contenteditable）同样。
   再写输入框别加 :focus 的边框色 / 外框线 / 光圈 / 底色变亮，:focus-within、:has(input:focus) 包着的那层也别加。复选框、单选框、滑块照旧画焦点框（下一条） */
input:not(:where([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"])):focus, select:focus, textarea:focus, :where([contenteditable]:not([contenteditable="false"])):focus { outline: none; }
input:where([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"]):focus { border-color: transparent; outline: 2px solid var(--accent); outline-offset: 0; box-shadow: none; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }
input[type="checkbox"]:focus-visible { outline-color: var(--accent); }

/* 页面底就是 --bg（深色 #141414）一整块：原来铺过两团很淡的光斑，用户 2026-09-15 要「整体的背景颜色统一，不要搞渐变」。 */
body.web-mode { background: var(--bg); }
.app-header { background: transparent; }

/* 顶部导轨：选中态用强调色。（侧栏的选中、悬浮、滑块颜色 2026-10-08 起只在 .sidebar-item / .sidebar-thumb 那几条里写） */
.mode-switch .mode-card { --mode-accent: var(--accent); }
.mode-thumb { border-color: rgba(0, 194, 255, .35); background: var(--accent-soft); }

/* 卡片、抽屉、弹窗：统一圆角与玻璃边线。 */
.work-card { border-color: var(--glass-line); border-radius: var(--radius-lg); box-shadow: none; }
.work-card-new { border: 1px dashed var(--line-strong); }
.work-new-plus { color: var(--accent); }
.asset-drawer-body { border-left-color: var(--glass-line); background: var(--surface); }
dialog { border: 1px solid var(--glass-line); border-radius: var(--radius-xl); box-shadow: var(--shadow-2); }
.new-work-style.active, .script-style-card.active, .script-tab.active, .script-visual-card.active { background: var(--accent-soft); color: var(--accent-text); }
.new-work-style.active small, .script-style-card.active small { color: var(--accent-text); }
.script-visual-card.active { box-shadow: inset 0 0 0 1px var(--accent); }
.script-episode-row.selected { box-shadow: inset 0 0 0 1px var(--accent); }
.script-episode .episode-script-body:focus, .episode-script-body:focus { outline: none; }
.script-back { background: var(--surface-soft); }
/* .new-work-modes 不在这串里：它是滑轨，圆角走共用那份的 999px，别被方角规则改掉。 */
.script-genres, .new-work-style, .script-style-card, .script-visual-card, .new-work-brief { border-radius: var(--radius-md); }

/* 剧本创作页的生成类按钮：和工作台其它生成按钮同一种颜色（白底黑字、浅色反过来），不用渐变；别的按钮就是普通色块。 */
.generate-command,
.generate-command:hover:not(:disabled) {
  border-color: transparent;
  background: var(--command-create-bg);
  color: var(--command-create-text);
  /* 生成类按钮的字不加粗（用户 2026-09-12）。 */
  font-weight: 400;
}
.generate-command:hover:not(:disabled) { filter: none; }
.generate-command:active:not(:disabled) { translate: 0 1px; scale: .98; }

/* 剧本创作设置面板脚上的「保存设置」「生成剧本设定」：两颗都是和图片页「全部生成」
   一样的绿色实心块——不做一灰一绿那种像"没选中"的样子；平时 / 悬浮 / 聚焦一个样，只有按下缩小。 */
.script-settings-foot button,
.script-settings-foot button:hover:not(:disabled),
.script-settings-foot button:focus,
.script-settings-foot button:focus-visible {
  border-color: transparent;
  /* 颜色跟着生成类走（2026-09-12 起白底黑字、浅色反过来），「保存设置」陪着一起换，两颗照旧同一身。 */
  background: var(--command-create-bg);
  color: var(--command-create-text);
  filter: none;
  outline: none;
  box-shadow: none;
  transform: scale(1);
  transition: transform .08s ease;
}
.script-settings-foot .generate-command,
.script-settings-foot .generate-command:hover:not(:disabled),
.script-settings-foot .generate-command:focus,
.script-settings-foot .generate-command:focus-visible {
  background: var(--command-create-bg);
  color: var(--command-create-text);
}
.script-settings-foot button:active:not(:disabled) { translate: 0 1px; scale: .98; }

/* 顶部模式导轨：深灰凹槽里嵌一块浅灰滑块，未选中的项没有底色（悬浮也没有）。
   状态点空闲时统一灰色，只有运行 / 失败 / 暂停这些需要提醒的状态才带颜色。
   空闲时不显示进度条，只有在跑的时候才亮出进度。
   四个颜色写成这条上的局部变量，别散在下面各处。2026-09-10 用户定的那套（槽 #202124、
   选中块 #393B3F、未选中字 #8B9099）带一点蓝，整站深色改成纯中性灰之后它成了顶栏上唯一偏蓝的
   一块，2026-09-12 用户点头换成中性灰：槽和面板同档、选中块照参考图、未选中字浅灰。
   浅色主题另有一套，紧跟在这条下面，按同样的明暗关系翻过来。
   形状一律胶囊，和全站滑轨页签一致。 */
.mode-switch {
  --mode-rail: #212121;
  --mode-thumb: #363636;
  --mode-text: #F2F2F2;
  --mode-text-muted: #9a9a9a;
  gap: 0;
  padding: 6px;
  border: 0;
  border-radius: 999px;
  background: var(--mode-rail);
  box-shadow: none;
}
/* 浅色主题（用户 2026-09-10：「切换为浅色后，顶部的4个按钮的颜色没变，要适配各自的主题」）。
   顶栏是透明的，导轨直接压在浅色页面上，照搬深色那四个值就是页面正中一块黑。
   按深色那套的明暗关系翻过来：槽比页面深一档、滑块比槽亮、选中的字最深、未选中的字中灰。
   白滑块和浅灰槽离得近，补一层很淡的投影才分得出来；进度槽和焦点框原来是半透明白，浅底上看不见，也翻成黑。 */
:root:not([data-theme="dark"]) .mode-switch {
  --mode-rail: #E8EAED;
  --mode-thumb: #FFFFFF;
  --mode-text: #202124;
  --mode-text-muted: #5F6368;
}
:root:not([data-theme="dark"]) .mode-thumb { box-shadow: 0 1px 3px rgba(32, 33, 36, .16); }
:root:not([data-theme="dark"]) .mode-switch .mode-card-progress { background: rgba(0, 0, 0, .1); }
:root:not([data-theme="dark"]) .mode-switch .mode-card:focus-visible { outline-color: rgba(0, 0, 0, .45); }
.mode-switch .mode-card { height: 62px; padding: 6px 10px; border-radius: 999px; color: var(--mode-text-muted); grid-template-rows: auto auto; row-gap: 5px; align-content: center; }
.mode-switch .mode-card:not(.active):hover:not(:disabled) { background: transparent; }
/* 模式卡之间不画分隔竖线，只靠滑块标出选中的那张。 */
.mode-switch .mode-card::before { content: none; }
/* 滑块和侧栏选中块一个做法：平实的半透明底色，不描边不发光。 */
.mode-thumb {
  top: 6px; bottom: 6px;
  border: 0;
  border-radius: 999px;
  background: var(--mode-thumb);
  box-shadow: none;
  transition: transform .32s cubic-bezier(.22, .61, .36, 1);
  will-change: transform;
  backface-visibility: hidden;
}
/* 字色跟着卡片走（未选中 --mode-text-muted、选中 --mode-text），图标是 currentColor 的
   遮罩，跟着一起变。所以这儿不写死颜色，只管字号。 */
.mode-card-title { font-size: 15px; color: inherit; }
.mode-switch .mode-card.active .mode-card-title { color: var(--mode-text); }
/* 这里只管几何，填充的渐变在上面那条通用规则里，两处别各写一份。
   **槽的底色必须是半透明的白，不能写死色值。** 这条槽压在两种底上：没选中的那几张
   压在导轨上，选中的那张压在滑块上，两者亮度不一样。写死一个值必然在其中一边不对——
   换成 #242b35 之后选中那张的槽就明显发黑。半透明白压在哪层就把哪层压暗一档，
   在两种底上都是同一个「凹槽」的观感。 */
.mode-switch .mode-card-progress { position: absolute; left: 12px; right: 12px; bottom: 5px; height: 4px; background: rgba(255, 255, 255, .14); }
.mode-switch .mode-card:focus-visible { outline-color: rgba(255, 255, 255, .6); }

/* 侧栏 logo 和品牌字都不发光。logo 没有底色方块，所以也别给它描边——
   那会在一枚没有底的标外面画一个方框。 */
.sidebar-head { position: relative; }
.sidebar-head::before { content: none; }
.sidebar-logo { box-shadow: none; }
:root:not([data-theme="dark"]) .sidebar-logo { background-color: var(--text); }
.sidebar-brand { color: #ffffff; text-shadow: none; }
:root:not([data-theme="dark"]) .sidebar-brand { color: var(--text); text-shadow: none; }

/* 选择项目页 + 新建项目弹窗：去掉紫色，跟顶部导轨一样走黑白玻璃风。
   （侧栏选中这里原来写过白字、半透明白的滑块；2026-10-08 方案一「统一横栏」起选中是强调色浅底，只在 .sidebar-thumb 那几条里写，别再加回灰滑块：
   和悬浮的灰色分不清，选中首页、鼠标停在我的项目上两行都像选中了） */
.work-card { border-color: rgba(255, 255, 255, .06); }
.work-card:not(.work-card-new):focus-visible { outline-color: rgba(255, 255, 255, .5); }
.work-card-new { border: 1px dashed rgba(255, 255, 255, .18); }
/* 项目卡不做悬浮态：原来悬浮那套（底和边都是 --surface-hover，看着整块变亮、没有描边）
   直接当成常态，鼠标放上去不再变。新建那张虚线卡也一样不动。 */
.work-card:not(.work-card-new) { background: var(--surface-hover); border-color: var(--surface-hover); }
.work-new-plus { color: #ffffff; }
.new-work-style.active { background: rgba(255, 255, 255, .12); color: #ffffff; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22); }
.new-work-style.active small { color: rgba(255, 255, 255, .7); }
.new-work-dialog button:focus-visible, #worksPage button:focus-visible { outline-color: rgba(255, 255, 255, .6); }
.new-work-dialog .primary-command, #worksPage .primary-command,
.new-work-dialog .primary-command:hover:not(:disabled), #worksPage .primary-command:hover:not(:disabled) {
  background: #f2f2f2; color: #111418; box-shadow: 0 0 0 1px rgba(255, 255, 255, .18), 0 0 18px rgba(255, 255, 255, .14);
}
.new-work-dialog .primary-command:hover:not(:disabled), #worksPage .primary-command:hover:not(:disabled) { filter: none; background: #ffffff; }
:root:not([data-theme="dark"]) .new-work-dialog .primary-command, :root:not([data-theme="dark"]) #worksPage .primary-command { background: #202124; color: #ffffff; box-shadow: none; }
/* 「开始创作」是这个弹窗的落点：生成类那一身（深色白底黑字、浅色黑底白字，用户 2026-09-12 定的），
   跟着主题切换走，不再用渐变。 */
.new-work-dialog .dialog-footer .primary-command,
.new-work-dialog .dialog-footer .primary-command:hover:not(:disabled) {
  background: var(--command-create-bg); color: var(--command-create-text); box-shadow: none;
}
.new-work-dialog .dialog-footer .primary-command:disabled { opacity: .48; }
/* ==== 浅色标准模板 ====
   用户 2026-09-10 定的：「套餐订阅中的这一套浅色系搭配非常好，作为浅色系的标准颜色模板」。
   从外到里一层比一层深，选中最深：
     页面底                      --bg               #f4f5f5
     卡片 / 面板                 --surface          #ffffff   描边 --line #d7dcda
     卡里的条目、输入框、次级按钮 --surface-soft     #f0f2f1   描边 --line
     悬浮、选中                  --tab-rail-strong  #e4e9e7   （选中另加强调色描边的照旧）
     掏钱 / 落点主按钮           #202124 底、#FFFFFF 字
   字用 --text / --muted。只管浅色主题，深色主题一个值不动。
   已经套上的：套餐订阅、我的项目、工作台（剧本创作 / 分镜生成 / 图片生成 / 视频生成、资产库抽屉）、新建项目那几个弹窗。
   **新做浅色页面照这五档配，不要再发明新的一档。**

   工作台原来比模板多两档近白的：--surface-alt #fafbfb（几乎就是白）、--surface-hover #f8faf9（悬浮和选中用它，
   白底上根本看不出来）。在这几个页面里并进模板：近白那档并回卡片白，悬浮选中那档换成 --tab-rail-strong。 */
:root:not([data-theme="dark"]) :is(#worksPage, #mainView, .new-work-dialog) {
  --surface-alt: var(--surface);
  --surface-hover: var(--tab-rail-strong);
}
/* 编辑中的输入区原来拿 --surface-hover 当底，并进模板后打字时整块变成悬浮那档灰；改用条目那档。 */

/* 我的项目：面板是模板里的卡片（白底 + 描边），项目卡是卡里的条目（--surface-soft + 描边），
   和套餐订阅里积分档位同一个层级。原来项目卡是 --surface-hover，白面板上几乎看不出卡片边界。 */
:root:not([data-theme="dark"]) .works-panel { box-shadow: 0 0 0 1px var(--line); }
:root:not([data-theme="dark"]) .work-card:not(.work-card-new) { background: var(--surface-soft); border-color: var(--line); }
:root:not([data-theme="dark"]) .work-card-new { background: var(--surface-soft); border-color: var(--line-strong); }
:root:not([data-theme="dark"]) .work-card:not(.work-card-new):focus-visible { outline-color: rgba(24, 32, 29, .45); }
:root:not([data-theme="dark"]) .work-new-plus { color: var(--text); }
/* 新建项目选风格：原来这条选择器里 :root 写了两遍，永远匹配不上，浅色下选中的那张是白字压在近乎透明的底上。 */
:root:not([data-theme="dark"]) .new-work-style.active { background: var(--tab-rail-strong); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
:root:not([data-theme="dark"]) .new-work-style.active small { color: var(--muted); }
/* 焦点框原来是半透明白，浅色底上看不见。 */
:root:not([data-theme="dark"]) .new-work-dialog button:focus-visible,
:root:not([data-theme="dark"]) #worksPage button:focus-visible { outline-color: rgba(24, 32, 29, .45); }

/* ---- 过期标记 / 警告条 / 进度 / 上传世界观（工作流审查修复） ---- */
/* 剧本创作页：运行条已用时、常驻警告条、分集行上的「缺大纲 / 未写出 / 大纲已更新」标记 */
.script-run-elapsed { flex: none; margin-right: 8px; font-variant-numeric: tabular-nums; opacity: .85; }
/* 剧本创作页顶部那条常驻警告条撤了（2026-09-14）：提醒在助手消息的「提醒」列表里。 */
.script-episode-flag { display: inline-flex; align-items: center; gap: 4px; min-height: 0; margin-right: 8px; padding: 1px 8px; border: 1px solid currentColor; border-radius: 999px; background: transparent; font: inherit; font-size: 12px; line-height: 1.5; cursor: pointer; }
.script-episode-flag.missing { color: var(--red); }
.script-episode-flag.stale { color: var(--warn, #d9a441); }
.script-episode-flag:disabled { opacity: .5; cursor: not-allowed; }
.script-episode-status.missing { color: var(--red); }
/* 正在写 / 改的那几集：勾选态留着，状态格标「生成中」。 */
.script-episode-status.running { color: var(--accent-text); }
.script-beat-card .script-episode-flag { align-self: flex-start; margin: 0 0 4px; }
/* 助手：发送失败的那条；思考中的秒表。
   （这里原来还有一条「过了阶段就把 action 降级成灰字」的样式，已经去掉——
   助手给的 action 一律是能点的按钮，摆一颗点不动的出来只会让人以为界面坏了。） */
.assistant-failed { align-self: flex-end; color: var(--red); font-size: 12px; }
.assistant-message.failed .assistant-bubble { opacity: .6; }
/* 按钮留下的一问一答（kind=auto）：用户那句（「请生成第 1 集的分镜」）和他亲手打的话同一个气泡色（用户 2026-09-16：
   「气泡的颜色也要和用户主动发的消息一个颜色」），原来那档灰底和淡一档撤了；助手那句照旧淡一档。在跑的那条尾部一行进度。 */
.assistant-message.auto.assistant .assistant-bubble { opacity: .78; font-size: 13px; }
.assistant-progress { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; width: 100%; font-size: 12px; color: var(--muted); }
.assistant-progress > span::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: var(--muted); animation: assistant-typing 1.2s ease-in-out infinite; }
/* 已用时那截是 <small>，浏览器默认给它 font-size: smaller，和前半句差一号（用户 2026-09-16：「为什么字体大小不一」）：跟着整行走。 */
.assistant-progress small { font-size: inherit; font-variant-numeric: tabular-nums; }
.assistant-typing-elapsed { margin-left: 8px; color: var(--muted); font-size: 12px; }
/* 助手的选择题卡片：助手要动已有成果时先让人勾范围，勾完点「提交」。
   选项是单选/多选控件，不是操作按钮——选中态必须看得出来（描边 + 右侧对勾），
   但一样不做悬浮换色，按下缩小走全局那条 button:active。 */
.assistant-questions { align-self: stretch; width: 100%; display: flex; flex-direction: column; padding: 14px; border-radius: var(--radius-lg); background: var(--surface-soft); }
.assistant-questions-head { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 0; padding: 0 0 12px; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.assistant-questions-spark { flex: none; color: var(--accent-text); font-size: 13px; }
.assistant-questions-head strong { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 14px; font-weight: 650; }
.assistant-questions-chevron { flex: none; width: 8px; height: 8px; margin: -3px 3px 0 0; border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted); transform: rotate(45deg); }
.assistant-questions-head[aria-expanded="false"] .assistant-questions-chevron { margin-top: 3px; transform: rotate(225deg); }
.assistant-questions-body { display: flex; flex-direction: column; gap: 18px; padding-top: 14px; }
.assistant-question { display: flex; flex-direction: column; gap: 8px; }
.assistant-question-title { margin: 0; font-size: 14px; font-weight: 650; line-height: 1.5; }
.assistant-options { display: flex; flex-direction: column; gap: 8px; }
/* 选项按钮（用户 2026-09-13）：文字自动换行（全局 button 是 nowrap），选中态是一圈外圈高亮，按下不缩
   （顶部 button:active 的排除名单里有它）——它是勾选项，不是操作按钮。 */
.assistant-option { display: flex; align-items: flex-start; gap: 10px; width: 100%; min-height: 0; padding: 12px 14px; border: 1px solid transparent; border-radius: 12px; background: var(--surface); color: var(--text); font: inherit; text-align: left; white-space: normal; cursor: pointer; }
.assistant-option.chosen { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
/* 已提交 / 历史卡片是只读的，但要照常看得清：不吃全局 button:disabled 那层半透明。 */
.assistant-option:disabled { opacity: 1; cursor: default; }
.assistant-option-dot { flex: none; width: 5px; height: 5px; margin-top: 8px; border-radius: 50%; background: var(--muted); }
.assistant-option-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.assistant-option-text strong { font-size: 14px; font-weight: 650; line-height: 1.5; white-space: normal; overflow-wrap: anywhere; }
.assistant-option-text small { color: var(--muted); font-size: 13px; line-height: 1.5; white-space: normal; overflow-wrap: anywhere; }
.assistant-option-check { flex: none; align-self: center; color: var(--text); font-size: 14px; opacity: 0; }
.assistant-option.chosen .assistant-option-check { opacity: 1; }
.assistant-questions-submit { align-self: flex-start; margin-top: 14px; min-height: 0; padding: 10px 26px; border: 0; border-radius: 999px; background: var(--command-generate-bg); color: var(--command-generate-text); font: inherit; font-size: 14px; font-weight: 650; cursor: pointer; }
.assistant-questions-done { align-self: flex-start; margin-top: 14px; padding: 4px 12px; border-radius: 999px; background: var(--green-soft); color: var(--green); font-size: 12px; }
.assistant-questions-stale { align-self: flex-start; margin-top: 14px; padding: 4px 12px; border-radius: 999px; background: var(--surface); color: var(--muted); font-size: 12px; }

/* 人物变更确认卡（用户 2026-09-12）：外壳、标题、「已提交」「已过去」两枚小标签照搬选择题卡片。
   每一行是「新增 / 修改 / 删除」一枚标签 + 名字 + 一句明细，行本身不能点；删除那枚用红色，一眼分出会少掉谁。
   两颗按钮一主一次，悬浮不换色（按钮那条规矩）。 */
.assistant-character-change .assistant-questions-head { cursor: default; }
.assistant-change-count { flex: none; color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.assistant-change-rows { display: flex; flex-direction: column; gap: 8px; padding-top: 14px; }
.assistant-change-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 14px; border-radius: 12px; background: var(--surface); }
.assistant-change-kind { flex: none; margin-top: 1px; padding: 0 8px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-text); font-size: 12px; line-height: 20px; }
.assistant-change-kind.run { color: var(--accent); }
.assistant-change-kind.remove { background: var(--red-soft); color: var(--red); }
.assistant-change-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.assistant-change-text strong { font-size: 14px; font-weight: 650; line-height: 1.5; }
.assistant-change-text small { color: var(--muted); font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; }
/* 分镜表 / 资产改动的字段（2026-10-09 用户：「助手的回答居然看不完全」，原来每项截到 60 个字）：一个字段一块，
   短的一行「标题 旧 → 新」；长的「原」「改为」各起一行，切镜一刀一行、台词一句一行；改为那几行用正文色，一眼看出改成了什么 */
.assistant-change-fields { display: flex; flex-direction: column; gap: 8px; margin-top: 3px; color: var(--muted); font-size: 13px; line-height: 1.55; }
.assistant-change-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.assistant-change-field > b { color: var(--text); font-size: 12.5px; font-weight: 600; }
.assistant-change-field.short { flex-direction: row; align-items: baseline; gap: 10px; }
.assistant-change-field.short > b { flex: none; }
.assistant-change-line { min-width: 0; overflow-wrap: anywhere; }
.assistant-change-diff { display: grid; grid-template-columns: 2.4em minmax(0, 1fr); gap: 8px; }
.assistant-change-diff.bare { grid-template-columns: minmax(0, 1fr); }
.assistant-change-diff em { font-style: normal; opacity: .75; }
.assistant-change-diff > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
.assistant-change-diff.now > span { color: var(--text); }
.assistant-change-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.assistant-change-actions .assistant-questions-submit { margin-top: 0; }
.assistant-change-dismiss { min-height: 0; padding: 10px 22px; border: 0; border-radius: 999px; background: var(--surface); color: var(--text); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
/* 分镜页：过期横幅 + 重拆、分集列表的过期签、资产卡角标、按新画风重生成、资产提示词进度、已删除引用 */
.breakdown-stale-band { display: flex; align-items: center; gap: 12px; margin: 0 0 10px; padding: 9px 14px; border-radius: 10px; background: rgba(217, 164, 65, .14); color: var(--warn, #d9a441); font-size: var(--font-base); }
.breakdown-stale-text { flex: 1; min-width: 0; }
.breakdown-stale-redo { display: inline-flex; align-items: center; gap: 6px; flex: none; min-height: 0; padding: 5px 12px; border: 0; border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; cursor: pointer; }
.breakdown-stale-redo:disabled { opacity: .5; cursor: not-allowed; }
.episode-pill.stale { border-color: var(--warn, #d9a441); color: var(--warn, #d9a441); }
.asset-stale { margin-left: auto; padding: 1px 7px; border: 1px solid var(--warn, #d9a441); border-radius: 999px; color: var(--warn, #d9a441); font-size: 11px; white-space: nowrap; }
/* 从剧本设定里删掉、图还在的角色卡：灰色角标，不是警告。 */
.asset-dropped { border-color: var(--muted); color: var(--muted); }
.asset-stale-regenerate { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; min-height: 0; padding: 5px 12px; border: 0; border-radius: 8px; background: rgba(217, 164, 65, .14); color: var(--warn, #d9a441); font: inherit; font-size: var(--font-base); cursor: pointer; }
.asset-stale-regenerate:disabled { opacity: .5; cursor: not-allowed; }
.asset-run-progress { display: inline-flex; align-items: center; gap: 4px; margin-left: 8px; color: var(--green); font-size: var(--font-base); white-space: nowrap; font-variant-numeric: tabular-nums; }
.shot-refs-removed { display: block; color: var(--red); font-size: 11px; }
/* 视频台：首帧已更新角标、描述下面的常驻提醒 */
/* 「首帧已更新」：和时长药丸并排站在标题下面，前面挂一个转圈箭头。 */
.studio-shot-badge { display: inline-flex; align-items: center; gap: 4px; padding: 1px 9px; border: 1px solid var(--warn, #d9a441); border-radius: 999px; color: var(--warn, #d9a441); font-size: 11px; line-height: 17px; }
.studio-shot-badge-icon { width: 12px; height: 12px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* 生成方式那行提示：选了「用自己的密钥」却还没填，就在生成选项底下说一句。
   和镜头警告同一副底色——都是「现在还生成不了」这类事。 */

/* 角色智配：和「生成方式 / 视频模型」同一行。它按内容占宽、**从不压缩**——
   开关、名字、价钱、那颗 i 少一样都看不明白；两个下拉平分剩下的宽度，
   它们截短一点不影响读（选中的那项本来就在最前面）。 */
.studio-smart-fit {
  display: flex; align-items: center; gap: 6px; min-height: 32px;
  padding: 0 8px; border-radius: var(--radius-sm); background: var(--input-bg); font-size: var(--font-base);
  white-space: nowrap;
}
.studio-smart-fit-toggle { display: flex; flex: none; align-items: center; gap: 6px; cursor: pointer; }
/* 这一镜在生成时锁住，和旁边几个下拉 disabled 的观感对齐（select:disabled 也是 .7）。 */
.studio-smart-fit.disabled { opacity: .7; }
.studio-smart-fit.disabled .studio-smart-fit-toggle { cursor: default; }
/* 滑动开关：全局那条 input 规则会把勾选框撑成 41x36，这里整个接管外观。
   开 / 关一眼看得出来，比一个小方块清楚——这一格是要花钱的，不能含糊。

   **控制台里所有勾选框也走这一份**（`#consolePage input[type=checkbox]`），Minimo3D 的「自动去背景」也是（2026-09-25）：
   那一页全是全站开关，一个小方块看不出开还是关。加第三处就往下面这几串选择器里
   添一行，别另抄一份——抄一份的下场见「滑轨页签」那段。

   **控制台里不许有第二个地方定义勾选框的宽高。** 这条的特异性是 (1,1,1)：
   `.console-permission-item input[type=checkbox]` 那种 (0,2,1) 压不过它，写了也是死的；
   而 `#pricingPackageBody .xxx input[type=checkbox]` 那种 (1,2,1) 压得过——开关被压成
   16×16，圆点却照旧按 13px 位移，直接从右边缘挤出去压在旁边的文字上。
   「首购加赠」和「开放购买」两处都这么坏过。tests/console.test.js（原 text-providers 那一段）里有一条会
   扫整份样式表，谁再加一条就红。
   发票弹窗那条是例外：它在 #consolePage 外面，那儿的勾选框是原生小方块。 */
.m3d-switch-row input[type="checkbox"],
.studio-smart-fit input[type="checkbox"],
#consolePage input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  position: relative; flex: none; width: 30px; height: 17px; margin: 0; padding: 0;
  border: none; border-radius: 999px; background: var(--line-strong);
  cursor: pointer; transition: background .2s ease;
}
.m3d-switch-row input[type="checkbox"]::after,
.studio-smart-fit input[type="checkbox"]::after,
#consolePage input[type="checkbox"]::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  transition: transform .2s ease;
}
.m3d-switch-row input[type="checkbox"]:checked,
.studio-smart-fit input[type="checkbox"]:checked,
#consolePage input[type="checkbox"]:checked { background: var(--accent); }
.m3d-switch-row input[type="checkbox"]:checked::after,
.studio-smart-fit input[type="checkbox"]:checked::after,
#consolePage input[type="checkbox"]:checked::after { transform: translateX(13px); }
.m3d-switch-row input[type="checkbox"]:disabled,
.studio-smart-fit input[type="checkbox"]:disabled,
#consolePage input[type="checkbox"]:disabled { cursor: default; opacity: .6; }
/* 焦点提示落在开关本身，别再叠全局那圈方角 box-shadow（圆不了，看着像个方框套着滑块）。 */
.m3d-switch-row input[type="checkbox"]:focus,
.studio-smart-fit input[type="checkbox"]:focus,
#consolePage input[type="checkbox"]:focus { box-shadow: none; }
.m3d-switch-row input[type="checkbox"]:focus-visible,
.studio-smart-fit input[type="checkbox"]:focus-visible,
#consolePage input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.studio-smart-fit-name { flex: none; color: var(--text); white-space: nowrap; }
/* 这一行不再挂价签：同一次生成的钱摆在两处（开关后面一枚、按钮上一枚），
   用户得自己把两个数加起来。价钱并进「保存并生成视频」那一枚了。 */
/* 那颗 i：解释这功能是什么、怎么收费。悬浮提示走全站共用的 bindHoverTip，
   不用原生 title——它要等一秒才出来，样式也改不动（dom-ids 测试也钉着不许用）。 */
/* 全站的信息 i 只有这一份：角色智配旁边那枚、资产卡保存期旁边那枚共用。
   再加一处就把类名加进这串选择器，别另抄一份。 */
.studio-smart-fit-info,
.asset-retention-info {
  flex: none; display: grid; place-items: center; width: 15px; height: 15px;
  border: 1px solid var(--muted); border-radius: 50%;
  color: var(--muted); font-size: 10px; font-style: italic; font-weight: 700; line-height: 1;
  cursor: help;
}

/* 被判成真人的弹窗：一枚状态药丸 + 标题 + 两段说明 + 一张功能卡 + 这一镜的花费。
   读下来就知道发生了什么、能怎么办、要花多少。 */
.smart-fit-dialog { width: min(520px, calc(100vw - 32px)); }
.smart-fit-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 16px 18px 0; }
/* 「无法生成」：红药丸，左边一个感叹号。它说的是状态，不是分类，所以不复用 type-label。 */
/* 左上角这一枚说的是「钱没丢」，所以是绿的、图标是个勾。红色留给下面那句
   失败原因——一个弹窗里两处红字，眼睛不知道该看哪一处。 */
.smart-fit-badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px 5px 10px;
  border-radius: 999px; background: rgba(63, 184, 132, .16);
  color: var(--green); font-size: var(--font-base); font-weight: 600;
}
.smart-fit-badge::before {
  content: ""; flex: none; width: 14px; height: 14px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233fb884' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8 12.5l2.5 2.5L16 9.5'/%3E%3C/svg%3E") no-repeat center / contain;
}
.smart-fit-body { padding: 14px 18px 18px; }
.smart-fit-title { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; font-size: 21px; }
.smart-fit-warn {
  flex: none; width: 26px; height: 26px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23f0788a' d='M12 2.6c.7 0 1.35.37 1.7.98l8.1 14.02c.7 1.2-.17 2.7-1.7 2.7H3.9c-1.53 0-2.4-1.5-1.7-2.7L10.3 3.58A1.96 1.96 0 0 1 12 2.6Z'/%3E%3Cpath fill='%23fff' d='M11 8h2v6h-2zM11 15.5h2v2h-2z'/%3E%3C/svg%3E") no-repeat center / contain;
}
.smart-fit-roles { margin: 0 0 10px; color: var(--text); font-weight: 600; line-height: 1.6; }
.smart-fit-reason { margin: 0 0 12px; color: var(--muted); line-height: 1.8; }
.smart-fit-accent { color: var(--accent); font-weight: 600; }
/* 功能卡：左边一枚渐变图标，右边名字和一句话。 */
.smart-fit-card {
  display: flex; gap: 14px; margin-top: 16px; padding: 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-soft);
}
.smart-fit-card-icon {
  flex: none; width: 44px; height: 44px; border-radius: 12px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M13.4 3.2 15 7l3.8 1.6L15 10.2l-1.6 3.8-1.6-3.8L8 8.6 11.8 7Z'/%3E%3Cpath fill='%23fff' d='m7.4 12.8.9 2.2 2.2.9-2.2.9-.9 2.2-.9-2.2-2.2-.9 2.2-.9Z'/%3E%3C/svg%3E") no-repeat center / 30px 30px,
    linear-gradient(135deg, #0099e6, #2f7cff);
}
.smart-fit-card-text { min-width: 0; }
.smart-fit-card-text strong { display: block; margin-bottom: 5px; font-size: 15px; }
.smart-fit-card-text p { margin: 0; color: var(--muted); line-height: 1.7; }
/* 这一镜要花多少：跟在功能卡下面。原来压在弹窗最底下，读到按钮时早忘了。 */
.smart-fit-estimate { display: flex; align-items: center; gap: 4px; margin: 12px 0 0; color: var(--muted); }
.smart-fit-estimate-cost { display: inline-flex; align-items: center; gap: 3px; color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.smart-fit-estimate-cost::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
/* 主操作用渐变：这一颗是花钱的，和「我知道了」一眼分得开。
   悬浮 / 聚焦不换样式，只有按下缩小——和全站按钮一个规矩。 */
.smart-fit-dialog .smart-fit-enable {
  border-color: transparent;
  background: var(--gradient-primary);
  background-origin: border-box;
  color: #fff;
}
.smart-fit-dialog .smart-fit-enable:hover:not(:disabled),
.smart-fit-dialog .smart-fit-enable:focus-visible {
  border-color: transparent;
  background: var(--gradient-primary);
  background-origin: border-box;
}
.smart-fit-dialog .smart-fit-enable .cost-chip { background: rgba(0, 0, 0, .22); }

/* 上传对话框：画风按钮 + 题材 / 时代 */
.workflow-visual-field { display: flex; flex-direction: column; gap: 6px; }
/* 风格选择器长得像个下拉：右边那个角标是 CSS 画的箭头，点开的是画风总表弹窗。 */
.workflow-visual-button {
  position: relative; min-height: 44px; padding: 0 34px 0 14px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--input-bg); color: var(--muted);
  font: inherit; text-align: left; cursor: pointer; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.workflow-visual-button::after {
  content: ""; position: absolute; right: 14px; top: 50%; width: 7px; height: 7px;
  border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: translateY(-70%) rotate(45deg);
}
.workflow-visual-button.picked { color: var(--text); }
/* 必填星号 + 输入框右边的 14/50 计数 */
.field-required { color: var(--red); font-style: normal; }
.field-counted { position: relative; display: block; }
.field-counted input { width: 100%; box-sizing: border-box; padding-right: 62px; }
.field-count { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 13px; pointer-events: none; }

/* ---- 控制台：能填的框要看得出是框 ----
   末尾那段深色 UI 把全局 input/select/textarea 的边框改成了 transparent，底色又是
   --input-bg；控制台的卡片（.console-model-group）底色是 --surface-soft，深色下这两个
   都是 #1f1f1f——框和卡片糊成一片，模型配置和定价页看不出哪里能填。
   这里在控制台内统一：底色沉一档（--console-input-bg），边框用 --line-strong 描出来。 */
#consolePage input:not([type="checkbox"]):not([type="radio"]),
#consolePage select,
#consolePage textarea {
  border: 1px solid var(--console-input-line);
  background: var(--console-input-bg);
}
#consolePage input:not([type="checkbox"]):not([type="radio"])::placeholder,
#consolePage textarea::placeholder { color: var(--muted); }
#consolePage input:disabled, #consolePage select:disabled, #consolePage textarea:disabled { opacity: .6; }
/* 价格册算出来的成本格描一道强调色边，悬浮给算式；售价格标红 = 上游不出这一档却还在卖。
   带 #consolePage 是为了压过上面那条 input 的默认框线。 */
#consolePage input.pricing-input.pricing-cost-derived { border-color: var(--accent); }
#consolePage input.pricing-input.pricing-warn { border-color: var(--danger, #e07a6a); }

/* 「我的 API Key」那个输入框：框线常驻，不是点进去才显形。
   上面 `:is(#settingsPage, #consolePage) input` 把 border 抹成 0，那条带 id、
   比一般的类选择器压得住，所以这里也得带上 #settingsPage 并且更具体一档。 */
#settingsPage .settings-api-key .console-toolbar input:not([type="checkbox"]) {
  border: 1px solid var(--console-input-line);
}

/* ---- 控制台定价页：顶上的四步滑动切换 ----
   和剧本页那条页签同一套视觉（圆角槽 + 滑块），但这里是控制台自己的类名，
   免得两边的布局改动互相牵连。四步的配置全渲染出来，非当前步 hidden——
   切来切去不会把还没保存的输入弄丢。 */
.pricing-mode-switch {
  position: relative;
  display: flex;
  gap: 6px;
  margin: 0 0 16px;
  padding: 4px;
  border-radius: 999px;
  background: var(--surface-soft);
}
.pricing-mode-tab {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  padding: 8px 12px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: color .2s;
}
.pricing-mode-tab.active { background: transparent; color: var(--accent-text, var(--accent)); }
.pricing-mode-thumb {
  position: absolute;
  z-index: 0;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: 0;
  border-radius: 999px;
  background: var(--accent-soft);
  pointer-events: none;
  will-change: transform;
}
.pricing-mode-switch.thumb-ready .pricing-mode-thumb {
  transition: transform .28s cubic-bezier(.22, .61, .36, 1), width .28s cubic-bezier(.22, .61, .36, 1);
}
@media (prefers-reduced-motion: reduce) { .pricing-mode-switch.thumb-ready .pricing-mode-thumb { transition: none; } }
/* 每一项下面那行说明：说清计价口径（按字数结算、只限 token…），别让运营猜。 */
.console-model-group .field > small.panel-note { margin: -2px 0 0; line-height: 1.5; }

/* ---- 消耗定价：四步各一张表 ----

   一节一张表，五列：项目 / 售价（积分）/ 折合金额 / 成本（元）/ 毛利。
   表头和数据行共用 --pricing-cols 这一条模板，改列宽只改这一处，两边不会走散。

   为什么不用 <table>：项目那一列底下要挂一行灰色小字（「按实际字数结算……」），
   表格里塞两行文字就得拆单元格或者绝对定位，grid 里它就是同一格的第二行。 */
.pricing-section {
  /* 整张表不铺满面板：名字和数字之间隔着四百像素的话，眼睛得横扫一趟才对得上。 */
  max-width: 840px;
  /* 第一列的下限按最长的那个名字定：「Seedance 2.0 Fast · 1080p（每秒）」
     200px 放不下会折成两行。整行合计 250+104+96+104+68 + 4×10 间距 = 662，
     加卡片左右内边距 32 → 一张卡最少 694，上面那条 min-width: 1500 就是这么算的。 */
  --pricing-cols: minmax(250px, 1fr) 104px 96px 104px 68px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px 12px;
  border-radius: 8px;
  background: var(--surface-soft);
}
.pricing-section h4 { margin: 0 0 2px; font-size: 15px; }
.pricing-section > .panel-note { margin: 0 0 8px; line-height: 1.6; }

.pricing-row {
  display: grid;
  grid-template-columns: var(--pricing-cols);
  gap: 10px;
  align-items: center;
  padding: 7px 0;
}
/* 行与行之间一条细线。最后一行不画，免得和卡片下沿并成两条。 */
.pricing-row + .pricing-row { border-top: 1px solid var(--line); }
.pricing-row-head {
  padding: 0 0 6px;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 12px;
}
/* 表头那四个数字列和下面的输入框右对齐在同一条线上。 */
.pricing-row-head > span:not(:first-child) { text-align: right; }
.pricing-row-head + .pricing-row { border-top: 0; }

.pricing-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pricing-name > span { line-height: 1.4; }
.pricing-name > small { margin: 0; line-height: 1.5; }

/* 数字一律右对齐、等宽：定价页整屏都是数字，左对齐的话小数点全是错开的。 */
.pricing-input {
  width: 100%;
  min-width: 0;
  height: 32px;
  padding: 0 8px;
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.pricing-input:disabled { opacity: .5; }
/* 成本还没录的时候是空的，占位字别抢眼。 */
.pricing-cost::placeholder { color: var(--muted); opacity: .6; }

.pricing-margin {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--muted);
}
.pricing-margin.good { color: var(--green); }
/* 卖得比成本还便宜：这一格必须扎眼，它是在亏钱。 */
.pricing-margin.bad { color: var(--red); }
.pricing-blank { text-align: right; color: var(--muted); }

/* 开关行：整行让给勾选框，后面四格没有意义。 */
.pricing-row-switch { grid-template-columns: 1fr; }
.pricing-switch { display: flex; align-items: center; gap: 8px; cursor: pointer; }

/* 窄屏放不下五列：折合金额和毛利先撤，留下「卖多少 / 成本多少」这两件要紧的。 */
@media (max-width: 1080px) {
  .pricing-section { --pricing-cols: minmax(140px, 1fr) 92px 92px; }
  .pricing-row > .pricing-money,
  .pricing-row > .pricing-margin,
  .pricing-row-head > span:nth-child(3),
  .pricing-row-head > span:nth-child(5) { display: none; }
  .pricing-row > .pricing-blank:nth-child(3),
  .pricing-row > .pricing-blank:nth-child(5) { display: none; }
}

/* 定价页每一格：左边填积分，右边填金额，填哪个另一个跟着走。
   定价的人心里想的往往是「这一项收两毛」，那就让他直接填两毛。
   token 日限额不是钱，那一格没有金额框。 */
.price-input-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.price-input-row > input { flex: 1 1 0; min-width: 0; }
.price-input-sign { flex: none; color: var(--muted); font-size: 12px; white-space: nowrap; }
.price-input-money { flex: 0 1 86px; min-width: 64px; font-variant-numeric: tabular-nums; }
.price-input-money:disabled { opacity: .5; }
/* 标题后那句「1 积分 ≈ ¥x.xxx」：跟着标题走，字号小一档、不抢标题。 */
#pricingRate { margin-left: 10px; font-size: var(--font-base); font-weight: 400; }

/* ---- 右上角常驻公告 ----
   一个喇叭加一行标题，挂在语言按钮左边（没内容就整条 hidden）。
   鼠标移上去 / 键盘聚焦出卡片：左边图、右边上标题下正文。 */
.header-banner {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* 和右边那几颗的间距只归 .header-tools 的 gap 管（用户 2026-10-08：「从账户按钮往左，每个按钮的间距都要保持一致」）。
     原来这里另加 10px 外边距，是那一排还挤在一起时补的；gap 放到 18 之后，公告和语言按钮之间就比别处宽出 10px。 */
  max-width: 520px;
  /* 这一排放不下时由公告让：标题收成省略号（全文在悬浮卡片里），主题、铃铛那几颗 34 宽的一律 flex: none 不缩。
     原来让的是主题和铃铛——公告和「发现新版本」同时挂着时，他的屏上两颗被挤成 22 / 20 宽的竖条。 */
  min-width: 0;
  padding: 0 14px 0 10px;
  /* 右上角两颗药丸（公告、更新）同一身：13px 字、不加粗、字上下居中（用户 2026-09-12）；高度和右边那排按钮一样是 34
     （用户 2026-10-05：「要和右侧的按钮高度一致」，选了整排统一 34，原来两颗都是 28）。 */
  height: 34px;
  box-sizing: border-box;
  /* 圆角和右边那几颗一样是 12（用户 2026-10-08：「统一」）；原来两头全圆，是这一排里唯一的药丸形。「发现新版本」那颗白底提醒照旧是药丸。 */
  border-radius: 12px;
  /* 底色、描边和右边主题 / 铃铛 / 积分那几颗同一套（用户 2026-10-05：「深浅模式下的底色配色都调整一下，确保符合项目主题配色」）：
     原来是 --surface-soft、没有描边——深色下比旁边几颗亮一档，浅色下和页面底色几乎一样、看不出边。浅色那一套在下面。 */
  border: 1px solid var(--header-control-border);
  background: rgba(0, 0, 0, .28);
  color: var(--text);
  font-size: 13px;
  font-weight: 400;
  line-height: 1;
  cursor: default;
  outline: none;
}
.header-banner:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 浅色：白底、浅灰描边，和主题 / 铃铛 / 积分的浅色那一套一样（--surface / --line / --text）。 */
:root:not([data-theme="dark"]) .header-banner { border-color: var(--line); background: var(--surface); color: var(--text); }
.header-banner-icon {
  flex: none;
  width: 18px;
  height: 18px;
  /* 喇叭跟着强调色走（深色 #00c2ff、浅色 #0099e6）：图本身是单色的 #1296db，直接贴的话两种主题下都不是项目的青蓝。 */
  background: var(--accent);
  -webkit-mask: url("/static/icon-banner.png") no-repeat center / contain;
  mask: url("/static/icon-banner.png") no-repeat center / contain;
}
.header-banner-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 卡片：挂在喇叭下方靠右，别被窗口右边切掉。
   ::before 把和喇叭之间那 8px 空隙盖住——不盖的话鼠标穿过去时会先离开喇叭、
   卡片当场消失，人根本移不进来。 */
.header-banner-card {
  /* popover 会被提到浏览器顶层：位置只能按视口算（JS 现摆），right/top 那套用不了。
     顶层里没有 z-index 之争，也不会被任何祖先的 overflow 裁掉。 */
  position: fixed;
  z-index: 60;
  margin: 0;
  inset: auto;
  border: 1px solid var(--line);
  display: flex;
  gap: 14px;
  width: 480px;
  max-width: min(480px, calc(100vw - 32px));
  padding: 14px;
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--shadow-2);
  cursor: auto;
  /* 出卡片带过渡（用户 2026-09-13）：淡入 + 从上方 6px 落下来；open 由 bindHeaderBanner 挂，时长和 BANNER_FADE_MS 一致。 */
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .16s ease, transform .16s cubic-bezier(.22, .61, .36, 1);
}
.header-banner-card.open { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .header-banner-card { transition: none; } }
/* 没在显示时不占位。popover 的默认样式也是 display:none，这条是给不支持
   popover 的浏览器兜底——那种情况下卡片还是靠 .hidden 收起来。 */
.header-banner-card.hidden:not(:popover-open) { display: none; }
.header-banner-card::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 0;
  right: 0;
  height: 10px;
}
/* 图片区给宽一点，图按原比例整张放进去（contain 而不是 cover——
   cover 会把竖图的头尾裁掉，运营传的活动图往往正是竖的）。
   剩下的地方用底色填，图上下左右居中。 */
.header-banner-image {
  flex: none;
  width: 168px;
  height: 148px;
  border-radius: 8px;
  object-fit: contain;
  object-position: center;
  background: var(--surface-soft);
}
.header-banner-text { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.header-banner-card-title { font-size: 15px; line-height: 1.4; }
.header-banner-body {
  margin: 0;
  max-height: 220px;
  overflow-y: auto;
  color: var(--muted);
  font-size: var(--font-base);
  line-height: 1.7;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
@media (max-width: 1100px) { .header-banner-title { display: none; } .header-banner { max-width: none; padding: 5px 10px; } }

/* 发票状态只有五个短选项，不该继承控制台其它筛选器的宽栏。 */
.console-invoice-toolbar { justify-content: flex-start; }
#consoleInvoiceFilter { flex: 0 0 112px; width: 112px; min-width: 112px; }
#consoleInvoiceRefresh { flex: none; }

/* 已发通知的「查看内容」弹窗：标题 + 配图 + 正文。
   图按原比例整张放进去，正文长了自己滚，不把弹窗撑出屏幕。 */
.notice-view-dialog { width: min(560px, calc(100vw - 32px)); }
.notice-view-image {
  display: block;
  width: 100%;
  max-height: 260px;
  margin: 0 0 12px;
  border-radius: 8px;
  object-fit: contain;
  background: var(--surface-soft);
}
.notice-view-body {
  margin: 0;
  max-height: 320px;
  overflow-y: auto;
  color: var(--text);
  font-size: var(--font-base);
  line-height: 1.75;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---- 点图看大图 ----
   dialog 而不是普通遮罩：查看内容那个弹窗是 showModal 开的，在 top layer 上，
   普通 div 再高的 z-index 也压不过去。**普通尺寸的弹窗**（和播放视频那个一个身量），
   不铺满全屏；只有右上角的 × 能关，不做动画。 */
/* 原来的灯箱（.image-lightbox）2026-09-13 撤了：点图看大图统一走下面的图片查看器。 */
/* 能点开看大图的图，鼠标上去给个放大镜。 */
.image-zoomable { cursor: zoom-in; }

/* ---- 图片查看器（用户 2026-09-13）----
   点缩略图打开：上面大图、下面一排缩略图（同一张图反复生成的各个版本），点下面切上面。
   照看图软件那种版式：深底、图居中、底下一条横向可滚的缩略图带，当前那张亮白边。 */
.image-viewer {
  width: min(1200px, 94vw);
  height: min(900px, 92vh);
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: #141414;
  color: #fff;
  overflow: hidden;
}
.image-viewer::backdrop { background: rgba(0, 0, 0, .78); }
.image-viewer:focus { outline: none; }
/* 网格的行写在弹窗本身上，[open] 里只留 display：写进 [open] 的话关闭那一刻 [open] 一摘行就没了，
   display 还被离散过渡按着，底下那条缩略图会先跳到上面再消失（用户 2026-09-13）。 */
.image-viewer { grid-template-rows: minmax(0, 1fr) auto; }
.image-viewer[open] { display: grid; }
/* 舞台：图居中；旋转 / 放大后超出的部分在这儿裁掉。 */
.image-viewer-stage { position: relative; display: grid; min-width: 0; min-height: 0; padding: 16px; place-items: center; overflow: hidden; }
/* 图的宽高由 app.js 的 fitImageViewer 按舞台算好写在行内（9:16 的竖图按高度贴合舞台，转 90 度按宽），
   这里只兜首帧：JS 还没算之前别撑破舞台。旋转走 transform。 */
.image-viewer-image {
  /* 绝对定位在舞台正中，宽高按原图写、摆放全靠 transform（app.js applyImageViewerTransform）：
     用网格居中的话，原图比舞台宽时那一列会从左边溢出去，图就偏了。算好之前藏着，别让原图大小的盒子闪一下。 */
  position: absolute; left: 50%; top: 50%; display: block; max-width: none; max-height: none;
  visibility: hidden; border-radius: 6px; transform-origin: center;
}
/* 右下角一排圆钮：左旋 / 右旋 / 放大 / 缩小 / 复原。图标是用户 2026-09-13 给的四张 PNG，mask + currentColor，
   右旋就是左旋镜像一下。深底上的半透明圆钮，悬浮不换色，只按下缩小（全站规矩）。 */
.image-viewer-tools { position: absolute; right: 16px; bottom: 12px; z-index: 1; display: flex; gap: 8px; }
.image-viewer-tools button,
.image-viewer-tools button:hover:not(:disabled),
.image-viewer-tools button:focus-visible {
  display: grid; place-items: center; width: 38px; height: 38px; min-height: 38px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, .6); color: #fff; box-shadow: none;
}
.viewer-icon { display: block; width: 20px; height: 20px; background: currentColor; }
.viewer-icon-rotate-left { -webkit-mask: url("/static/icon-rotate-left.png") no-repeat center / contain; mask: url("/static/icon-rotate-left.png") no-repeat center / contain; }
.viewer-icon-rotate-right { -webkit-mask: url("/static/icon-rotate-left.png") no-repeat center / contain; mask: url("/static/icon-rotate-left.png") no-repeat center / contain; transform: scaleX(-1); }
.viewer-icon-zoom-in { -webkit-mask: url("/static/icon-zoom-in.png") no-repeat center / contain; mask: url("/static/icon-zoom-in.png") no-repeat center / contain; }
.viewer-icon-zoom-out { -webkit-mask: url("/static/icon-zoom-out.png") no-repeat center / contain; mask: url("/static/icon-zoom-out.png") no-repeat center / contain; }
.viewer-icon-reset { -webkit-mask: url("/static/icon-reset.png") no-repeat center / contain; mask: url("/static/icon-reset.png") no-repeat center / contain; }
/* 放大之后能拖着看；拖的时候不选中文字。 */
.image-viewer-image { cursor: grab; user-select: none; -webkit-user-drag: none; }
.image-viewer-image.dragging { cursor: grabbing; }
/* 底下一行：缩略图带占满左边，右下角「确认选择」只在挑参考图时出现（用户 2026-09-13）。 */
.image-viewer-foot { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; background: #1d1d1d; }
.image-viewer-foot .image-viewer-strip { min-width: 0; }
/* 「确认选择」：查看器不分主题永远是深底，所以这颗写死白底黑字，不能用 --command-create-bg
   （浅色主题下它是近黑，压在 #1d1d1d 的底上就看不见了）。用户 2026-09-13：不要渐变。
   悬浮 / 聚焦不换色，只按下缩小（全站规矩）。 */
.image-viewer-pick,
.image-viewer-pick:hover:not(:disabled),
.image-viewer-pick:focus-visible {
  margin: 0 16px 0 8px; border-color: transparent; background: #ffffff; color: #202124; box-shadow: none; white-space: nowrap;
}

/* 资产卡、项目卡上的垃圾桶：用户 2026-09-13 给的图（当天换过一版，同名覆盖），mask + currentColor，颜色跟着按钮走（悬浮变红）。 */
.trash-icon {
  display: block; width: 15px; height: 15px; background: currentColor;
  -webkit-mask: url("/static/icon-trash.png") no-repeat center / contain;
  mask: url("/static/icon-trash.png") no-repeat center / contain;
}
/* 参考图空位上的加号换成用户 2026-09-13 给的那张；别处的加号照旧。 */
.reference-plus .plus-icon {
  -webkit-mask: url("/static/icon-plus-reference.png") no-repeat center / contain;
  mask: url("/static/icon-plus-reference.png") no-repeat center / contain;
}
/* 出镜资产卡下面那颗「详情」（用户 2026-09-13）：整行一颗，点开资产提示词弹窗。 */
.asset-card:not(.asset-card-library) .asset-figure-tools.asset-episode-tools { width: 100%; margin-top: 6px; grid-template-columns: minmax(0, 1fr); }
/* 还没有图的出镜资产卡：「详情」「去生成图片」两颗平分这一行（用户 2026-09-14：去生成图片放到详情右边） */
.asset-card:not(.asset-card-library) .asset-figure-tools.asset-episode-tools.asset-tools-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.image-viewer-strip { display: flex; gap: 8px; padding: 10px 16px 14px; overflow-x: auto; background: #1d1d1d; scrollbar-width: thin; }
.image-viewer-thumb {
  flex: none; width: 96px; height: 72px; min-height: 0; padding: 0;
  border: 2px solid transparent; border-radius: 6px; background: #2a2a2a; overflow: hidden; cursor: pointer;
}
.image-viewer-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.image-viewer-thumb:hover:not(.active) { border-color: rgba(255, 255, 255, .4); }
.image-viewer-thumb.active { border-color: #fff; }
/* 鼠标点过的那张不留焦点框（当前那张已经有白边了），键盘走到才亮。 */
.image-viewer-thumb:focus:not(:focus-visible) { outline: none; box-shadow: none; }
.image-viewer-caption { position: absolute; left: 16px; top: 12px; z-index: 1; font-size: 13px; color: rgba(255, 255, 255, .75); text-shadow: 0 1px 2px rgba(0, 0, 0, .6); }
.image-viewer-close {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 36px; height: 36px; min-height: 36px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, .55); color: #fff; cursor: pointer;
}
.image-viewer-close:hover:not(:disabled), .image-viewer-close:focus-visible { background: rgba(0, 0, 0, .55); color: #fff; box-shadow: none; }
.image-viewer-close .close-icon { width: 16px; height: 16px; }

/* 同一张图反复生成过（有 -v2、-v3）：缩略图后面露出两张斜着的「底片」，看着是一叠（用户 2026-09-13）。 */
.output-thumbnail.stacked { position: relative; overflow: visible; }
.output-thumbnail.stacked img { position: relative; z-index: 1; border-radius: 3px; box-shadow: 0 1px 3px rgba(0, 0, 0, .45); }
.output-thumbnail.stacked::before,
.output-thumbnail.stacked::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  /* 底片的底色用 --muted（次要文字那档灰）：深色下是浅灰、浅色下是深灰，压在行底色上一眼看得见
     （用户 2026-09-13 两次：--surface、--tab-rail-strong 在深色下都和行底色太接近）。描边用行底色，像纸叠着的缝。 */
  border: 1px solid var(--surface-soft); border-radius: 4px; background: var(--muted);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}
.output-thumbnail.stacked::before { transform: rotate(-9deg) translate(-3px, 2px); }
.output-thumbnail.stacked::after { transform: rotate(7deg) translate(3px, 1px); }

/* ---- 创作会员卡 ----
   和充值积分分开一行：买的是每月一笔积分 + 助手额度，不是一次性的积分包，
   两件事混在一张卡里没人算得清。 */
.member-card { margin-bottom: 16px; }
.member-card-badge { background: var(--gradient-primary); }
/* 图标换成用户给的那枚（2026-09-10，icon-member-card.png）：白色图形当遮罩，压在渐变圆底上用白色画——
   和充值卡那枚一样走遮罩，不直接贴图，将来换色不用另出一版图。
   图形占整张图九成宽，20px 画出来和充值卡那枚 22px 的钱包看着一样大。 */
.member-card-badge { color: #FFFFFF; }
.member-card-badge::before {
  -webkit-mask: url("/static/icon-member-card.png") no-repeat center / 20px 20px;
  mask: url("/static/icon-member-card.png") no-repeat center / 20px 20px;
}
/* 文案离标题栏远一点：原来贴得太近，一眼看过去像挤在一起。 */
.member-card-body { padding-top: 12px; }
.member-plan { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 28px; }
.member-plan-price { display: flex; align-items: baseline; gap: 4px; flex: none; }
.member-plan-price strong { font-size: 34px; line-height: 1; letter-spacing: -.5px; }
.member-plan-price span { color: var(--muted); font-size: var(--font-base); }
/* 权益一行三个、五条排两行（用户 2026-09-12：「创作会员的权益改成 2 行，再收敛一点高度」）。
   列按内容宽（不是各占一份），否则文字短的时候几列会被拉得老远；
   整块再往右挪一点，和左边的价格 / 赠送数分开。 */
.member-plan-perks {
  display: grid;
  /* auto 列宽按内容走、justify-content 靠左收住：用 1fr 的话两列会各占一半，
     文字短的时候中间空出一大条。minmax(0, max-content) 也不行——容器一挤
     就被压成 0。 */
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 4px 22px;
  flex: 1 1 auto;
  /* 和左边的价格 / 每月赠送数拉开：两块讲的不是一回事，挨太近会读成一段。 */
  margin: 0 0 0 56px;
  padding: 0;
  list-style: none;
}
@media (max-width: 900px) { .member-plan-perks { grid-template-columns: minmax(0, 1fr); } }
.member-plan-perks li { position: relative; padding-left: 18px; color: var(--text); font-size: var(--font-base); line-height: 1.6; }
/* 前面那个勾用伪元素画，不引图标资源。 */
.member-plan-perks li::before {
  content: "";
  position: absolute;
  top: 5px;
  left: 0;
  width: 12px;
  height: 12px;
  background: var(--green);
  -webkit-mask: url("/static/icon-check.png") no-repeat center / contain;
  mask: url("/static/icon-check.png") no-repeat center / contain;
}
.member-plan-button { flex: none; min-width: 132px; }
.member-plan-note { flex: 1 0 100%; margin: 0; color: var(--muted); font-size: var(--font-base); }

/* ---- 购买：每张卡自己带按钮，付款方式挪进出码弹窗 ---- */
/* 卡片从 button 变成 div（里面装了真按钮，嵌套 button 不合法），
   hover 高亮跟着改到 div 上；选中态没有了——点按钮直接出码。 */
.buy-package:hover { background: var(--buy-control-hover); }
.buy-package-action { align-self: flex-end; margin-top: 10px; padding: 8px 22px; font-size: 15px; }

/* 选中态那块白底是一块单独的滑块，切换时滑过去，不是给按钮直接换背景。
   位置由 positionPayThumb 按按钮实际宽度算——两个页签宽度不一样，写死不行。 */
.pay-methods { position: relative; display: flex; gap: 6px; padding: 5px; border-radius: 999px; background: var(--surface-soft); align-self: flex-start; }
.pay-method-thumb {
  position: absolute; top: 5px; bottom: 5px; left: 0; z-index: 0; width: 0;
  /* 选中那块跟着收款方式的品牌色走，一眼看得出现在扫的是哪家的码。 */
  border-radius: 999px; background: color-mix(in srgb, var(--pay-brand) 26%, transparent);
  border: 1px solid color-mix(in srgb, var(--pay-brand) 55%, transparent);
  box-sizing: border-box;
  pointer-events: none;
  transition: transform .28s cubic-bezier(.22, .61, .36, 1), width .28s cubic-bezier(.22, .61, .36, 1);
  will-change: transform;
}
/* 第一次摆位不要过渡，否则弹窗一开滑块会从最左边飞过来。 */
.pay-methods:not(.thumb-ready) .pay-method-thumb { transition: none; }
.pay-method {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 0;
  padding: 8px 18px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--font-base);
  cursor: pointer;
  transition: color .2s;
}
.pay-method.active { background: transparent; color: var(--text); font-weight: 650; }
/* 码盘底下那行「打开支付宝扫一扫」。背景跟随主题，强调词才用渠道品牌色。 */
.pay-method-hint { margin: 0; color: var(--muted); font-size: 13px; }
.pay-method-hint b { color: var(--pay-brand); font-weight: 650; }

/* ---- 发票弹窗 ---- */

/* 两步共用一个 <dialog>，高度固定：切到「已申请」或者列表为空时窗口不该
   忽大忽小，那一下跳动比多留一片空白难看得多。 */
.invoice-dialog { width: min(900px, calc(100vw - 32px)); }
/* 高度写死，不是 min-height：切页签（已申请那边没有底部汇总条）、
   切抬头类型（个人抬头没有税号那一行）都会让内容变矮，用 min-height 的话
   弹窗跟着一跳一跳。钉死总高，多出来的内容归内容区自己滚。 */
.invoice-step { display: flex; flex-direction: column; height: 560px; }
/* 填写这一步比列表那步内容多，560 装不下。表头 68 + 底栏 68 + 顶部留白 18
   + 表单 484 ＝ 638 是"刚好不出滚动条"，再多给一点当呼吸空间。
   取的是最高的那个状态（企业/非企业带税号行），个人抬头少一行也只是多出
   一截空白，窗口高度不变——这正是上一轮要的「切抬头类型不许跳」。 */
#invoiceFormStep { height: 664px; }
.invoice-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* 顶上留够：原来是 4px，内容面板几乎贴着标题栏那条分隔线，挤得慌。 */
  padding: 18px 24px 0;
}

/* 页签的形状和颜色走上面那份「滑轨页签」，这里不再单独定义——
   原来这儿抄了一份，底轨用 --surface-soft、滑块用 --surface，和控制台、
   剧本创作那两处颜色对不上，正是这次要统一掉的。 */

.invoice-hint { margin: 0; color: var(--muted); font-size: var(--font-base); }

/* 表格自己滚，弹窗不跟着长。 */
.invoice-table-wrap { flex: 1; min-height: 0; overflow: auto; border-radius: 10px; }
.invoice-table { width: 100%; border-collapse: collapse; font-size: var(--font-base); }
.invoice-table th {
  position: sticky; top: 0; z-index: 1;
  padding: 12px 14px; text-align: left; font-weight: 600;
  color: var(--muted); background: var(--surface-soft);
}
.invoice-table td { padding: 14px; border-top: 1px solid var(--line); }
.invoice-table tbody tr:first-child td { border-top: 0; }
.invoice-col-check { width: 44px; }
/* 金额靠右并等宽：一列数字左右不齐没法一眼比大小。
   表头也要带上 .invoice-table 前缀——上面那条 `.invoice-table th` 的
   text-align: left 优先级比单个类高，只写 .invoice-col-total 压不过它，
   结果是「共计」两个字靠左、底下的钱数靠右，对不上。 */
.invoice-col-total,
.invoice-table th.invoice-col-total { text-align: right; font-variant-numeric: tabular-nums; }
.invoice-reject { display: block; margin-top: 2px; color: var(--muted); font-size: 12px; }
/* 控制台发票表：抬头那一列底下挂着税号和邮箱，运营要照着税号往税务系统里敲。
   表格默认把长单元格压窄再截断，税号显示不全等于这一行白摆——给足宽度、
   让它换行而不是截断。 */
#consoleInvoiceBody td:first-child {
  min-width: 260px;
  /* 表格默认 nowrap + 省略号，公司全称会被截成「长沙引力怪兽科技有…」——
     可运营正是要照着这个全称往税务系统里敲，截断了这一格就白摆。 */
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
#consoleInvoiceBody td:first-child .invoice-reject { white-space: normal; word-break: break-all; }
/* 操作那一列的按钮别换行挤成两排。 */
#consoleInvoiceBody td.console-actions { white-space: nowrap; }
.invoice-empty { margin: 32px 0; color: var(--muted); text-align: center; }

/* 底部：左边汇总、右边按钮，占满整行。 */
.invoice-foot { justify-content: space-between; align-items: center; }
.invoice-selection { margin: 0; color: var(--muted); font-size: var(--font-base); }
.invoice-selection strong { color: var(--text); font-weight: 650; }
/* 金额要压过上面那条 strong 规则，所以带上父级选择器提一档优先级——
   只写 .invoice-amount 的话同为一个类，后写的赢不了，钱数会是普通白字。 */
.invoice-selection .invoice-amount,
.invoice-static .invoice-amount { color: var(--accent); }

/* ---- 第二步：填抬头 ---- */

/* 返回箭头在左、标题紧跟其后。dialog-header 默认是 space-between，
   这一步只有两个孩子，不改的话标题会被推到最右边去。 */
.invoice-form-header { align-items: center; justify-content: flex-start; gap: 4px; }
/* 全局那条 `input, select { height: 36px; padding: 7px 9px }` 把勾选框和单选框
   也一起撑大了（量出来 41×36），标签被挤得只剩几十像素宽，「企业单位」
   四个字只好一个字一行竖着排。这里按这两种控件本来的尺寸收回去。 */
.invoice-dialog input[type="checkbox"],
.invoice-dialog input[type="radio"] {
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  flex: none;
}
/* 全局那条 `input:not([type="checkbox"]):focus` 把复选框排除了、单选框没排除，
   于是点一下单选就套上一圈方形的 box-shadow——圆点外面一个方框，很怪。
   这里把方框去掉，聚焦提示改成跟着圆点走的圆环。 */
/* 圆点外面那个方框，来路有两条，都要堵：
   1. 全局 `input:not([type="checkbox"]):focus` 的那圈方形 box-shadow
      ——它排除了复选框，没排除单选框；
   2. 浏览器**原生**的焦点环。上一轮我以为"交回原生就会跟着控件画成圆的"，
      错了：Chrome 给原生单选框画的就是个方角矩形，颜色还取 accent-color，
      所以看着是一个紫色方框把圆点包住。appearance: auto 时 border-radius
      对它不生效，圆不了。
   所以两条一起关掉，焦点提示挪到右边的文字上——总得让键盘用户知道自己在哪，
   但不能是个框。 */
.invoice-dialog input[type="radio"]:focus,
.invoice-dialog input[type="radio"]:focus-visible {
  border-color: transparent;
  box-shadow: none;
  outline: none;
}
.invoice-dialog input[type="radio"]:focus-visible + span { color: var(--accent-text); }
.invoice-form {
  display: flex; flex-direction: column; gap: 4px;
  padding: 18px 20px; border-radius: 12px; background: var(--surface-soft);
}
/* 标签一列定宽，控件一列自适应——标签宽度不一，不定宽的话每行输入框起点都不同。 */
.invoice-row { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; gap: 16px; min-height: 52px; }
.invoice-label { color: var(--muted); font-size: var(--font-base); }
.invoice-required { color: #e5534b; font-style: normal; margin-left: 2px; }
.invoice-static { font-weight: 600; }
.invoice-row input[type="text"],
.invoice-row input[type="email"] {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--text); font: inherit;
}
.invoice-kinds { display: flex; gap: 22px; flex-wrap: wrap; }
/* 选项文字不许断开：「企业单位」拆成四行就不是选项了。 */
.invoice-kind { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; white-space: nowrap; }
.invoice-agree { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: var(--font-base); white-space: nowrap; }
.invoice-agree a { color: var(--accent); }

/* ---- 确认开票弹窗 ---- */

.invoice-issue-dialog { width: min(680px, calc(100vw - 32px)); }
.invoice-issue-body { display: flex; flex-direction: column; gap: 18px; padding: 4px 24px 8px; }
/* 开票前最该核对的两项：给谁开、开多少。摊在最上面一块底色里。 */
.invoice-issue-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 32px;
  padding: 14px 16px;
  border-radius: 10px;
  background: var(--surface-soft);
}
.invoice-issue-summary p { margin: 0; font-size: var(--font-base); }
.invoice-issue-summary span { color: var(--muted); }
.invoice-issue-summary strong { font-weight: 650; }
/* 收票邮箱单独一行：发错地方是这一步唯一不可撤的错。 */
.invoice-issue-mail {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: var(--font-base);
}
.invoice-issue-mail-icon { flex: none; color: var(--accent); }
.invoice-issue-mail-label { color: var(--muted); }
.invoice-issue-mail strong { margin-left: auto; font-weight: 650; word-break: break-all; }

.invoice-issue-field { display: flex; flex-direction: column; gap: 8px; }
.invoice-issue-label { font-size: var(--font-base); font-weight: 650; }
.invoice-issue-label small { color: var(--muted); font-weight: 400; }
.invoice-issue-field > small { color: var(--muted); font-size: 12px; }
.invoice-issue-field input[type="text"] {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

/* 上传区：整块可点，也能把文件拖进来。原生的「选择文件」按钮样式改不动，
   所以把真正的 input 藏起来，这里画一个和全站一致的。 */
.invoice-issue-drop {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 0;
  padding: 16px;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.invoice-issue-drop:active { transform: scale(.995); }
/* 拖到上面时给点反馈，否则不知道松手会不会接住。 */
.invoice-issue-drop.dragging { border-color: var(--accent); background: var(--accent-soft); }
.invoice-issue-drop.has-file { border-style: solid; border-color: var(--accent); }
.invoice-issue-drop-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-text);
}
.invoice-issue-drop-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.invoice-issue-drop-text strong { font-weight: 650; word-break: break-all; }
.invoice-issue-drop-text small { color: var(--muted); font-size: 12px; }
.invoice-issue-drop-pick {
  flex: none;
  margin-left: auto;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font-size: var(--font-base);
}

/* 「确认已开票」白底黑字。悬浮、聚焦一律同一样式，只有按下缩一下——
   全站按钮一个规矩，这里不能因为换了配色就破例。 */
#invoiceIssueSubmit,
#invoiceIssueSubmit:hover,
#invoiceIssueSubmit:focus,
#invoiceIssueSubmit:focus-visible {
  background: #fff;
  border-color: #fff;
  color: #111;
}
#invoiceIssueSubmit:active { translate: 0 1px; scale: .98; }
#invoiceIssueSubmit:disabled { opacity: .45; }
.invoice-foot-actions { display: inline-flex; gap: 10px; }
/* 两步的主按钮「去开票」「提交申请」都是白底黑字。
   悬浮、聚焦一律同一样式，只有按下缩一下——全站按钮一个规矩，
   不能因为换了配色就破例。 */
#invoiceNextButton,
#invoiceNextButton:hover,
#invoiceNextButton:focus,
#invoiceNextButton:focus-visible,
#invoiceSubmitButton,
#invoiceSubmitButton:hover,
#invoiceSubmitButton:focus,
#invoiceSubmitButton:focus-visible {
  background: #fff;
  border-color: #fff;
  color: #111;
}
#invoiceNextButton:active,
#invoiceSubmitButton:active { translate: 0 1px; scale: .98; }
#invoiceNextButton:disabled,
#invoiceSubmitButton:disabled { opacity: .45; }

/* 「申请开票」不另设样式：和旁边的「时间范围」共用 .orders-range，
   一条工具栏上两颗按钮长得一样才像同一排东西。白底黑字试过，太抢眼了。 */

@media (max-width: 560px) {
  .invoice-row { grid-template-columns: minmax(0, 1fr); gap: 6px; min-height: 0; padding: 6px 0; }
  .invoice-foot { flex-wrap: wrap; gap: 12px; }
}

/* 会员卡：每月积分做成 icon + 数字，按钮压在右下角。 */
.member-plan-grant { display: flex; align-items: center; gap: 8px; flex: none; }
.member-plan-grant-icon {
  width: 22px;
  height: 22px;
  background: var(--credits-icon-color, var(--primary-bg)); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.member-plan-grant strong { font-size: 26px; line-height: 1; font-variant-numeric: tabular-nums; }
.member-plan-grant small { color: var(--muted); font-size: var(--font-base); }
.member-plan-button { flex: none; }

/* ---- 购买类按钮：开通会员 / 立即购买 / 立享加赠 ----
   深色主题一律白底黑字（用户 2026-09-10 定的，原来是紫粉渐变白字），压在深色购买卡上一眼看见。
   浅色主题购买卡跟着变白，白按钮就贴在卡上看不见了，所以翻成深底白字，见这一节最后那条。
   悬浮、聚焦、选中都不改样式（只有按下缩一下），免得同一颗按钮在不同状态下看着像两个东西。
   「立即兑换」不在里面：那是核销一个码，不是付钱。 */
.buy-action {
  min-height: 0;
  border: 0;
  border-radius: 999px;
  background: #FFFFFF;
  color: #000000;
  font: inherit;
  font-weight: 650;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(0, 0, 0, .28);
  transition: transform .12s;
}
.buy-action:hover:not(:disabled),
.buy-action:focus,
.buy-action:focus-visible,
.buy-action:active:not(:disabled) {
  border: 0;
  background: #FFFFFF;
  color: #000000;
  filter: none;
  box-shadow: 0 8px 22px rgba(0, 0, 0, .28);
}
.buy-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 按下缩一下是全站统一的（顶上那条 button:active），这里不另写一份——
   写了反而因为特异性更低而不生效，看着像坏了。 */
.buy-action:disabled { opacity: .45; box-shadow: none; cursor: not-allowed; }
/* 浅色主题：按深色那套的关系翻过来，深底白字、阴影收淡。四种状态照旧同一个样子。 */
:root:not([data-theme="dark"]) .buy-action,
:root:not([data-theme="dark"]) .buy-action:hover:not(:disabled),
:root:not([data-theme="dark"]) .buy-action:focus,
:root:not([data-theme="dark"]) .buy-action:focus-visible,
:root:not([data-theme="dark"]) .buy-action:active:not(:disabled) {
  background: #202124;
  color: #FFFFFF;
  box-shadow: 0 6px 16px rgba(32, 33, 36, .18);
}
:root:not([data-theme="dark"]) .buy-action:disabled { box-shadow: none; }

/* 有效期：写在档位表和会员卡下面，字小一档、不抢按钮。 */
.buy-validity { margin: 10px 0 0; color: var(--muted); font-size: var(--font-base); line-height: 1.5; }
/* 购买须知：整页底部一段，管着上面两张卡。用比说明文案再淡一档的颜色和
   一条上边框和卡片拉开——它不属于任何一张卡，是这一页的落款。
   行高给到 1.8：整段是一口气读下来的长句，密了没人看。 */
.buy-terms {
  margin: 16px auto 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--font-base);
  line-height: 1.6;
}
/* 有效期和按钮一条水平线：说明靠左、按钮靠右，占满整行。 */
.member-plan-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex: 1 0 100%; margin-top: 0; }
.member-plan-foot .buy-validity { margin: 0; }
/* 已开通那句：挂在按钮正下方，跟着按钮靠右。字号和有效期说明一档，
   但用正文色——这句是「你现在的状态」，不是卖点旁边的小字。 */
.member-active-note {
  flex: 1 0 100%;
  /* 行间距交给 .member-plan 的 gap，这里再加 margin 就离按钮太远了。 */
  margin: 0;
  color: var(--text);
  font-size: var(--font-base);
  line-height: 1.6;
  text-align: right;
}

/* ---- 会员与充值：字段横着排，不铺满 ----
   注册赠送和会员套餐每项都很窄（一个数字 + 一格金额），一栏一行会拖出
   很长一列空白。改成一行排几个，每格按内容给个够用的宽度就行。 */
#accountPricingForm,
#membershipPlanForm { display: block; max-width: 1120px; }
#accountPricingForm .console-model-group,
#membershipPlanForm .console-model-group {
  display: flex;
  /* 原来的 .console-model-group 是 column，不显式改回 row 的话
     再怎么 wrap 也还是一行一个。 */
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 14px 20px;
}
/* 标题独占一行，下面的字段才好横着排。 */
#accountPricingForm .console-model-group > h4,
#membershipPlanForm .console-model-group > h4 { flex: 0 0 100%; }
#accountPricingForm .console-model-group > .field,
#membershipPlanForm .console-model-group > .field { flex: 0 0 auto; width: 236px; }
/* 说明那行跟着字段走，别把格子撑宽。 */
#accountPricingForm .field > small.panel-note,
#membershipPlanForm .field > small.panel-note { max-width: 236px; }

/* 「开放购买」：会员卡里独占一行摆在标题底下（下面的字段是横排的，
   混进去会被当成第一个字段），充值档位表里就是单元格里一枚勾选框。 */
#membershipPlanForm .console-model-group > .pricing-sale-toggle {
  flex: 0 0 100%;
  margin: -2px 0 2px;
  background: transparent;
  padding: 0;
}
/* 档位表那一格：和「首购加赠」那格一样，选择器要带上 #pricingPackageBody——
   `#pricingPackageBody input { width:100% }` 是 ID 选择器，光靠类名压不过它，
   勾选框会被撑成一条。 */
#consolePage #pricingPackageBody .pricing-sale-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: transparent;
  white-space: nowrap;
}

/* 账号等级角标：非会员那张是白图，走 mask 跟着主题色，低调；
   会员那张是品牌蓝的钻石，原样铺上去——那是要被看见的东西。 */
.account-tier {
  flex: none;
  width: 16px;
  height: 16px;
  margin-left: 6px;
  background: currentColor;
  opacity: .55;
  -webkit-mask: url("/static/icon-tier-free.png") no-repeat center / contain;
  mask: url("/static/icon-tier-free.png") no-repeat center / contain;
}
.account-tier.member {
  width: 17px;
  height: 17px;
  opacity: 1;
  background: url("/static/icon-tier-member.png") no-repeat center / contain;
  -webkit-mask: none;
  mask: none;
}

/* ---- 支付弹窗：按参考图左右镜像复刻，唯一变化是二维码在左 ---- */
.pay-dialog,
.pay-dialog[open] {
  position: relative;
  width: min(859px, calc(100vw - 48px));
  height: min(458px, calc(100vh - 48px));
  max-width: none;
  max-height: none;
  padding: 0;
  border: 1px solid #414141;
  border-radius: 15px;
  background: #202020;
  color: #e8e8e8;
  font-family: var(--font-text);
  overflow: hidden;
  box-sizing: border-box;
}

.pay-dialog[open] { display: block; }

.pay-dialog::backdrop {
  background: rgba(0, 0, 0, .58);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.pay-dialog .pay-close {
  position: absolute;
  z-index: 8;
  top: 14px;
  right: 15px;
  width: 29px;
  height: 29px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: #efefef;
  font-size: 23px;
  font-weight: 300;
  line-height: 1;
}

.pay-dialog .pay-body {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: 0;
  overflow: hidden;
}

.pay-dialog .pay-panels {
  display: grid;
  grid-template-columns: 36.36% 63.64%;
  width: 100%;
  height: 100%;
  min-height: 0;
  gap: 0;
  border: 0;
  border-radius: 0;
  overflow: hidden;
}

.pay-dialog .pay-left {
  position: relative;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-right: 1px solid #414141;
  background: #383838;
  color: #e6e6e6;
  box-sizing: border-box;
}

/* 切换槽贴在码上方、和码同宽（184 = 168 的码 + 两边 8 的白底），不再浮在左上角。
   一颗按钮就撑满整条，两颗各占一半。 */
.pay-dialog .pay-methods {
  align-self: center;
  width: 184px;
  box-sizing: border-box;
  background: #2d2d2d;
}
.pay-dialog .pay-method { flex: 1; justify-content: center; gap: 6px; padding: 7px 8px; font-size: 13px; }

.pay-dialog .pay-code-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 13px;
  padding: 0;
  background: transparent;
  transform: translateY(-22px);
}

.pay-dialog .pay-code-panel.is-empty {
  width: 209px;
  min-height: 164px;
  border: 1px dashed #626262;
  border-radius: 8px;
}

.pay-dialog .pay-code-box {
  position: relative;
  display: flex;
  /* 外沿固定 184，码多大留白就是剩下的一半：152 → 16px。 */
  padding: calc((184px - var(--pay-code-size, 152px)) / 2);
  border-radius: 6px;
  background: #ffffff;
  box-shadow: none;
}

.pay-dialog .pay-code-panel.is-empty .pay-code-box {
  background: transparent;
  box-shadow: none;
}

.pay-dialog .pay-code-marks { display: none !important; }
.pay-dialog .pay-method-hint { display: none !important; }

.pay-dialog .pay-expiry {
  display: block;
  width: 184px;
  margin: -2px 0 0;
  color: #a7a7a7;
  font-family: var(--font-text);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0;
  text-align: center;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.pay-dialog .pay-hint {
  position: absolute;
  left: 62px;
  right: 62px;
  bottom: 104px;
  max-width: none;
  margin: 0;
  color: #b8b8b8;
  font-size: 13px;
  line-height: 1.6;
  text-align: center;
}

.pay-dialog .pay-hint:not([data-tone="warn"]) { display: none; }
.pay-dialog .pay-hint[data-tone="warn"] { color: #e0b96a; }

.pay-dialog .pay-meta {
  min-width: 0;
  display: grid;
  grid-template-rows: 66px minmax(0, 1fr);
  gap: 0;
  padding: 0;
  background: #202020;
  color: #e8e8e8;
}

.pay-dialog .pay-meta-header {
  display: flex;
  align-items: center;
  min-width: 0;
  padding: 0 57px 0 23px;
  border-bottom: 1px solid #414141;
  box-sizing: border-box;
}

.pay-dialog .pay-meta-header h2 {
  margin: 0;
  color: #e4e4e4;
  font-size: 19px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}

.pay-dialog .pay-meta-body {
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 13px 23px 22px;
  overflow-y: auto;
  box-sizing: border-box;
}

.pay-dialog .pay-info-section h3,
.pay-dialog .pay-detail-section h3 {
  margin: 0 0 12px;
  color: #e1e1e1;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.25;
}

.pay-dialog .pay-product-summary {
  display: block;
  width: min(243px, 100%);
  height: 70px;
  min-height: 70px;
  padding: 11px 12px;
  border: 1px solid var(--pay-brand);
  border-radius: 11px;
  background: #2b2b2b;
  color: #e7e7e7;
  box-sizing: border-box;
}

.pay-dialog .pay-product-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 11px;
}

.pay-dialog .pay-product-head > strong {
  min-width: 0;
  font-size: 14px;
  font-weight: 650;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.pay-dialog .pay-product-tag {
  flex: none;
  padding: 3px 7px;
  border: 0;
  border-radius: 4px;
  background: color-mix(in srgb, var(--pay-brand) 15%, #2b2b2b);
  color: var(--pay-brand);
  font-size: 12px;
  line-height: 1.45;
  white-space: nowrap;
}

.pay-dialog .pay-product-price {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin-top: 6px;
}

.pay-dialog .pay-product-price strong {
  color: #eeeeee;
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.pay-dialog .pay-product-price span { color: #c6c6c6; font-size: 12px; }
.pay-dialog .pay-section-divider {
  display: block;
  height: 0;
  margin: 14px 0 16px;
  border-top: 1px dashed #444444;
}

.pay-dialog .pay-detail-section { min-height: 0; }

.pay-dialog .pay-detail-card {
  width: 100%;
  min-height: 170px;
  padding: 12px 15px;
  border-radius: 11px;
  background: #333333;
  color: #e8e8e8;
  box-sizing: border-box;
}

.pay-dialog .pay-detail-list,
.pay-dialog .pay-price-list { margin: 0; }

.pay-dialog .pay-detail-list > div,
.pay-dialog .pay-price-list > div,
.pay-dialog .pay-order-detail {
  display: grid;
  grid-template-columns: 77px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  min-height: 23px;
  padding: 0;
  border: 0;
  font-size: 12px;
  line-height: 1.35;
}

.pay-dialog .pay-detail-list dt,
.pay-dialog .pay-price-list dt { color: #8f8f8f; }

.pay-dialog .pay-detail-list dd,
.pay-dialog .pay-price-list dd {
  min-width: 0;
  margin: 0;
  color: #e5e5e5;
  font-weight: 600;
  text-align: right;
  overflow-wrap: anywhere;
}

.pay-dialog .pay-order-value {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-width: 0;
  color: #d8d8d8;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  text-align: right;
  word-break: break-all;
}

.pay-dialog .pay-price-divider {
  display: block;
  height: 0;
  margin: 5px 0 7px;
  border-top: 1px solid #505050;
}

.pay-dialog .pay-price-list .pay-total-row {
  display: grid;
  grid-template-columns: 77px minmax(0, 1fr);
  align-items: baseline;
  gap: 10px;
  margin: 0;
  padding: 3px 0 0;
  border: 0;
}

.pay-dialog .pay-price-list .pay-total-row dt {
  color: #d8d8d8;
  font-size: 13px;
  font-weight: 600;
}

.pay-dialog .pay-price-list .pay-total-row dd {
  color: #59ccf4;
  font-size: 24px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.pay-dialog .pay-copy-button { color: #a8a8a8; }

@media (max-width: 1000px) {
  .pay-dialog,
  .pay-dialog[open] {
    width: calc(100vw - 24px);
    height: calc(100vh - 24px);
    border-radius: 16px;
    overflow-y: auto;
  }
  .pay-dialog .pay-body { height: auto; min-height: 100%; overflow: visible; }
  .pay-dialog .pay-panels { grid-template-columns: minmax(0, 1fr); height: auto; overflow: visible; }
  .pay-dialog .pay-left { min-height: 284px; border-right: 0; border-bottom: 1px solid #414141; }
  .pay-dialog .pay-code-panel { transform: translateY(-10px); }
  .pay-dialog .pay-hint { bottom: 69px; }
  .pay-dialog .pay-meta { grid-template-rows: auto auto; }
  .pay-dialog .pay-meta-header { min-height: 66px; padding: 16px 58px 16px 20px; }
  .pay-dialog .pay-meta-header h2 { font-size: 19px; }
  .pay-dialog .pay-meta-body { padding: 16px 20px 22px; overflow: visible; }
  .pay-dialog .pay-product-summary { width: min(243px, 100%); }
}

@media (max-width: 520px) {
  .pay-dialog .pay-left { min-height: 284px; }
  .pay-dialog .pay-code-panel.is-empty { width: calc(100% - 36px); }
  .pay-dialog .pay-meta-body { padding: 18px; }
  .pay-dialog .pay-product-summary { height: auto; min-height: 70px; }
  .pay-dialog .pay-detail-card { padding: 18px; }
  .pay-dialog .pay-detail-list > div,
  .pay-dialog .pay-price-list > div,
  .pay-dialog .pay-price-list .pay-total-row { grid-template-columns: 92px minmax(0, 1fr); font-size: 14px; }
  .pay-dialog .pay-price-list .pay-total-row dd { font-size: 30px; }
}
/* 短信配置沿用控制台字段布局，窄屏时避免密钥与长标签横向溢出。 */
.console-sms-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin: 16px 0;
}
.console-sms-grid .field, .console-sms-grid input, .console-sms-grid select { min-width: 0; width: 100%; }
.console-sms-grid .field > button { justify-self: end; width: auto; }
#consoleSmsStatus { overflow-wrap: anywhere; }
/* 控制台「模型配置」页下面的 Minimo3D 那一块（2026-09-25）：服务器字段沿用短信那套两列布局；3D 任务表的状态字按颜色分。 */
.console-model3d-jobs-head { margin-top: 20px; }
.console-model3d-jobs-head h4 { margin: 0; white-space: nowrap; }
.console-model3d-jobs-head select { flex: none; width: auto; min-width: 128px; }
#consoleModel3dJobsSummary { white-space: nowrap; }
.console-model3d-jobs { max-height: 560px; }
.console-model3d-jobs td.console-cell-ellipsis { max-width: 280px; }
#consoleModel3dStatus { overflow-wrap: anywhere; }
.console-model3d-state.succeeded { color: var(--success); }
.console-model3d-state.failed { color: var(--red); }
.console-model3d-state.running { color: var(--blue); }
.console-model3d-state.queued { color: var(--muted); }
#bindPhoneCaptchaDialog { width: min(400px, calc(100vw - 32px)); }
#bindPhoneCaptchaImage { display: block; max-width: 100%; height: 44px; }
@media (max-width: 640px) { .console-sms-grid { grid-template-columns: minmax(0, 1fr); } }

/* 文本模型那几家：一家一页，切换时只显示当前这家。 */
.text-pane.hidden { display: none; }
.text-pane-subtitle { margin: 16px 0 2px; font-size: var(--font-base); font-weight: 700; }
/* 全局那条 `input, select { height: 36px; padding: 7px 9px }` 会把勾选框一起撑大
   （量过：398×36，一个方块横跨整行）。用到勾选框的地方都得自己把尺寸收回来。 */
.text-pane-switch { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; cursor: pointer; }
.text-pane-switch span { color: var(--text); font-size: var(--font-base); }

/* 分步模型九格：一列排下来太长，两列并排看得见全貌。窄了自动退回一列。 */
.text-pane-stages {
  display: grid;
  /* 就要两列。auto-fit 在宽屏上会自己排成四列，那样一行四个下拉太挤、
     标签也读不清。窄了才退成一列。 */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 14px;
}
@media (max-width: 720px) { .text-pane-stages { grid-template-columns: minmax(0, 1fr); } }
/* 卡片里也是两列。**别靠上面那条 max-width: 720px 的媒体查询**——
   它量的是视口，而卡片窄是因为分了四列，视口一点不窄，那条永远不会触发；
   这里显式写一遍，免得以后有人以为窄屏会自动退成一列。 */
.provider-card .text-pane-stages { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
/* 两列之后每格只有 ~130，标签和下拉都得收窄一档才装得下。 */
.provider-card .text-pane-stages .field > span { font-size: 12px; }
.provider-card .text-pane-stages select,
.provider-card .text-pane-stages input { height: 30px; padding: 0 6px; font-size: 12px; }
/* 分步模型每一行：模型下拉 + 「思考」开关。开关的尺寸归共用那份管
   （#consolePage input[type="checkbox"]），这里只排布，别在这儿写宽高。 */
.provider-card .text-stage-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.provider-card .text-stage-row > select { flex: 1 1 auto; min-width: 0; }
.provider-card .text-stage-thinking { flex: none; }
.provider-card .text-stage-thinking > span { font-size: 12px; white-space: nowrap; }
/* 勾了「思考」那一行下面展开的两格：强度、预算。没勾的藏起来——这儿自己写了 display，
   得把 [hidden] 再压回 none，不然 UA 那条输给它，藏不住。
   底色用 --accent-soft：它是半透明的强调色，压在卡片的 --surface-soft 上两套主题都看得见；
   左边一道强调色竖线把这块钉在上面那一行上，一眼看出是谁的参数（用户原话：
   「不然都不知道是谁开了」）。别写死色值——写死的会和卡片底同色隐形，滑轨页签栽过。 */
.provider-card .text-stage-thinking-params {
  display: flex; gap: 8px; min-width: 0;
  margin-top: 6px; padding: 8px 10px;
  border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0;
  background: var(--accent-soft);
}
.provider-card .text-stage-thinking-params[hidden] { display: none; }
.provider-card .text-stage-thinking-params > .field { flex: 1 1 0; min-width: 0; }
.provider-card .text-stage-thinking-params > .field > span { color: var(--accent-text); }
/* 四块配置整体压矮一点：原来一屏装不下一块，来回滚才看得全。
   动的只有间距，字号和控件高度不碰——那两样一改就和别处不一样了。 */
#consolePage .console-model-group { padding: 12px 14px; }
/* 供应商清单那几张卡：内边距归卡头和卡身各自管；也不跟着上面那条隔一张换一个底色——
   拖动时卡片换了位置底色就跟着闪。拖着的那张用一圈强调色（这里的卡片没有边框可换色）。 */
#consolePage .provider-card.provider-card-catalog,
#consolePage .provider-list > .provider-card:nth-child(even) { padding: 0; background: var(--surface-soft); }
#consolePage .provider-card.is-dragging { box-shadow: 0 0 0 1px var(--accent), 0 14px 36px var(--dialog-shadow); }
#consolePage .console-model-group h4 { margin: 0 0 2px; }
#consolePage .console-model-group > .panel-note { margin: 0 0 8px; }
#consolePage .console-model-group .field { gap: 3px; }
#consolePage .console-model-group > .field,
#consolePage .console-model-group .text-pane > .field { margin-bottom: 8px; }
#consolePage .text-pane-subtitle { margin: 10px 0 2px; }
#consolePage .text-pane-switch { margin: 8px 0 0; }
#consolePage .text-pane .panel-note { margin: 4px 0 0; line-height: 1.5; }

/* 消耗定价里的开关那一格。`.field` 是纵向 flex，勾选框直接摆进去会被全局那条
   `input, select { width: 100%; height: 36px }` 撑成一整条横杠，标签被压在下面，
   看着就是「开关和文字重叠」。改成「勾选框 + 标签」一行，说明缩到第二行。
   和功能权限那一处（.console-feature-item）用同一套排布，两边看起来才一样。 */
.console-switch-field {
  display: grid;
  /* 同上：第一列按控件实际宽度算。尺寸不在这儿定——那份药丸开关的样式是共用的，
     这里再写一遍就成了第二处，改一处另一处不跟着变。 */
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: 8px;
  row-gap: 3px;
}
.console-switch-field > span { color: var(--text); font-weight: 400; }
.console-switch-field > small { grid-column: 2; margin: 0; }


/* ---------- 侧栏的「帮助」悬浮面板 ---------- */

/* 侧栏是 overflow: hidden 的，面板挂在里面会被裁掉，所以它是 fixed 的、
   待在 <aside> 外面，位置由 JS 按帮助按钮的实际位置算。 */
.help-flyout {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 136px;
  padding: 6px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 14px 38px var(--dialog-shadow);
}

.help-flyout-item {
  height: 36px;
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  font-size: 14px;
  text-align: left;
}
.help-flyout-item:hover { background: var(--surface-soft); color: var(--text); }

/* 「备案信息」右端那个尖角：和侧栏帮助那颗同一张图、同一个意思。 */
.help-flyout-item { display: flex; align-items: center; gap: 8px; }
.help-flyout-chevron {
  flex: none;
  margin-left: auto;
  width: 12px;
  height: 12px;
  opacity: .5;
  background: currentColor;
  -webkit-mask: url("/static/icon-chevron-right.png") center / contain no-repeat;
  mask: url("/static/icon-chevron-right.png") center / contain no-repeat;
}

/* 备案信息面板：只读的四行，不是菜单，所以不要 hover 反馈。
   地址很长，宽度放到 300 并允许折行。 */
.icp-panel { width: 300px; padding: 12px 14px; cursor: default; }
/* 把和帮助面板之间那道空隙盖住。不盖的话鼠标横穿过去时会先离开菜单、
   面板当场收掉，人根本移不进来——右上角那张公告卡片是同一个做法。 */
.icp-panel::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -10px;
  width: 10px;
}
.icp-panel dl { display: grid; gap: 10px; margin: 0; }
.icp-panel dt { color: var(--muted); font-size: var(--font-base); }
.icp-panel dd { margin: 2px 0 0; color: var(--text); font-size: 13px; line-height: 1.6; word-break: break-word; }

/* ---------- 用户协议 / 隐私政策弹窗 ---------- */

/* 条款很长，高度先占满可用空间，正文自己滚——不然整个弹窗跟着页面滚，
   标题栏和「知道了」都会跑出视野。 */
.legal-dialog {
  width: min(880px, calc(100vw - 32px));
  height: min(820px, calc(100vh - 48px));
  max-height: calc(100vh - 48px);
  overflow: hidden;
}
.legal-dialog { flex-direction: column; }
.legal-dialog[open] { display: flex; }
.legal-dialog-body {
  flex: 1;
  min-height: 0;
  padding: 14px 24px 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---------- 三层视觉设计（2026-09-22）：分镜页的光位带子、镜头行的意图、设定页的影像风格 ---------- */

/* 和过期提示那条带子同一个位置，但它不是警告：底色用面板那一档、描边用普通线。
   只占一行（2026-09-24）：标题、几个光位几镜意图、「查看」「重做视觉设计」；光位全文在弹窗里。原来每个场景七项全文铺在这里，
   六个场景 861 像素高，把分镜表的滚动区挤成了 0 高。 */
.breakdown-look-band {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 10px 0 4px;
  padding: 8px 10px 8px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface-soft);
  font-size: 13px;
  line-height: 1.5;
}
.breakdown-look-title { color: var(--text); font-weight: 600; }
.breakdown-look-summary { color: var(--text); }
.breakdown-look-note { color: var(--muted); }
.breakdown-look-actions { display: flex; gap: 8px; margin-left: auto; }
.breakdown-look-redo {
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 12.5px;
}
.shot-intent-line { display: block; margin-top: 2px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 分镜表每一镜的概要最多两行，全文在悬浮里（2026-09-24）。 */
.shot-summary-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
/* 「标签：内容」清单式的悬浮提示（每一刀的运镜、导演意图全文）比一句话长，框放宽一些。 */
.hover-tip:has(.hover-tip-block) { max-width: 420px; }
.hover-tip-block p { margin: 0; line-height: 1.55; }
.hover-tip-block p + p { margin-top: 4px; }
.hover-tip-block b { font-weight: 600; }

/* 「视觉设计 · 查看」弹窗：和协议弹窗同一种排法（头 + 能滚的正文 + 脚），高度随内容、封顶一屏。 */
.look-dialog {
  width: min(760px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  overflow: hidden;
  flex-direction: column;
}
.look-dialog[open] { display: flex; }
.look-dialog-body {
  flex: 1;
  min-height: 0;
  padding: 4px 24px 10px;
  overflow-y: auto;
  overscroll-behavior: contain;
  font-size: 13px;
  line-height: 1.6;
}
.look-dialog-head { margin: 14px 0 4px; font-size: 14px; font-weight: 600; color: var(--text); }
.look-dialog-head:first-child { margin-top: 4px; }
.look-dialog-hint { margin: 0 0 8px; color: var(--muted); }
.look-dialog-card { margin: 0 0 10px; padding: 12px 14px; border-radius: 12px; background: var(--surface-soft); }
.look-dialog-card h4 { margin: 0 0 6px; font-size: 13.5px; font-weight: 600; color: var(--text); }
.look-dialog-fields { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 4px 12px; margin: 0; }
.look-dialog-fields + .look-dialog-fields { margin-top: 4px; }
.look-dialog-fields dt { color: var(--muted); }
.look-dialog-fields dd { margin: 0; color: var(--text); }

.script-look-bible { margin: 0 0 6px; }
.script-look-bible dt { color: var(--muted); }
.script-look-bible dd { margin: 0 0 6px; }
.script-look-bans { color: var(--muted); font-size: 13px; margin: 0 0 8px; }

/* 侧栏第一行是 3D 导演台、下面一条横线（Minimo3D 那一行 2026-10-27 撤了，从产品切换的「3D」进）。它藏着（内测时的普通用户：
   网关发页面就带着 hidden）或者被摘掉（入口关了）时，这条线也不画：首帧就对，不等 JS。注释不算兄弟元素，+ 和 :first-child 都照样认得。 */
.sidebar-nav > .sidebar-divider:first-child,
.sidebar-nav > .sidebar-item[data-page="director"].hidden:first-child + .sidebar-divider { display: none; }

/* ---- 侧边栏「3D 导演台」页（独立镜头，2026-09-27） ---- */
.director-home { display: flex; flex-direction: column; gap: 18px; max-width: 1280px; }
/* 标题后面紧跟「新建镜头」，副标题在下面一行、不换行（用户 2026-09-27），窗口窄到放不下时末尾省略。 */
.director-home-head { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.director-home-title { display: flex; align-items: center; gap: 16px; }
.director-home-title h2 { margin: 0; font-size: 20px; font-weight: 700; }
.director-home-head p { margin: 0; color: var(--muted); line-height: 1.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.director-home-title .primary-command { flex: none; min-height: 32px; padding: 0 16px; }
.director-home-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.director-home-empty { grid-column: 1 / -1; display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 60px 0; color: var(--muted); }
.director-home-empty p { margin: 0; }
.director-shot-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.director-shot-cover {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: 8px;
  background: var(--surface-soft);
}
.director-shot-cover:hover:not(:disabled) { border: 0; background: var(--surface-soft); }
.director-shot-cover video { display: block; width: 100%; height: 100%; object-fit: cover; }
.director-shot-placeholder {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 34px;
  height: 34px;
  background: var(--muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='12.5' height='10' rx='2'/%3E%3Cpath d='m15.5 10.5 5.5-3v9l-5.5-3'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='12.5' height='10' rx='2'/%3E%3Cpath d='m15.5 10.5 5.5-3v9l-5.5-3'/%3E%3C/svg%3E") no-repeat center / contain;
}
.director-shot-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.director-shot-body strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.director-shot-meta { color: var(--muted); font-size: 12px; }
.director-shot-body p { margin: 2px 0 0; color: var(--muted); font-size: 12px; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.director-shot-actions { display: flex; gap: 6px; margin-top: auto; }
.director-shot-actions button { flex: 1; min-height: 30px; padding: 0 8px; }

/* ---- 视频时长滑块（用户 2026-09-27：选时长「不要弹出下拉框」，照他给的参考图拖动选择）----
   shared.js 的 durationSlider：左边带刻度的滑轨、下面一排刻度数字，右边填数字的框。视频生成页那一格、新建独立镜头弹窗共用；
   导演台（director.css）自己一份，样子一样。滑轨三档灰：深色照参考图，浅色照「浅色标准模板」。 */
:root { --duration-track: #e4e9e7; --duration-fill: #c2cac6; --duration-tick: rgba(0, 0, 0, .28); }
:root[data-theme="dark"] { --duration-track: #3a3a3a; --duration-fill: #5c5c5c; --duration-tick: rgba(255, 255, 255, .35); }
/* 2026-09-27 用户「太大了，等比缩小一点」：整体收到大约八成。 */
.duration-slider { display: flex; align-items: flex-start; gap: 18px; }
.duration-slider-rail { flex: 1; min-width: 0; padding-top: 6px; }
.duration-slider-track { position: relative; height: 14px; border-radius: 5px; background: var(--duration-track); cursor: pointer; touch-action: none; }
.duration-slider-fill { position: absolute; top: 0; bottom: 0; left: 0; width: 0; border-radius: 5px 0 0 5px; background: var(--duration-fill); pointer-events: none; }
.duration-slider-ticks { position: absolute; inset: 0; pointer-events: none; }
.duration-slider .duration-slider-ticks span { position: absolute; top: 5px; width: 1px; height: 4px; background: var(--duration-tick); transform: translateX(-50%); }
.duration-slider-thumb { position: absolute; top: -2.5px; width: 4px; height: 19px; margin-left: -2px; border-radius: 2px; background: var(--text); cursor: grab; }
.duration-slider-thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.duration-slider-scale { position: relative; height: 14px; margin-top: 8px; }
.duration-slider .duration-slider-scale span { position: absolute; color: var(--muted); font-size: 11px; font-weight: 400; font-variant-numeric: tabular-nums; transform: translateX(-50%); }
/* 输入框收小，右边照参考图两颗上下的小箭头（用户 2026-09-27：「在输入框里还有上和下的按钮用于调整时长」「输入时长的文本框现在太大了」）。 */
.duration-slider-input { display: flex; flex: none; align-items: center; gap: 4px; width: 84px; height: 34px; margin: 0; padding: 0 4px 0 10px; border-radius: 8px; background: var(--surface-soft); cursor: text; }
.duration-slider-steps { display: flex; flex-direction: column; margin-left: 2px; }
#mainView .duration-slider-steps button, .duration-slider-steps button {
  width: 16px; height: 13px; min-height: 0; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--muted); box-shadow: none;
}
.duration-slider-steps button::before { content: ""; display: block; width: 5px; height: 5px; margin: 0 auto; border-left: 1.5px solid currentColor; border-top: 1.5px solid currentColor; transform: translateY(2px) rotate(45deg); }
.duration-slider-steps button + button::before { transform: translateY(-2px) rotate(225deg); }
.duration-slider-steps button:disabled { opacity: .35; }
.duration-slider .duration-slider-input input,
#mainView .duration-slider .duration-slider-input input {
  flex: 1; width: auto; min-width: 0; height: auto; min-height: 0; padding: 0; border: 0; border-radius: 0;
  background: transparent; color: var(--text); font: inherit; font-size: 13px; box-shadow: none;
  -moz-appearance: textfield; appearance: textfield;
}
.duration-slider-input input::-webkit-outer-spin-button,
.duration-slider-input input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.duration-slider .duration-slider-input input:focus { outline: none; }
.duration-slider .duration-slider-input span { color: var(--muted); font-size: 13px; font-weight: 400; }
/* 视频生成页那一格：和旁边的下拉同一身（同高、同底色、右边一个小勾），点开往上弹面板。 */
.studio-duration-host { display: flex; min-width: 0; }
#mainView .studio-duration-button, .studio-duration-button {
  width: 100%; min-width: 0; min-height: 32px; margin: 0; padding: 4px 16px 4px 8px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background-color: var(--input-bg); color: var(--text); font-size: var(--font-base); text-align: left; white-space: nowrap; box-shadow: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23b3b3b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 6px center; background-size: 8px 5px;
}
#mainView .studio-duration-button:hover:not(:disabled), .studio-duration-button:hover:not(:disabled) { border-color: transparent; background-color: var(--input-bg); }
.studio-duration-button:disabled { opacity: .7; }
.studio-duration-panel {
  position: fixed; left: 0; top: 0; z-index: 6;
  display: flex; flex-direction: column; gap: 12px; width: 416px; padding: 12px 16px 14px;
  border-radius: 12px; background: var(--surface-alt); box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
.studio-duration-panel.hidden { display: none; }
.studio-duration-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.studio-duration-head strong { color: var(--muted); font-size: 12px; font-weight: 650; }
.studio-duration-head span { color: var(--muted); font-size: 11px; }

/* ======================================================================
   按钮与弹窗：照 New API 的样子（2026-09-28）
   用户：「按钮和弹窗样式，都可以借鉴 NewAPI 这一套」。追问后定的：按钮外观和交互照 New API——次要 / 描边 / 幽灵 / 危险
   悬浮变底色，按下往下沉 1 像素再缩到 .98，键盘聚焦一圈 3px 光圈；**主按钮保持黑白纯色**（深色白底黑字、浅色黑底白字，
   悬浮不变，New API 的主按钮本来也不变）；确认框、输入框、表单、详情这类通用弹窗换成它的样子，登录框、付款弹窗保持版式。
   New API 的这两样来自 shadcn/ui（MIT）；这里照它的圆角、状态、间距，用我们自己的主题变量重写，没拷它的代码（它是 AGPL）。
   数值是在它线上那份样式里逐个状态量出来的：按钮圆角 16px（小号 12.8px），描边款浅色描边 / 深色 17% 白边，
   危险款底色掺 10%（深色 20%）红、悬浮 20%（30%），光圈是强调色 50%；弹窗圆角 22.4px、外面一圈 1px 的 10% 前景色、
   蒙版 10% 黑加 4px 模糊，开合 .1 秒淡入加从 .95 放大，底部按钮栏是一条带顶线的浅底。

   **只管「按钮体系」**，三组，各自保持原来的优先级（别用 :is() 串在一起——它按最高的那个算，会压掉容器里写的规则）：
   - 没加类名的普通按钮（次要款）：`button:where(:not([class]), [class=""], [class="hidden"])`，优先级和最底层的 `button {}` 一样，
     写在某个容器里的规则（设置面板脚上那两颗实心块、登录 / 注册那对切换）照样压得住它；悬浮是在原底色上叠一层白 / 黑，不换底色。
   - 类名款：主按钮、危险、图标（幽灵款）、生成类，外加下面点名的一批「长得就是按钮」的定制按钮（只换形状、光圈，颜色照旧）。
   - 弹窗底部栏里的（取消那颗是描边款）。
   菜单行、列表行、页签、分段切换、卡片、已经是胶囊的（999px）不在里面——它们自己定的外观，给它们加悬浮底色就是 2026-08
   那次「怎么又有悬浮了」。不碰 display 和内边距：覆盖了会把某些靠 CSS 藏起来的按钮放出来、把图标按钮撑变形。
   ====================================================================== */
:root {
  --btn-radius: 16px;
  --btn-radius-sm: 12.8px;
  --btn-ease: cubic-bezier(.4, 0, .2, 1);
  --btn-ring: color-mix(in srgb, var(--accent) 50%, transparent);
  /* 次要款悬浮：New API 是把底色调成 80% 不透明、透出后面的页面底（浅色变浅、深色变深）。我们在原底色上叠一层同样效果的纱。 */
  --btn-hover-veil: rgba(255, 255, 255, .2);
  /* New API 默认主题的 destructive：浅色 #e40014、深色 #ff6568；报错通知也用这一个（--toast-danger）。 */
  --danger: #e40014;
  --btn-outline-border: var(--line);
  --btn-outline-bg: var(--surface);
  --btn-outline-hover: var(--surface-soft);
  --btn-ghost-hover: var(--surface-soft);
  --btn-danger-bg: color-mix(in srgb, var(--danger) 10%, transparent);
  --btn-danger-hover: color-mix(in srgb, var(--danger) 20%, transparent);
  --btn-danger-ring: color-mix(in srgb, var(--danger) 20%, transparent);
  --dialog-radius: 22.4px;
  --dialog-ring: color-mix(in srgb, var(--text) 10%, transparent);
  --dialog-overlay: rgba(0, 0, 0, .1);
  --dialog-footer-bg: color-mix(in srgb, var(--surface-soft) 50%, transparent);
}
:root[data-theme="dark"] {
  --btn-hover-veil: rgba(0, 0, 0, .2);
  --danger: #ff6568;
  --btn-outline-border: rgba(255, 255, 255, .17);
  --btn-outline-bg: rgba(255, 255, 255, .05);
  --btn-outline-hover: rgba(255, 255, 255, .084);
  --btn-ghost-hover: color-mix(in srgb, var(--surface-soft) 50%, transparent);
  --btn-danger-bg: color-mix(in srgb, var(--danger) 20%, transparent);
  --btn-danger-hover: color-mix(in srgb, var(--danger) 30%, transparent);
  --btn-danger-ring: color-mix(in srgb, var(--danger) 40%, transparent);
}

/* 形状、字重、过渡。 */
button:where(:not([class]), [class=""], [class="hidden"]),
.button-link, .primary-command, .danger-command, .icon-command, .generate-command,
.model-test-button, .asset-generate, .cost-book-command, .subtab-pick-command, .prompt-command,
.storyboard-delete-command, .provider-model-add, .provider-card-remove, .metrics-refresh,
.episode-manage, .studio-shot-manage, .ticket-close, .shot-action-command, .one-click-command,
.dialog-footer button {
  border-radius: var(--btn-radius);
  transition:
    background-color .15s var(--btn-ease), background-image .15s var(--btn-ease), border-color .15s var(--btn-ease),
    color .15s var(--btn-ease), box-shadow .15s var(--btn-ease), opacity .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
/* 字重 500（New API 的 font-medium）。生成类按钮的字不加粗是用户 2026-09-12 定的，不在这里。 */
button:where(:not([class]), [class=""], [class="hidden"]),
.button-link, .primary-command, .danger-command, .icon-command,
.model-test-button, .asset-generate, .cost-book-command, .subtab-pick-command, .prompt-command,
.storyboard-delete-command, .provider-model-add, .provider-card-remove, .metrics-refresh,
.episode-manage, .studio-shot-manage, .ticket-close, .shot-action-command, .one-click-command,
.dialog-footer button:not(.generate-command) {
  font-weight: 500;
}
/* 小号（高度 30px 以下的那几颗）圆角 12.8px，和 New API 的 sm / icon-sm 一样。 */
.subtab-pick-command, .prompt-command, .storyboard-delete-command, .studio-shot-manage, .ticket-close, .episode-manage {
  border-radius: var(--btn-radius-sm);
}

/* 次要款悬浮：在原底色上叠一层纱，底色本身不换（容器里定了颜色的照旧是那个颜色）。 */
button:where(:not([class]), [class=""], [class="hidden"]):hover:not(:disabled) {
  background-image: linear-gradient(var(--btn-hover-veil), var(--btn-hover-veil));
}
/* 描边款：弹窗底部栏里除了主按钮、危险、生成以外的（取消那颗）。 */
.dialog-footer button:not(.primary-command):not(.danger-command):not(.generate-command) {
  border-color: var(--btn-outline-border);
  background: var(--btn-outline-bg);
  color: var(--text);
}
.dialog-footer button:not(.primary-command):not(.danger-command):not(.generate-command):hover:not(:disabled) {
  background: var(--btn-outline-hover);
}
/* 幽灵款：图标按钮（弹窗右上角的 × 就是它）。 */
.icon-command { border-color: transparent; background: transparent; }
.icon-command:hover:not(:disabled) { border-color: transparent; background: var(--btn-ghost-hover); color: var(--text); }
/* 危险款。确认框里那颗「删除」照旧黑白（用户 2026-09-13：「删除的弹窗，都改成白底黑字」），那条规则更具体、压得住这里。 */
.danger-command { border-color: transparent; background: var(--btn-danger-bg); color: var(--danger); }
.danger-command:hover:not(:disabled) { border-color: transparent; background: var(--btn-danger-hover); color: var(--danger); filter: none; }
/* 链接款：平时不带下划线，悬浮才有（New API 的 link）。 */
.link-button { text-decoration: none; text-underline-offset: 4px; }
.link-button:hover:not(:disabled) { text-decoration: underline; }

/* 键盘聚焦：边线换强调色，外面一圈 3px 的半透明光圈（鼠标点不出来，只有键盘 Tab 到才有）。
   光圈用 outline 画、不用 New API 的 box-shadow：好些按钮有更具体的规则把 box-shadow 清成 none（确认框那颗确认），
   用 box-shadow 的话它们一聚焦就什么都看不出来——键盘用户不知道焦点在哪。outline 会顺着圆角走。 */
button:where(:not([class]), [class=""], [class="hidden"]):focus-visible,
.button-link:focus-visible, .primary-command:focus-visible, .danger-command:focus-visible, .icon-command:focus-visible,
.generate-command:focus-visible, .model-test-button:focus-visible, .asset-generate:focus-visible,
.cost-book-command:focus-visible, .subtab-pick-command:focus-visible, .prompt-command:focus-visible,
.storyboard-delete-command:focus-visible, .provider-model-add:focus-visible, .provider-card-remove:focus-visible,
.metrics-refresh:focus-visible, .episode-manage:focus-visible, .studio-shot-manage:focus-visible,
.ticket-close:focus-visible, .shot-action-command:focus-visible, .one-click-command:focus-visible,
.dialog-footer button:focus-visible {
  outline: 3px solid var(--btn-ring);
  outline-offset: 0;
  border-color: var(--accent);
}
.danger-command:focus-visible { outline-color: var(--btn-danger-ring); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
button:disabled { opacity: .5; }

/* ---- 通用弹窗 ----
   登录框（.login-dialog、.auth-dialog）、付款弹窗（.pay-dialog）保持版式（用户定的）；图片查看器、视频播放器是看内容的，
   被挤下线 / 登录失效那两个有自己的整屏糊背景，都不套这一身。 */
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) {
  border: 0;
  border-radius: var(--dialog-radius);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--dialog-ring);
  transform: scale(.95);
  transition:
    opacity .1s ease,
    transform .1s ease,
    overlay .1s ease allow-discrete,
    display .1s ease allow-discrete;
}
dialog[open]:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) { transform: none; }
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog)::backdrop {
  background: var(--dialog-overlay);
  backdrop-filter: blur(4px);
  transition:
    opacity .1s ease,
    overlay .1s ease allow-discrete,
    display .1s ease allow-discrete;
}
@starting-style {
  dialog[open]:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) { opacity: 0; transform: scale(.95); }
}
@media (prefers-reduced-motion: reduce) {
  dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog),
  dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog)::backdrop { transition: none; }
}
/* 标题栏：不再有底线，标题 16px 中等字重；右上角的 × 是 28px 的幽灵按钮，贴着弹窗角各留 8px（New API 的 top-2 right-2）。
   × 挪成绝对定位（相对弹窗本身，弹窗是 position: fixed），标题右边空出位置别压字。 */
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) .dialog-header { position: static; padding: 16px 48px 4px 20px; border-bottom: 0; }
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) .dialog-header h2 { font-size: 16px; font-weight: 500; line-height: 1.25; }
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) .dialog-header .icon-command {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  border-radius: var(--btn-radius-sm);
}
/* 底部按钮栏：一条带顶线的浅底，按钮靠右。付款弹窗也有 .dialog-footer，版式不动（按钮照样换，见上面）。 */
dialog:not(.login-dialog, .auth-dialog, .pay-dialog, .image-viewer, .video-player-dialog, .session-kicked-dialog) .dialog-footer {
  margin-top: 12px;
  padding: 16px 20px;
  border-top: 1px solid var(--line);
  background: var(--dialog-footer-bg);
  border-radius: 0 0 var(--dialog-radius) var(--dialog-radius);
}
/* 定高、竖排的弹窗（.new-work-dialog 那一类是 640px 高的竖排 flex，新建分集也挂着它）：底部栏贴到最底下，别悬在中间、下面空一截。 */
dialog.new-work-dialog > .dialog-footer { margin-top: auto; }

/* ---- 付款弹窗：版式不动，只换里面的按钮（2026-09-28）----
   右上角 × 和复制键是幽灵款：平时透明，悬浮出一块底，圆角 12.8px。付款弹窗不跟主题、一直是深底，那两处的悬浮底写死成 8% 白。
   支付宝 / 微信那对切换是跟着品牌色走的滑轨，不动。复制键别的地方（订单表）也用，悬浮底跟主题。 */
.pay-dialog .pay-close,
.pay-copy-button {
  border-radius: var(--btn-radius-sm);
  transition:
    background-color .15s var(--btn-ease), color .15s var(--btn-ease), translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
.pay-copy-button:hover:not(:disabled) { background: var(--btn-ghost-hover); color: var(--text); }
.pay-dialog .pay-close:hover:not(:disabled),
.pay-dialog .pay-copy-button:hover:not(:disabled) { background: rgba(255, 255, 255, .08); color: #fff; }
.pay-dialog .pay-close:focus-visible,
.pay-copy-button:focus-visible { outline: 3px solid var(--btn-ring); outline-offset: 0; }

/* ==================== 屏幕适配（2026-10-01） ==================== */
/* 用户：「现在的网站在MacBook下，和手机端，观看体验都非常的不好」。整站原来是在 2560 宽的大屏上调的（他的屏：4K @1.5），
   MacBook 的浏览器只有 1440–1512 宽、790–870 高，两边写死的宽度（侧栏 208、助手栏 540、分镜页左栏 260）把中间挤到三百来像素；
   手机上侧栏常驻、三栏硬挤，基本没法用。分档（这一块里的规则全按它写，别另起断点）：
     手机 ≤ 767 · 平板 768–1199 · 笔记本 1200–1599（MacBook 落在这里）· 台式 ≥ 1600（**这一档一条都不生效，他的屏一像素不变**）
     矮屏 ≤ 820 高（MacBook Air 13 一类；他的屏和 14 寸 MacBook 都比它高）。
   验收：scripts/viewport_audit.py 截全部页面，2560 / 1707 两档和改之前逐像素比必须零差异。 */

/* 横着滚的那几块滚到头时别把劲传给浏览器：Mac 触控板两指横滑滚分镜台、素材条、宽表格，滚到头会顺带触发「后退」整页走掉。
   首页精选作品那条本来就有。只在 1600 以下加：它会让那几块换一种合成方式，2560 宽上素材卡虚线框的一个角差了 1 级灰（逐像素对比抓到的）。 */
@media (max-width: 1599px) {
  .studio-board, .studio-resource-strip, .studio-board-head, .table-wrap, .header-project-spec { overscroll-behavior-x: contain; }
}

/* ---- 侧栏：笔记本（≥ 1200，MacBook 落在这里）和大屏一样整条展开；平板（768–1199）收窄成 76 宽、图标下面带字 ----
   v1.20.0 起笔记本、平板都收成一列只有图标的窄栏（名字靠悬浮），用户 2026-10-03 在 MacBook 上看了：「左侧栏只有icon没有文字，这样不行，
   改成同时展示icon和文字」。**别再收成只有图标**。平板那一档照项目侧轨的排法（图标在上、字在下），内容区宽度和原来差不多。 */
@media (min-width: 768px) and (max-width: 1199px) {
  .sidebar { width: 76px; padding-right: 6px; padding-left: 6px; }
  /* 只管侧栏里面那几样：控制台顶栏也用 .sidebar-brand / .sidebar-version 画 logo 旁的字和版本号，别连它一起藏 */
  .sidebar :is(.sidebar-brand, .sidebar-chevron, .sidebar-external, .sidebar-section, .sidebar-foot) { display: none !important; }
  .sidebar-head { justify-content: center; padding: 4px 0 0; }
  .sidebar-nav { gap: 6px; }
  .sidebar-item { flex-direction: column; justify-content: center; gap: 6px; height: 54px; padding: 0; font-size: 12px; font-weight: 400; line-height: 1; }
  .sidebar-item.active, .sidebar-item.active:hover { font-weight: 400; }
  .sidebar-divider { margin: 0 8px; }
  .sidebar-item .sidebar-icon { top: 0; }
  .sidebar-item .sidebar-label { max-width: 100%; }
  /* 选中滑块跟着这一档的格子：横向贴着收窄后的格子，高度和格子一样 */
  .sidebar-thumb { left: 6px; width: calc(100% - 12px); height: 54px; }
  /* 未读的小红点挪到图标右上角 */
  .sidebar-item .sidebar-dot { position: absolute; top: 7px; right: 16px; margin: 0; }
  /* 产品切换在 64 宽里横着放不下三颗：竖着排、一颗一行；滑块只会横着摆，这一档不用它，选中那颗自己铺滑块那身颜色 */
  .product-switch { margin-top: 10px; }
  .product-rail { flex-direction: column; border-radius: 12px; }
  .product-tab { flex: none; height: 24px; padding: 0; font-size: 11px; border-radius: 9px; }
  .product-thumb { display: none; }
  .product-tab.active { background: var(--primary-bg); color: var(--on-primary); }
}
/* 屏幕矮（1280×690 一类）、手机抽屉里：十一项加 logo 比屏幕高时，各项别被压扁叠到一起（侧栏是竖着的弹性布局，默认会压），
   保持原高、侧栏自己滚。大屏上一向放得下，不受影响。 */
@media (max-width: 1599px) {
  .sidebar > *, .sidebar-nav > * { flex-shrink: 0; }
  .sidebar { overflow-y: auto; scrollbar-width: none; }
  .sidebar::-webkit-scrollbar { display: none; }
}

/* ---- 手机：侧栏收进抽屉（左上角菜单键，用户定的），内容占满整宽 ---- */
.nav-menu-button, .nav-drawer-backdrop { display: none; }
/* 只在有菜单键的页面（新版主页面）收进抽屉：回退到旧页面（?ui=old，没有菜单键）时侧栏照旧摆着，不至于藏进一个打不开的抽屉 */
@media (max-width: 767px) {
  body:has(#navMenuButton) { grid-template-columns: minmax(0, 1fr); }
  body:has(#navMenuButton) > .app-shell { grid-column: 1; }
  body:has(#navMenuButton) > .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 120;
    width: min(280px, 82vw);
    height: auto;
    padding: max(14px, env(safe-area-inset-top)) 10px max(12px, env(safe-area-inset-bottom));
    overflow-y: auto;
    transform: translateX(-102%);
    transition: transform .24s cubic-bezier(.22, .61, .36, 1);
    box-shadow: none;
  }
  html.nav-drawer-open body:has(#navMenuButton) > .sidebar { transform: none; box-shadow: 0 0 40px rgba(0, 0, 0, .45); }
  .nav-drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 119;
    background: rgba(0, 0, 0, .5);
    opacity: 0;
    pointer-events: none;
    transition: opacity .24s ease;
  }
  html.nav-drawer-open .nav-drawer-backdrop { opacity: 1; pointer-events: auto; }
  /* 帮助的小面板从抽屉里弹出来，要压在抽屉上面 */
  .help-flyout { z-index: 130; }
  .nav-menu-button {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--text);
  }
  .nav-menu-icon {
    width: 20px;
    height: 14px;
    background:
      linear-gradient(currentColor, currentColor) top / 100% 2px no-repeat,
      linear-gradient(currentColor, currentColor) center / 100% 2px no-repeat,
      linear-gradient(currentColor, currentColor) bottom / 100% 2px no-repeat;
    border-radius: 1px;
  }
}
@media (prefers-reduced-motion: reduce) {
  body > .sidebar, .nav-drawer-backdrop { transition: none !important; }
}

/* ---- 顶栏 ---- */
/* 笔记本 / 平板：左边（选择项目 + 剧名 + 那排药丸，药丸一排就四百多像素）按内容要多宽给多宽、放不下才收，模式条在剩下的地方居中，
   从 640 收到 480（四颗各 120，图标加四个字放得下）；语言切换只留国旗。大屏上照旧三栏对称、模式条在整条顶栏正中。 */
@media (min-width: 768px) and (max-width: 1599px) {
  .app-header { grid-template-columns: minmax(0, max-content) minmax(max-content, 1fr) auto; gap: 14px; }
  .mode-switch { width: 480px; }
  .header-tools { gap: 5px; }
  .lang-switch .lang-name, .lang-switch .lang-caret { display: none; }
  .lang-switch .lang-current { padding: 0 8px; }
}
/* 再窄一档（13 寸 MacBook 以下）：模式条 440、去掉模式图标（一颗 110 放不下图标加四个字）；
   1366 以下 380，「选择项目」只留箭头（字还在、读屏照读），间距再紧一点。 */
@media (min-width: 768px) and (max-width: 1479px) {
  .mode-switch { width: 440px; }
  .mode-card-icon { display: none; }
}
@media (min-width: 768px) and (max-width: 1365px) {
  .app-header { gap: 10px; }
  .header-tools { gap: 4px; }
  /* 进了项目顶栏挤：「套餐订阅」只留图标（字还在、读屏照读 aria-label）。 */
  .app-header:has(#pickProjectButton:not(.hidden)) .header-billing-button { width: 34px; padding: 0; }
  .app-header:has(#pickProjectButton:not(.hidden)) .header-billing-label { display: none; }
  .mode-switch { width: 380px; }
  .header-brand .pick-project { width: 48px; padding: 0; justify-content: center; font-size: 0; }
  .header-brand .pick-project .pick-project-icon { width: 10px; height: 16px; }
}
/* 手机：菜单键（非工作台时带一枚 logo）| 项目名 | 右边那排；模式条单独一行。语言切换只留国旗。 */
@media (max-width: 767px) {
  .app-header {
    grid-template-areas: "menu brand tools" "modes modes modes";
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 8px 10px;
    padding: max(8px, env(safe-area-inset-top)) 12px 8px;
  }
  .nav-menu-button { grid-area: menu; gap: 10px; width: auto; padding: 0 8px; }
  .nav-menu-logo {
    width: calc(16px * 738 / 458);
    height: 16px;
    background-color: currentColor;
    -webkit-mask: url("/static/logo-mark.svg?v=1") center / contain no-repeat;
    mask: url("/static/logo-mark.svg?v=1") center / contain no-repeat;
  }
  .app-header:has(#pickProjectButton:not(.hidden)) .nav-menu-logo { display: none; }
  .header-tools { gap: 3px; }
  .lang-switch .lang-name, .lang-switch .lang-caret { display: none; }
  .lang-switch .lang-current { padding: 0 8px; }
  /* 进了项目：一行放不下返回、剧名、语言、主题、铃铛、积分——只留积分，语言 / 主题 / 铃铛回到项目外面再用。
     「选择项目」只留箭头；剧名下那排药丸（画风、集数、时长、比例、叙事）横着滑。模式条单独一行、去掉图标、矮一截。 */
  .app-header:has(#pickProjectButton:not(.hidden)) :is(#langSwitch, #themeToggleButton, #notifyButton, #headerBillingButton, .header-banner) { display: none; }
  /* 「套餐订阅」手机上只留皇冠（侧栏抽屉里已经没有这一项了，顶栏这颗是唯一的入口）。 */
  .header-billing-button { width: 34px; padding: 0; }
  .header-billing-label { display: none; }
  .header-brand { gap: 8px; }
  .header-brand .pick-project { width: 40px; height: 40px; min-height: 40px; padding: 0; justify-content: center; font-size: 0; border-radius: 10px; }
  .header-brand .pick-project .pick-project-icon { width: 10px; height: 16px; }
  .header-project { height: auto; min-width: 0; }
  .header-project h2 { font-size: 18px; }
  .header-project-spec { overflow-x: auto; scrollbar-width: none; }
  .header-project-spec::-webkit-scrollbar { display: none; }
  .mode-switch { width: 100%; }
  .mode-switch .mode-card { height: 44px; padding: 4px 6px; row-gap: 2px; }
  .mode-card-icon { display: none; }
  .mode-card-title { font-size: 13px; }
}
/* 手机：整页的高度按手机浏览器实际看得见的那一块算（iOS Safari 的 100% 会把地址栏后面那截也算进去），侧栏切出来的页面留白收窄、底部让开 Home 横条。 */
@media (max-width: 767px) {
  html, body { height: 100dvh; }
  .app-page { padding: 12px 14px calc(20px + env(safe-area-inset-bottom)); }
  .page-head { margin-bottom: 12px; }
}

/* ---- 工作台三栏 ---- */
/* 笔记本：助手栏从 540 按屏宽收到 380–540（用户定的），分镜页左边的分集列表 260 → 220；中间那一栏（剧本正文、分镜表）拿回来。 */
@media (min-width: 1200px) and (max-width: 1599px) {
  .script-layout { grid-template-columns: minmax(0, 1fr) clamp(360px, 28vw, 540px); }
  .breakdown-columns { grid-template-columns: 196px minmax(0, 1fr) clamp(360px, 28vw, 540px); }
}
/* 视频台（笔记本）：MacBook 只有 790–870 高，上半截不到 420，角色 / 场景 / 道具三条素材栏按比例分只剩卡片顶上一截、镜头描述只剩一行。
   素材那一栏改成整栏能滚、每一类照旧 142 高（看得全）；右边台词（大屏定高 142）和镜头描述（至少 165）加起来比那一栏高，
   改成两块按 2 : 3 分剩下的高度，字多了各自框里滚——两个框都看得见，不用先滚右栏才找到镜头描述。 */
@media (min-width: 1200px) and (max-width: 1599px) {
  .studio-resources { overflow-y: auto; scrollbar-width: thin; }
  .studio-resources .studio-resource-section { flex: none; height: 142px; min-height: 142px; }
  .studio-resources .studio-whitebox-section { height: auto; min-height: 0; }
  .studio-copy-section { display: flex; flex: 2 1 0; min-height: 96px; flex-direction: column; }
  .studio-copy-section.studio-description-section { flex: 3 1 0; min-height: 120px; }
  .studio-dialogue-editor-wrap { flex: 1 1 0; height: auto; min-height: 0; max-height: none; }
}
/* 矮屏（≤ 820 高：MacBook Air 13、「更大字体」档）：顶栏上下各收 4px、模式卡 60 → 50；视频台下面的分镜台从至少 260 收到 200
   （卡片里的图按这一行的高度现算，Board.tsx 的 fitBoardCards，跟着变小），上半截的预览、台词、镜头描述拿回来。 */
@media (min-width: 768px) and (max-width: 1599px) and (max-height: 820px) {
  .app-header { padding-top: 8px; padding-bottom: 8px; }
  .mode-switch .mode-card { height: 50px; row-gap: 2px; padding-top: 4px; padding-bottom: 4px; }
  .video-studio { grid-template-rows: minmax(0, 74fr) minmax(200px, 26fr); }
}
/* 分镜表最后一列：四颗按钮从两行两列改成一列竖排（列宽见 breakdown/model.ts 的 SHOT_COMPACT_COLUMN_WIDTHS）。大屏照旧两行两列（用户 2026-09-24 定的）。 */
@media (max-width: 1599px) {
  .shot-action-inner { grid-template-columns: max-content; }
}

/* ---- 付款弹窗的手机提示（PayDialogs.tsx 的 PayMobileHint）：电脑上不出 ---- */
.pay-mobile-hint { display: none; }
.pay-code-image img { display: block; width: 100%; height: 100%; -webkit-touch-callout: default; }
@media (max-width: 767px) {
  .pay-mobile-hint { display: block; max-width: 300px; margin: 8px auto 0; color: var(--muted); font-size: 12.5px; line-height: 1.6; text-align: center; }
}

/* ---- 手机：侧栏切出来的那几页 ---- */
@media (max-width: 767px) {
  /* 套餐页顶上的余额 / 总用量 / 生成次数：三格挤在一行数字叠在一起，改成三行、名字在左数字在右 */
  .stat-strip { grid-template-columns: minmax(0, 1fr); }
  .stat-cell { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; border-left: 0; border-top: 1px solid var(--line); }
  .stat-cell:first-child { border-top: 0; }
  .stat-cell .stat-value { height: auto; }

  /* 积分明细：表头藏掉，一条一张卡——来源（粗）｜积分值 / 详情 / 时间 */
  #creditsPage .credits-table { width: 100%; min-width: 0 !important; table-layout: auto; }
  #creditsPage .credits-table thead { display: none; }
  #creditsPage .credits-table tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "source amount" "note note" "time time";
    gap: 3px 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--cell-line);
  }
  #creditsPage .credits-table td { display: block; width: auto !important; height: auto; padding: 0 !important; border: 0; white-space: normal; }
  #creditsPage .credits-table td:nth-child(1) { grid-area: time; color: var(--muted); font-size: 12px; }
  #creditsPage .credits-table td:nth-child(2) { grid-area: source; font-weight: 600; }
  #creditsPage .credits-table td:nth-child(3) { grid-area: note; color: var(--muted); }
  #creditsPage .credits-table td:nth-child(4) { grid-area: amount; text-align: right; font-weight: 700; }
  #creditsPage .credits-table td[colspan] { grid-column: 1 / -1; grid-row: 1; text-align: center; }

  /* 我的订单：订单号 ｜ 状态 / 积分 · 付款方式 ｜ 金额 / 创建时间 */
  #ordersPage .orders-table { width: 100%; min-width: 0 !important; table-layout: auto; }
  #ordersPage .orders-table thead { display: none; }
  #ordersPage .orders-table tbody tr {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "id id status" "credits method amount" "time time time";
    align-items: center;
    gap: 4px 10px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--cell-line);
  }
  #ordersPage .orders-table td { display: block; width: auto !important; height: auto; padding: 0 !important; border: 0; }
  #ordersPage .orders-table td:nth-child(1) { grid-area: id; min-width: 0; }
  #ordersPage .orders-table td:nth-child(2) { grid-area: method; color: var(--muted); }
  #ordersPage .orders-table td:nth-child(3) { grid-area: credits; }
  #ordersPage .orders-table td:nth-child(4) { grid-area: amount; text-align: right; font-weight: 700; }
  #ordersPage .orders-table td:nth-child(5) { grid-area: status; text-align: right; }
  #ordersPage .orders-table td:nth-child(6) { grid-area: time; color: var(--muted); font-size: 12px; }
  #ordersPage .orders-table td[colspan] { grid-column: 1 / -1; grid-row: 1; text-align: center; }
  #ordersPage .orders-toolbar { flex-wrap: wrap; gap: 8px; }

  /* 邀请码：码和状态一行，有效期、复制换到第二行 */
  .invite-row { flex-wrap: wrap; row-gap: 6px; }
  .invite-code { white-space: nowrap; }
  .invite-tag { white-space: nowrap; }
  .invite-meta { flex: 1 1 100%; order: 3; margin-left: 0; }

  /* 我的项目：标题一行、搜索框占满下一行 */
  .works-head { flex-wrap: wrap; row-gap: 10px; }
  .works-head h2 { white-space: nowrap; }
  .works-head-divider { display: none; }
  .works-head .works-search { flex: 1 1 100%; width: auto; }
  .works-grid .work-card-new { min-height: 120px; }
}

/* ---- 手机、平板（1200 宽以下）：剧本页「剧本 / 助手」、分镜页「分集 / 分镜 / 助手」一次只露一栏 ----
   mobile-panes.tsx 的页签写 data-mobile-pane；露出来那一栏撑满剩下的高度（页签占了一行，原来按整块算的 100% 不对，改成弹性布局撑）。
   这一档原来 1180 以下剧本页、1100 以下分镜页把助手栏摞到正文下面，正文被挤成一条（iPad 横屏、没开满屏的 MacBook 窗口都会碰到）。 */
@media (max-width: 1199px) {
  #scriptView .script-page-body, #scriptPageBody { display: flex; flex-direction: column; flex: 1; height: auto; min-height: 0; gap: 0; }
  #scriptView .script-layout { flex: 1; height: auto; min-height: 0; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
  .script-layout[data-mobile-pane="assistant"] > .script-main,
  .script-layout[data-mobile-pane="main"] > .script-side { display: none; }
  #scriptView .script-main, #scriptView .script-side { height: auto; min-height: 0; }
  #scriptView .script-side .script-assistant-panel, #scriptView .script-assistant-panel { height: 100%; min-height: 0; border-radius: 10px; }

  .breakdown > .breakdown-columns, .breakdown-columns { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
  .breakdown-columns[data-mobile-pane="episodes"] > :not(.breakdown-col-episodes),
  .breakdown-columns[data-mobile-pane="detail"] > :not(.breakdown-col-detail),
  .breakdown-columns[data-mobile-pane="assistant"] > :not(.breakdown-col-assistant) { display: none; }
  .breakdown-col-assistant { grid-column: auto; }
}

/* ---- 手机：工作台的留白、资产库入口 ---- */
.asset-drawer-header-button { display: none; }
@media (max-width: 767px) {
  #mainView { padding: 4px 10px 0; }
  #workflowView, #imageView { padding: 0 0 8px; }

  #scriptView .script-page-body, #scriptPageBody { padding: 0 0 8px; }
  .breakdown-col { padding: 8px 12px 12px; }

  /* 资产库：顶栏那颗图标打开、抽屉占满整屏；侧边把手只在打开时露成左边一条窄边（手机上没有 Esc，点它收起） */
  .app-header:has(#pickProjectButton:not(.hidden)) .asset-drawer-header-button:not(.hidden) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    min-height: 0;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
  }
  .asset-drawer { width: 100vw; }
  .asset-drawer.collapsed { transform: translateX(100%); }
  .asset-drawer-toggle { align-self: stretch; justify-content: center; width: 32px; padding: 0; gap: 0; border-radius: 0; }
  .asset-drawer-toggle .asset-drawer-toggle-label, .asset-drawer-toggle .asset-drawer-toggle-icon { display: none; }
  .asset-drawer-body { padding: 12px; border-radius: 0; }
  #assetDrawer .asset-drawer-body .asset-grid { grid-template-columns: minmax(0, 1fr); }
}
/* 手机：分镜表一行一张卡（表头藏掉，各格按位置排；列宽那套写在 col 和表上的内联宽度一律不认）。平板上那一栏够宽，紧凑列宽放得下，照旧是表 */
@media (max-width: 767px) {
  #workflowView .shot-table { display: block; width: 100% !important; min-width: 0 !important; margin-top: 0; table-layout: auto; }
  #workflowView .shot-table colgroup, #workflowView .shot-table thead { display: none; }
  #workflowView .shot-table tbody { display: block; }
  #workflowView .shot-table tbody tr {
    display: grid;
    grid-template-columns: 24px minmax(84px, auto) minmax(0, 1fr);
    grid-template-areas: "check identity visual" "content content content" "actions actions actions";
    gap: 8px 10px;
    padding: 12px;
  }
  /* 卡片的底色、圆角、状态色（选中描边、正在展开的蓝）从每一格挪到整行上：一行现在是一个盒子 */
  #workflowView .shot-table tbody tr:not(.empty-row) { margin-bottom: 8px; border-radius: 10px; background: var(--surface-soft); }
  #workflowView .shot-table tbody tr.picked { box-shadow: inset 0 0 0 1px var(--green); }
  #workflowView .shot-table tbody tr.expanding { background: var(--blue-soft); }
  #workflowView .shot-table tbody td {
    display: block; width: auto !important; height: auto !important; min-height: 0; padding: 0 !important; border: 0;
    background: transparent !important; box-shadow: none !important; border-radius: 0 !important;
  }
  #workflowView .shot-table tbody td:nth-child(1) { grid-area: check; }
  #workflowView .shot-table tbody td:nth-child(2) { grid-area: identity; }
  #workflowView .shot-table tbody td:nth-child(3) { grid-area: visual; }
  #workflowView .shot-table tbody td:nth-child(4) { grid-area: content; }
  #workflowView .shot-table tbody td:nth-child(5) { grid-area: actions; text-align: left; }
  #workflowView .shot-table tbody td[colspan] { grid-column: 1 / -1; grid-row: auto; }
  /* 四颗按钮两列：「生成分镜图提示词」连价签一行占满（半行放不下），下面「去生图 | 详情」「首帧 / 直出」 */
  #workflowView .shot-action-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-content: stretch; }
  #workflowView .shot-action-inner > .shot-action-command.is-prompt { grid-column: 1 / -1; min-width: 0; }
}
/* 手机：分镜页「分镜」那一栏整栏一起滚（2026-10-02）。原来照大屏的定高排：上面标题、视觉设计、钩子、页签、工具条一摞就占满一屏，
   表格区（保底 260）被顶到屏幕外、出镜资产那一格被挤成 0 高，这一栏又不能滚——镜头卡、资产卡在手机上根本看不到。
   这一档让这一栏按内容长高、外面那层分栏容器（没有圆角）来滚：带圆角的这一栏本身照旧不是滚动容器（左上角方角缺口那条），
   表格、资产格也不再各自滚，跟着整栏走。分镜页整块也改成弹性撑满（手机顶栏两行，按视口高度算的定高多出 28，底下被裁）。 */
@media (max-width: 767px) {
  .mode-view > .breakdown:not(.hidden) { flex: 1; height: auto; }
  .breakdown-columns[data-mobile-pane="detail"] { overflow-y: auto; grid-template-rows: max-content; align-content: start; }
  /* 横着照旧裁（原来整栏 overflow: hidden）：伸出去的东西不让整栏能左右拖 */
  .breakdown-columns[data-mobile-pane="detail"] > .breakdown-col-detail { overflow: visible; overflow-x: clip; }
  .breakdown-col-detail > .shot-table-wrap { flex: none; min-height: 0; }
  .breakdown-col-detail > .shot-table-wrap > .table-wrap { flex: none; overflow: visible; }
  .breakdown-col-detail .episode-asset-grid { flex: none; overflow: visible; }
  /* 两层工具条（「删除 / 全部选择 / 清空已选 / 已选设为 首帧·直出」，出镜资产那层「角色 / 场景 / 道具 | 全部选择 / 清空已选 / 生图提示词」）
     一行放不下，折行：原来伸出这一栏被裁掉，「直出」「清空已选」「生图提示词」在手机上点不到 */
  .breakdown-col-detail .asset-subtabs { flex-wrap: wrap; row-gap: 8px; }
}
/* 手机：剧本页剧集列表一行排成两行（2026-10-02）。原来是 900 以下那套六列，定宽几列加起来比手机上这一行还宽，
   集名那一列被挤成 0 宽、一个字都看不见。这一档去掉序号（和「第 N 集」重复），状态挪到第二行，「编辑正文」竖着居中。 */
@media (max-width: 767px) {
  .script-episode-row {
    grid-template-columns: 18px auto minmax(0, 1fr) auto;
    grid-template-areas: "check num title edit" "check status status edit";
    gap: 4px 10px;
    padding: 10px 12px;
  }
  .script-episode-row > input { grid-area: check; }
  .script-episode-index { display: none; }
  .script-episode-num { grid-area: num; }
  .script-episode-title { grid-area: title; }
  .script-episode-status { grid-area: status; text-align: left; }
  .script-episode-edit { grid-area: edit; }
}
/* 手机、平板（1200 宽以下）：图片任务表一行一张卡（九列在这一档放不下，操作列被截）；平板上一行两张 */
@media (max-width: 1199px) {
  #imageView #taskTable { display: block; width: 100% !important; min-width: 0 !important; margin-top: 0; table-layout: auto; }
  #imageView #taskTable colgroup { display: none; }
  /* 表头藏掉之后「全选」跟着没了，平板、手机上只能一张张勾（2026-10-02）：表头只留勾选那一格，铺成卡片上面一整条「☐ 全选」，照旧吸顶 */
  #imageView #taskTable thead { display: block; position: sticky; top: 16px; z-index: 1; margin-bottom: 8px; }
  #imageView #taskTable thead tr { display: flex; }
  #imageView #taskTable thead th { display: none; }
  #imageView #taskTable thead th.check-column {
    display: flex; flex: 1; align-items: center; gap: 8px; position: static; width: auto;
    padding: 9px 12px; border-radius: 10px; font-weight: 400;
  }
  #imageView #taskTable thead th.check-column input { top: 0; }
  #imageView #taskTable thead th.check-column::after { content: "全选"; }
  #imageView #taskTable tbody { display: block; }
  #imageView #taskTable tbody tr {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr) minmax(0, 1fr) auto;
    grid-template-areas:
      "check name name status"
      "check output output output"
      "check refs refs refs"
      "check kind size elapsed"
      "actions actions actions actions";
    align-items: center;
    gap: 6px 10px;
    padding: 12px;
  }
  #imageView #taskTable tbody tr:not(.empty-row) { margin-bottom: 8px; border-radius: 10px; background: var(--surface-soft); }
  #imageView #taskTable tbody tr.picked { box-shadow: inset 0 0 0 1px var(--green); }
  #imageView #taskTable tbody tr.queued { background: var(--blue-faint); }
  #imageView #taskTable tbody tr.current { background: var(--blue-soft); }
  #imageView #taskTable tbody td {
    display: block; width: auto !important; height: auto !important; min-height: 0; padding: 0 !important; border: 0;
    background: transparent !important; box-shadow: none !important; border-radius: 0 !important;
  }
  #imageView #taskTable tbody td:nth-child(1) { grid-area: check; align-self: start; }
  #imageView #taskTable tbody td:nth-child(2) { grid-area: status; }
  #imageView #taskTable tbody td:nth-child(3) { grid-area: kind; color: var(--muted); }
  #imageView #taskTable tbody td:nth-child(4) { grid-area: name; font-weight: 600; }
  #imageView #taskTable tbody td:nth-child(5) { grid-area: refs; }
  #imageView #taskTable tbody td:nth-child(5):empty { display: none; }
  #imageView #taskTable tbody td:nth-child(6) { grid-area: output; }
  #imageView #taskTable tbody td:nth-child(7) { grid-area: size; color: var(--muted); }
  #imageView #taskTable tbody td:nth-child(8) { grid-area: elapsed; color: var(--muted); text-align: right; }
  #imageView #taskTable tbody td:nth-child(9) { grid-area: actions; }
  #imageView #taskTable tbody td[colspan] { grid-column: 1 / -1; grid-row: auto; }
  #imageView .row-actions { justify-content: flex-start; flex-wrap: wrap; }
}
@media (min-width: 768px) and (max-width: 1199px) {
  #imageView #taskTable tbody { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  #imageView #taskTable tbody tr:not(.empty-row) { margin-bottom: 0; }
  #imageView #taskTable tbody tr.empty-row { grid-column: 1 / -1; }
}
/* 手机：图片生成页参数两个一行（原来一项占一整行，八项把任务表挤出屏幕），整页往下滚，任务表至少占一屏多一点 */
@media (max-width: 767px) {
  #imageView { overflow-y: auto; }
  #imageView > .image-type-bar, #imageView > .control-band, #imageView > .warning-band { flex: none; }
  #imageView > .image-type-bar { padding: 12px; gap: 10px; }
  #imageView > .image-type-bar .search-field { width: auto; }
  #imageView > .provider-band { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; padding: 12px; }
  #imageView > .provider-band > .field { width: auto; min-width: 0; }
  #imageView > .provider-band > :not(.field) { grid-column: 1 / -1; }
  #imageView > .provider-band .image-commands { justify-content: flex-end; margin-left: 0; padding-left: 0; }
  #imageView > .task-section { flex: none; height: max(420px, calc(100dvh - 190px)); }
}
/* 手机：视频台。分镜台（换镜头的唯一入口）一直钉在下面、矮一截，卡片里的图跟着这一行的高度现算；上面两块按页签切——
   「预览」是预览窗在上、角色 / 场景 / 道具素材在下，整块能滚；「台词与生成」是右栏那一套。 */
@media (max-width: 767px) {
  .video-studio-view { padding: 0 0 8px; }
  .video-studio { grid-template-rows: minmax(0, 1fr) 232px; height: calc(100% - 45px); }
  .video-studio > .studio-center.mobile-active { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; }
  .studio-center > .studio-preview-panel { order: -1; flex: none; height: min(300px, 60vw); }
  .studio-center > .studio-resources { flex: none; overflow: visible; }
  .studio-center > .studio-resources .studio-resource-section { flex: none; height: 142px; min-height: 142px; }
  .studio-board-head { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .studio-board-head::-webkit-scrollbar { display: none; }
}

/* ==== 项目工作区：方案一「创作侧轨」（用户 2026-10-02：「使用方案一，做成正式版本」）====
   大屏（≥ 768）进了项目（body.project-workspace，nav.ts 的 switchPage 挂上 / 摘掉）：
   - 左边全站侧栏换成项目侧轨（ProjectRail.tsx，画在同一个 #sidebar 里）：logo 回首页、剧本 / 分镜 / 图片 / 视频四步、资产库、帮助；
   - 顶栏收成一行（52 高）：「选择项目」剧名 设定药丸 …… 右边那排；四张模式卡大屏上藏掉（手机还在用）；
   - 资产库不再是右边缘那只竖把手：从侧轨旁边推出来一块，不盖顶栏、不盖侧轨，右边给把手留的 64 收回来。
   只有新版页面会挂 project-workspace（回退到旧页面一条都不生效）；手机（≤ 767）一条都不生效，照旧是抽屉 + 顶栏那排模式卡。
   出了项目（首页、我的项目……）侧栏、顶栏照旧。经过：《界面与账号.md》十八。 */
.project-rail { display: none; }
.asset-drawer-close { display: none; }
@media (min-width: 768px) {
  /* ---- 侧轨 ---- */
  /* 上边距 6：logo 那一格 40 高，中线落在 26，正好对着顶栏那一行（8 + 36 / 2） */
  body.project-workspace .sidebar { width: 76px; padding: 6px 8px 14px; }
  body.project-workspace .sidebar > :not(.project-rail) { display: none; }
  body.project-workspace .project-rail {
    --rail-active-bg: rgba(255, 255, 255, .1);
    --rail-active-text: #ffffff;
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    min-height: 0;
  }
  :root:not([data-theme="dark"]) body.project-workspace .project-rail { --rail-active-bg: rgba(0, 0, 0, .08); --rail-active-text: var(--text); }
  body.project-workspace .project-rail-logo {
    display: grid;
    place-items: center;
    flex: none;
    height: 40px;
    min-height: 0;
    margin: 0 0 10px;
    padding: 0;
    border: 0;
    border-radius: 10px;
    background: none;
    color: var(--text);
    cursor: pointer;
  }
  body.project-workspace .project-rail-logo:hover { background: var(--surface-soft); }
  body.project-workspace .project-rail-logo .sidebar-logo { --brand-mark: 15px; }
  body.project-workspace .project-rail-stages { position: relative; display: flex; flex: none; flex-direction: column; gap: 4px; }
  /* 选中那一步的浅底是一块滑块（.project-rail-thumb），切步骤时从旧那一格滑到新那一格（用户 2026-10-09：「左侧的 剧本、分镜、图片、视频，按钮也要滑动动画」）；
     位置由 ProjectRail.tsx 按当前那一格的 offsetTop 用 transform 摆，过渡和全站侧栏的滑块一样（.32 秒）。选中那一格自己不铺底色 */
  body.project-workspace .project-rail-thumb { position: absolute; top: 0; left: 0; z-index: 0; width: 100%; height: 66px; border-radius: 12px;
    background: var(--rail-active-bg); opacity: 0; pointer-events: none; will-change: transform;
    transition: transform .32s cubic-bezier(.22, .61, .36, 1), opacity .16s ease; }
  body.project-workspace .project-rail-thumb.shown { opacity: 1; }
  body.project-workspace .project-rail-thumb.instant { transition: none; }
  body.project-workspace .project-rail-stages > .project-rail-item { z-index: 1; }
  /* 一格：图标、两个字、（四步才有的）一条细条，竖着排、整格居中。 */
  body.project-workspace .project-rail-item {
    position: relative;
    display: flex;
    flex: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    height: 66px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 12px;
    background: transparent;
    color: var(--muted);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: .02em;
    cursor: pointer;
  }
  body.project-workspace .project-rail-item:hover { background: var(--surface-soft); color: var(--text); }
  body.project-workspace .project-rail-item.current,
  body.project-workspace .project-rail-item.current:hover { background: transparent; color: var(--rail-active-text); }
  /* 资产库开着：这一格亮成强调色（抽屉是从它旁边推出来的） */
  body.project-workspace .project-rail-item.open,
  body.project-workspace .project-rail-item.open:hover { background: var(--accent-soft); color: var(--accent); }
  /* 图标画在 ::before 上：遮罩会连子元素一起裁，资产库的数字角标挂在这一格里，不能被裁掉 */
  body.project-workspace .project-rail-icon { position: relative; display: block; flex: none; width: 22px; height: 22px; }
  body.project-workspace .project-rail-icon::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: currentColor;
    -webkit-mask: var(--rail-icon) center / contain no-repeat;
    mask: var(--rail-icon) center / contain no-repeat;
  }
  body.project-workspace .project-rail-icon[data-icon="script"] { --rail-icon: url("/static/icon-mode-script.png"); }
  body.project-workspace .project-rail-icon[data-icon="workflow"] { --rail-icon: url("/static/icon-mode-storyboard.png"); }
  body.project-workspace .project-rail-icon[data-icon="image"] { --rail-icon: url("/static/icon-mode-image.png"); }
  body.project-workspace .project-rail-icon[data-icon="video"] { --rail-icon: url("/static/icon-mode-video.png"); }
  body.project-workspace .project-rail-icon[data-icon="assets"] { --rail-icon: url("/static/icon-asset-library.png"); }
  body.project-workspace .project-rail-icon[data-icon="help"] { --rail-icon: url("/static/icon-help.png"); }
  body.project-workspace .project-rail-badge {
    position: absolute;
    top: -7px;
    left: 14px;
    min-width: 18px;
    height: 16px;
    padding: 0 5px;
    box-sizing: border-box;
    border-radius: 8px;
    background: var(--line-strong);
    color: var(--text);
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 0 0 2px var(--sidebar-bg);
  }
  /* 四步底下那条细条（口径见 stage-progress.ts）：做完的部分绿；正在跑时换成蓝、按这一趟的进度走；没开始只剩底槽 */
  body.project-workspace .project-rail-meter { display: block; flex: none; width: 26px; height: 3px; border-radius: 3px; background: var(--line-strong); overflow: hidden; }
  body.project-workspace .project-rail-meter > i { display: block; height: 100%; border-radius: inherit; background: var(--success); transition: width .2s ease; }
  body.project-workspace .project-rail-meter[data-state="running"] > i { background: var(--accent); }
  body.project-workspace .project-rail-sep { display: block; flex: none; height: 1px; margin: 10px; background: var(--line); }
  body.project-workspace .project-rail-spacer { flex: 1; min-height: 10px; }

  /* ---- 顶栏：一行 52 高。模式卡藏掉，剧名和药丸并排；右边那排的右边线照旧和下面内容底板的右边对齐（底板右边收到 22 + 8） ---- */
  body.project-workspace .app-header { grid-template-areas: "brand tools"; grid-template-columns: minmax(0, 1fr) auto; padding-top: 8px; padding-bottom: 8px; }
  body.project-workspace #appShell:has(#mainView:not(.hidden)) > .app-header { padding-right: 30px; }
  body.project-workspace #modeSwitch { display: none; }
  body.project-workspace .header-tools { align-self: center; }
  /* 和右上角那排按钮一样 34 高、圆角 12（用户 2026-10-08 改的，见上面 .pick-project 那块） */
  body.project-workspace .header-brand .pick-project { height: 34px; min-height: 34px; padding: 0 14px 0 12px; gap: 8px; border-radius: 12px; font-size: 14px; }
  body.project-workspace .header-project { height: 36px; flex-direction: row; align-items: center; gap: 14px; }
  body.project-workspace .header-project h2 { font-size: 20px; }
  /* 一行放不下时先裁药丸（右端被这一格裁掉，和原来第二行时一样不换行、不挤成一字一行），剧名留着；剧名太长才省略，最多占一半 */
  body.project-workspace .header-project > h2 { flex: 0 0 auto; max-width: 50%; min-width: 0; }
  body.project-workspace .header-project > .header-project-spec { flex: 0 1 auto; min-width: 0; }
  body.project-workspace #mainView { padding-right: 22px; }
  /* 剧本页、分镜页主体按视口高度算（原来按 98 高的顶栏减 122）：顶栏少了 46，这里跟着少 46 */
  body.project-workspace .mode-view > .breakdown:not(.hidden) { height: calc((100vh - var(--top-banner-height, 0px)) - 76px); }
  body.project-workspace .script-settings { max-height: calc((100vh - var(--top-banner-height, 0px)) - 92px); }

  /* ---- 资产库：抽屉（用户 2026-10-02：「资产库也改成抽屉」）。贴着侧轨的右边线，从顶栏下面一直到屏幕底，从侧轨底下滑出来——
     侧轨压在抽屉上面（z-index 71 > 70），收起时整块缩回侧轨底下；只有不挨着边的右边两个角是圆的，和原来右边那只抽屉左右对调。
     遮罩只压正文（侧轨、顶栏照旧点得到）。层级：资产库遮罩 65 < 抽屉 70 < 侧轨 71 < 通知遮罩 73 < 帮助浮层 74 < 通知抽屉 75 < 顶栏 80 ---- */
  body.project-workspace .sidebar { z-index: 71; }
  body.project-workspace .asset-drawer {
    top: calc(var(--top-banner-height, 0px) + 52px);
    right: auto;
    bottom: 0;
    left: 76px;
    z-index: 70;
    /* 宽度照原来抽屉那个算法（资产卡固定三列，卡片和原来一样大），另外右边至少露出 96 的正文 */
    width: min(1680px, 80vw, calc(100vw - 172px));
    /* 慢一点，让人看清它是从侧轨里推出来的（和原来右边那只抽屉一个速度） */
    transition: transform .36s cubic-bezier(.22, .61, .36, 1), visibility 0s linear 0s;
  }
  body.project-workspace .asset-drawer.collapsed {
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform .28s cubic-bezier(.4, 0, .2, 1), visibility 0s linear .28s;
  }
  body.project-workspace .asset-drawer-toggle { display: none; }
  body.project-workspace .asset-drawer-body {
    position: relative;
    border: 0;
    border-right: 1px solid var(--line);
    border-radius: 0 14px 14px 0;
    box-shadow: 12px 0 28px rgba(0, 0, 0, .28);
  }
  :root:not([data-theme="dark"]) body.project-workspace .asset-drawer-body { box-shadow: 12px 0 28px rgba(0, 0, 0, .12); }
  body.project-workspace .asset-drawer-head { padding-right: 44px; }
  body.project-workspace .asset-drawer-close {
    position: absolute;
    top: 12px;
    right: 14px;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    min-height: 0;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--surface);
    color: var(--text);
  }
  body.project-workspace .asset-drawer-close .close-icon { width: 14px; height: 14px; }
  body.project-workspace .asset-drawer-scrim { top: calc(var(--top-banner-height, 0px) + 52px); left: 76px; z-index: 65; }
  /* 帮助浮层（连备案那一块）在侧轨右边出来，抽屉开着时也要压在抽屉上面 */
  body.project-workspace .help-flyout { z-index: 74; }
  /* 通知抽屉（点铃铛）：别的页顶栏 60 高、抽屉从 72 起（顶栏下面空 12）；项目里顶栏 52，照同一个关系从 64 起 */
  body.project-workspace .notify-scrim,
  body.project-workspace .notify-drawer { top: 64px; }
  body.project-workspace .notify-scrim { z-index: 73; }
}
/* 剧本页主体：1200 以下是「剧本 / 助手」分栏页签那一套（弹性撑满，不按视口高度算），只管 1200 以上 */
@media (min-width: 1200px) {
  body.project-workspace #scriptView .script-page-body { height: calc((100vh - var(--top-banner-height, 0px)) - 76px); }
}
/* 平板（768–1199）分镜页上面多一行「分集 / 分镜 / 助手」分栏页签：按视口高度算的那个定高再减不掉这一行，底下「生成全集分镜」被裁掉一截
   （原来就是这样）。这一档改成弹性撑满剩下的高度，和剧本页那一栏同一个做法。 */
@media (min-width: 768px) and (max-width: 1199px) {
  body.project-workspace .mode-view > .breakdown:not(.hidden) { flex: 1; height: auto; }
}
/* 矮屏（≤ 820 高）：分镜那一栏里表格区的保底从 260 降到 200。iPad 横屏（700 高）上面几块（标题、视觉设计、钩子、页签、工具条）
   加上底部按钮一共要 340 左右，表格再保底 260 就超出这一栏 22，最底下「生成全集分镜」被裁掉一截（这一栏不能改成能滚，见上面 260 那条的注释）。
   200 照样露得出表头和一行半。 */
@media (min-width: 768px) and (max-height: 820px) {
  body.project-workspace .breakdown-col-detail > .shot-table-wrap { min-height: 200px; }
}
/* 窄屏的资产库：1100 以下几乎铺满（让出侧轨，两边各留 8），900 以下（竖着拿的平板）一列——两列时卡片上的按钮挤得截字 */
@media (min-width: 768px) and (max-width: 1100px) {
  body.project-workspace .asset-drawer { width: calc(100vw - 92px); }
}
@media (min-width: 768px) and (max-width: 899px) {
  body.project-workspace .asset-drawer-body .asset-grid { grid-template-columns: minmax(0, 1fr); }
}
/* 768–1365 宽「选择项目」只剩箭头（见上面笔记本那几档）：跟着顶栏矮下来，收成 34 见方（和右上角那排一样高） */
@media (min-width: 768px) and (max-width: 1365px) {
  body.project-workspace .header-brand .pick-project { width: 34px; padding: 0; font-size: 0; }
}
@media (min-width: 768px) and (prefers-reduced-motion: reduce) {
  body.project-workspace .asset-drawer,
  body.project-workspace .asset-drawer.collapsed,
  body.project-workspace .project-rail-meter > i,
  body.project-workspace .project-rail-thumb { transition: none; }
}

/* ---- 手机：项目底部步骤栏 + 顶栏（2026-10-03） ----
   用户：「这次对顶部四个按钮的改动方案，也要同步到手机端」，在「底部步骤栏」和「抽屉里的侧轨」里选了前者。大屏进了项目左边是项目侧轨；
   手机上同一套放在屏幕最底下一条：剧本 / 分镜 / 图片 / 视频 / 资产库，图标 + 字 + 细条（口径 stage-progress.ts，组件 ProjectRail.tsx 末尾）。
   这一条是 #appShell 那一列的最后一格，内容区自己让出来，不用另算高度。顶栏那排模式卡、顶栏右边的资产库图标挪进了这一条。
   顶栏剩下两行：菜单 | 返回 | 剧名 | 积分；下面一整行是那排设定药丸（用户：「手机浏览的时候，顶部的药丸栏右边被遮住了一大半」——
   原来药丸挤在剧名底下、夹在菜单和积分中间，只露出两颗，后面的横着滑也看不见）。放不下就折行，不再横着滑（横着滑的容器会把药丸的下拉裁掉）。
   资产库抽屉在手机上照旧铺满，只是让出这一条：再点「资产库」或任一步就收起（和大屏点侧轨一样）。 */
.project-stagebar { display: none; }
@media (max-width: 767px) {
  body.project-workspace { --stagebar-height: calc(58px + env(safe-area-inset-bottom, 0px)); }
  body.project-workspace .project-stagebar {
    --rail-active-bg: rgba(255, 255, 255, .1);
    --rail-active-text: #ffffff;
    display: flex;
    flex: none;
    gap: 2px;
    box-sizing: border-box;
    height: var(--stagebar-height);
    padding: 4px 6px env(safe-area-inset-bottom, 0px);
    border-top: 1px solid var(--line);
    background: var(--sidebar-bg);
  }
  :root:not([data-theme="dark"]) body.project-workspace .project-stagebar { --rail-active-bg: rgba(0, 0, 0, .08); --rail-active-text: var(--text); }
  body.project-workspace .project-stagebar-item {
    position: relative;
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    height: 50px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  body.project-workspace .project-stagebar-item.hidden { display: none; }
  body.project-workspace .project-stagebar-item.current { background: var(--rail-active-bg); color: var(--rail-active-text); }
  /* 资产库开着：这一格亮成强调色（和侧轨那一格一样） */
  body.project-workspace .project-stagebar-item.open { background: var(--accent-soft); color: var(--accent); }
  body.project-workspace .project-stagebar-icon { position: relative; display: block; flex: none; width: 20px; height: 20px; }
  body.project-workspace .project-stagebar-icon::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: currentColor;
    -webkit-mask: var(--rail-icon) center / contain no-repeat;
    mask: var(--rail-icon) center / contain no-repeat;
  }
  body.project-workspace .project-stagebar-icon[data-icon="script"] { --rail-icon: url("/static/icon-mode-script.png"); }
  body.project-workspace .project-stagebar-icon[data-icon="workflow"] { --rail-icon: url("/static/icon-mode-storyboard.png"); }
  body.project-workspace .project-stagebar-icon[data-icon="image"] { --rail-icon: url("/static/icon-mode-image.png"); }
  body.project-workspace .project-stagebar-icon[data-icon="video"] { --rail-icon: url("/static/icon-mode-video.png"); }
  body.project-workspace .project-stagebar-icon[data-icon="assets"] { --rail-icon: url("/static/icon-asset-library.png"); }
  body.project-workspace .project-stagebar-badge {
    position: absolute;
    top: -6px;
    left: 13px;
    min-width: 18px;
    height: 16px;
    padding: 0 5px;
    box-sizing: border-box;
    border-radius: 8px;
    background: var(--line-strong);
    color: var(--text);
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 0 0 2px var(--sidebar-bg);
  }
  body.project-workspace .project-stagebar-meter { display: block; flex: none; width: 22px; height: 3px; border-radius: 3px; background: var(--line-strong); overflow: hidden; }
  body.project-workspace .project-stagebar-meter.blank { background: transparent; }
  body.project-workspace .project-stagebar-meter > i { display: block; height: 100%; border-radius: inherit; background: var(--success); transition: width .2s ease; }
  body.project-workspace .project-stagebar-meter[data-state="running"] > i { background: var(--accent); }

  /* 顶栏：模式卡那一排、右边的资产库图标都挪进了底部这一条；剧名那一行四格，药丸单独一整行 */
  body.project-workspace .app-header {
    grid-template-areas: "menu back title tools" "spec spec spec spec";
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    align-items: center;
  }
  body.project-workspace #modeSwitch { display: none; }
  body.project-workspace .app-header .asset-drawer-header-button { display: none !important; }
  body.project-workspace .app-header .header-brand,
  body.project-workspace .app-header .header-project { display: contents; }
  body.project-workspace .app-header .pick-project { grid-area: back; }
  body.project-workspace .app-header #breakdownTitle { grid-area: title; min-width: 0; }
  body.project-workspace .app-header .header-project-spec { grid-area: spec; min-width: 0; overflow: visible; }
  /* 药丸是一整颗、五段（画风 / 集数 / 时长 / 比例 / 叙事），手机上收紧：段内边距 8 → 4、按钮 4 → 2，叙事方式那颗的「i」藏掉
     （它是鼠标悬浮出说明的，手机上点不出来）——390 宽的手机一行放得下，更窄的（360）折成两行。
     下拉不再贴着各自那一段往右长（右边两段的下拉会伸出屏幕），一律和整颗药丸一样宽：药丸当定位框，各段不当；
     段间那道竖线原来是挂在段上的绝对定位伪元素，段不当定位框之后改用背景画，位置、长短照旧（上下各让 4）。 */
  body.project-workspace .app-header .header-project-spec .script-spec-pill {
    --spec-sep: rgba(255, 255, 255, .12);
    position: relative;
    flex-wrap: wrap;
    height: auto;
    min-height: 28px;
    padding: 0 4px;
  }
  :root:not([data-theme="dark"]) body.project-workspace .app-header .header-project-spec .script-spec-pill { --spec-sep: var(--line); }
  body.project-workspace .app-header .header-project-spec .script-spec-input { position: static; height: 28px; padding: 0 4px; }
  body.project-workspace .app-header .header-project-spec .script-spec-input + .script-spec-input::before { display: none; }
  body.project-workspace .app-header .header-project-spec .script-spec-input + .script-spec-input {
    background: linear-gradient(var(--spec-sep), var(--spec-sep)) left center / 1px 20px no-repeat;
  }
  body.project-workspace .app-header .header-project-spec .script-spec-ratio-button { padding-left: 2px; padding-right: 2px; }
  body.project-workspace .app-header .header-project-spec .script-narrative-info { display: none; }
  body.project-workspace .app-header .header-project-spec .ratio-popover,
  body.project-workspace .app-header .header-project-spec .visual-style-popover { left: 0; right: 0; min-width: 0; }
  /* 画风那个下拉原来按「屏幕宽 − 300」定宽（给大屏侧栏留的），手机上只剩 90 像素宽，风格卡挤成一条（原来就这样）：和药丸一样宽 */
  body.project-workspace .app-header .header-project-spec .script-spec-visual .visual-style-popover { width: auto; }

  /* 资产库抽屉、遮罩让出底部这一条 */
  body.project-workspace .asset-drawer,
  body.project-workspace .asset-drawer-scrim { bottom: var(--stagebar-height); }
}
@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  body.project-workspace .project-stagebar-meter > i { transition: none; }
}

/* ==================== 页头和顶栏齐平：我的项目 / Minimo3D / 3D 导演台（2026-10-07） ====================
   用户：「Minimo3D，3D导演台，我的项目，这3个页面，标题字体的大小改成一致 / 内容区域距顶部的高度，和首页中影院横幅距顶部的高度保持一致 /
   Minimo3D右侧 生成服务状态 的状态栏，放到左侧，避免整体上移后和上方的UI重叠」。
   这三页上顶栏只有右上角那一排（左边空着）：顶栏改成浮在外壳右上角、只占那一排的宽，页面从最顶上排——标题行的中线对上那一排按钮（29），
   标题行下面的内容从 70 开始，和首页影院横幅一样。顶栏照旧 z-index 80、右内边距照旧跟着底板的右边，那一排按钮的位置一像素不动。
   body.page-flush-header 由两边外壳的 switchPage 挂。只管 ≥ 1200：再窄的话标题行会伸到右上角那一排底下，照旧是顶栏一行、页面在下面。
   2026-10-08 首页也挂上（只有新版，旧页面的首页还是原来那样），见下面「首页」那几条。 */
@media (min-width: 1200px) {
  body.page-flush-header .app-shell { position: relative; }
  body.page-flush-header .app-shell > .app-header {
    position: absolute;
    top: 0;
    right: 0;
    width: auto;
    grid-template-areas: "tools";
    grid-template-columns: auto;
    gap: 0;
    padding-left: 0;
    background: none;
    /* 顶栏浮在页面那层滚动上，右内边距那一截正好压着滚动条的上半截：顶栏自己不接鼠标，只有那一排按钮接，滚动条从顶上就拖得动 */
    pointer-events: none;
  }
  /* 那一排按钮底下铺一层页面底色（看不出来）：页面里有吸顶的那一行时，浏览器把它单独合成一层，浮在上面的顶栏跟着被合成，
     透明底的合成层里字只给灰度抗锯齿，套餐订阅、积分那几个字比原来虚一圈（2026-10-08 在 1 倍、1.5 倍屏上数过字边的彩色像素：铺之前 0，铺了和原来一样）。
     只铺按钮那一排、不铺整个顶栏：顶栏的右内边距压着滚动条，铺满会把滚动条顶上那一截盖掉 */
  body.page-flush-header .app-shell > .app-header > .header-tools { pointer-events: auto; background: var(--bg); }
  body.page-flush-header .app-header :is(.header-brand, .mode-switch) { display: none; }
  /* 我的项目：标题行就是搜索框那 30 高（14–44，中线 29），下面留 26 → 面板从 70 开始 */
  body.page-flush-header #worksPage { padding-top: 14px; }
  body.page-flush-header #worksPage .works-head { padding-bottom: 26px; }
  /* Minimo3D：页头 44 高（7–51，中线 29），页面的 gap 12 再加 7 → 三栏从 70 开始 */
  body.page-flush-header #model3dPage { padding-top: 7px; }
  body.page-flush-header #model3dPage .m3d-top { margin-bottom: 7px; }
  /* 3D 导演台：标题行（新建镜头那颗 32 高）在 13–45，说明那一行算内容、从 70 开始 */
  body.page-flush-header #directorPage { padding-top: 13px; }
  body.page-flush-header #directorPage .director-home-head { gap: 25px; }

  /* 首页（2026-10-08，用户：「右上角的那几个按钮所在的栏位，是一个固定的区域，首页往下翻时，Minimo精选作品的栏位，也固定在这一栏」）：
     原来顶栏单独占一行（0–58）、页面在它下面滚，精选作品那一行吸住时停在顶栏下面，上下两条。现在页面从最顶上排，
     顶上 0–55 铺一条不动的底（页面那层滚动的 ::before，sticky 在 0）：往下滚的创作卡、最近项目从它底下过去，和原来滚到顶栏底下一个样；
     精选作品那一行吸住时就停在这一条里，分类那一排的中线对上右上角按钮（29），按钮浮在它上面。内容照旧从 70 开始（原来是顶栏 58 + 上内边距 12）。
     那一行的上内边距 10 → 13、这一节的上外边距 18 → 15：静止时字一像素不动，吸住时正好铺满 0–55。scroll-padding-top 告诉浏览器顶上 55 被挡着
     （键盘移焦点、scrollIntoView 停在它下面；项目卡「•••」的菜单按它判断滚没滚到看不见的地方）。 */
  body.page-flush-header #homePage.home-create-page { padding-top: 70px; scroll-padding-top: 55px; }
  body.page-flush-header #homePage.home-create-page::before {
    content: "";
    position: sticky;
    top: -70px;
    z-index: 5;
    display: block;
    height: 55px;
    margin: -70px 0 15px;
    background: var(--bg);
  }
  body.page-flush-header #homePage .home-works-section { margin-top: 15px; }
  body.page-flush-header #homePage .home-works-head { top: -70px; padding-top: 13px; }
  /* 我的项目：标题和搜索那一行吸住时也停在这一条里（同一天，用户：「我的项目中，顶部搜索的那一栏，也固定」），搜索框 14–44、中线 29。
     上外边距 32 拆成 18 + 上内边距 14：静止时一像素不动，吸住时 0–70 整块铺着底色，滚上来的卡片从它底下过去。
     contain: paint：吸顶的这一行会被单独合成一层，不加的话 1 倍屏上标题字掉成灰度（数过：830 → 0，加了回到 830）。
     只在这一档加：更窄时搜索框贴着这一行的上边（手机上还贴着左边），聚焦那圈 2px 光晕会被它裁掉 */
  /* 静止时标题和「上传作品」的标题同一个高度（用户 2026-10-08：「我的项目整体往下移，把“我的项目”标题高度和 上传作品 的标题高度对齐」）：
     上外边距 18 → 40，标题从 48 挪到 70（上传作品那一页的标题在 70）。吸住的位置由 top 管、不跟着挪：照旧停在右上角那一排里 */
  body.page-flush-header .works-board .works-board-head { top: -14px; margin-top: 40px; padding-top: 14px; contain: paint; }
  /* 这两页吸住的那一行和右上角那一排在同一条里：顶栏最宽只到它们右边，公告长了收成省略号（全文在悬浮卡片里），盖不到分类和搜索框。
     按左边那一行最宽的时候留：首页标题 + 分类 534、我的项目标题 + 搜索 424，再加左内边距 30 和中间留的空 */
  body.page-flush-header #appShell:has(> #homePage:not(.hidden)) > .app-header { max-width: calc(100% - 600px); }
  body.page-flush-header #appShell:has(> #worksPage:not(.hidden)) > .app-header { max-width: calc(100% - 490px); }
}
/* 矮屏（笔记本那几档里 ≤ 820 高的）：顶栏上下内边距收到 8（「屏幕适配」那一块），右上角那一排在 8–42、中线 25，原来首页内容从 62 开始（顶栏 50 + 12）。
   首页、我的项目按这条线再排一遍：首页内容从 62 开始、吸住时那一条是 0–51（分类那一排 9–41）；我的项目吸住时搜索框 10–40。静止时照旧一像素不动 */
@media (min-width: 1200px) and (max-width: 1599px) and (max-height: 820px) {
  body.page-flush-header #homePage.home-create-page { padding-top: 62px; scroll-padding-top: 51px; }
  body.page-flush-header #homePage.home-create-page::before { top: -62px; height: 51px; margin: -62px 0 11px; }
  body.page-flush-header #homePage .home-works-section { margin-top: 19px; }
  body.page-flush-header #homePage .home-works-head { top: -62px; padding-top: 9px; }
  /* 矮屏上「上传作品」的标题在 62（顶栏收到 50）：上外边距 22 → 36，标题从 48 挪到 62 */
  body.page-flush-header .works-board .works-board-head { margin-top: 36px; padding-top: 10px; }
}

/* ==================== 用户投稿（2026-10-07）：上传作品页、设置页「个人资料」、控制台「作品审核」 ====================
   用户原话：「还需要设计一个用户上传作品的功能，入口放在左侧栏，管理员审核后，可以在首页的精选作品中展示」
   「精选作品下方要显示用户的名字，设置页中要可以设置名字，每个用户还要有一个用户号UID」。
   上传作品页和设置、邀请一样收在一栏里（--page-column-width），面板也是无描边的色块。 */
#showcasePage > .page-head,
#showcasePage > .showcase-intro,
#showcasePage > .panel { max-width: var(--page-column-width); margin-left: auto; margin-right: auto; }
#showcasePage .panel { border: 0; border-radius: 8px; background: var(--surface); box-shadow: none; }
#showcasePage > .panel:nth-of-type(even) { background: var(--surface-alt); }
.showcase-intro { margin-top: -6px; margin-bottom: 14px; color: var(--muted); font-size: var(--font-base); }
.showcase-panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.showcase-panel-head h3 { margin: 0; }
.showcase-byline { color: var(--muted); font-size: 13px; }
.showcase-user-row { display: flex; align-items: center; gap: 10px; max-width: 520px; }
.showcase-user-row input { flex: 1; min-width: 0; }
/* 上传作品页的用户名框不能点、字置灰（改名在设置里，用户 2026-10-07）；「修改用户名」悬浮不变色——
   没类名的按钮悬浮会叠一层纱（「按钮与弹窗」那一块），这一颗单独撤掉。 */
.showcase-user-row input:disabled { color: var(--muted); -webkit-text-fill-color: var(--muted); opacity: 1; pointer-events: none; }
.showcase-user-row button { flex: none; }
#showcaseEditName:hover { background-image: none; }
/* 用户名那一行和下面片名、类型几个框一样宽。 */
.showcase-user-row { max-width: none; }
.showcase-form { display: grid; grid-template-columns: minmax(260px, 420px) minmax(0, 1fr); gap: 24px; align-items: start; }
.showcase-pickers { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.showcase-cover-pick {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  background: var(--surface-soft);
  color: var(--muted);
  overflow: hidden;
}
.showcase-cover-pick.has-image { border-style: solid; border-color: var(--line); }
.showcase-cover-pick img { display: block; width: 100%; height: 100%; object-fit: cover; }
.showcase-video-pick {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  padding: 12px 14px;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  background: var(--surface-soft);
  color: var(--muted);
  text-align: left;
}
.showcase-video-pick strong { color: var(--text); font-weight: 600; word-break: break-all; }
.showcase-fields { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.showcase-fields .field-label { color: var(--muted); font-size: 13px; }
.showcase-fields .field-label:not(:first-child) { margin-top: 6px; }
.showcase-submit-row { display: flex; align-items: center; gap: 14px; margin-top: 4px; }
.showcase-progress { position: relative; flex: 1; max-width: 320px; height: 28px; border-radius: 999px; background: var(--surface-soft); overflow: hidden; }
.showcase-progress span { position: absolute; top: 0; bottom: 0; left: 0; background: color-mix(in srgb, var(--accent) 38%, transparent); transition: width .2s ease; }
.showcase-progress em { position: relative; display: block; color: var(--text); font-size: 12.5px; font-style: normal; line-height: 28px; text-align: center; }
.showcase-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px 14px; }
.showcase-card { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.showcase-card-cover {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: var(--surface-soft);
  overflow: hidden;
  cursor: pointer;
}
.showcase-card-cover img { display: block; width: 100%; height: 100%; object-fit: cover; }
.showcase-card-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.showcase-card-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.showcase-card-title strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.showcase-status { flex: none; padding: 2px 9px; border-radius: 999px; background: var(--surface-soft); color: var(--muted); font-size: 12px; line-height: 1.5; }
.showcase-status.is-pending { color: var(--accent); }
.showcase-status.is-approved { color: var(--success); }
.showcase-status.is-rejected { color: var(--red); }
.showcase-card-meta { color: var(--muted); font-size: 12.5px; }
.showcase-card-reason { margin: 0; color: var(--red); font-size: 12.5px; }
.showcase-card-delete { align-self: flex-start; min-height: 0; padding: 4px 12px; font-size: 12.5px; }
.showcase-card-reward { color: var(--accent); font-size: 12.5px; }
/* 精选激励计划（2026-10-08，用户：「给精选激励计划写一个活动须知，放到合适的位置。我的想法是上了首页就奖励积分2000」）：
   上传作品页顶上一条奖励横条（强调色浅底，和下面两块色块面板区分开），右边「活动须知」是次要款按钮（不带类名）；
   须知是弹窗，提交说明里的《活动须知》长得像链接、点了打开同一个框。横条是 <aside>：别改成 section，下面两块面板按 section 的 nth-of-type 隔行换底色。 */
#showcasePage > .showcase-reward { display: flex; align-items: center; gap: 14px; padding: 16px 20px; background: var(--accent-soft); }
.showcase-reward-icon { display: grid; flex: none; place-items: center; width: 42px; height: 42px; border-radius: 10px; background: var(--surface); }
.showcase-reward-icon .credits-bolt { width: 22px; height: 22px; }
.showcase-reward-text { display: flex; flex: 1; flex-direction: column; gap: 3px; min-width: 0; }
.showcase-reward-kicker { color: var(--accent); font-size: 12.5px; font-weight: 600; }
.showcase-reward-text strong { color: var(--text); font-size: 16px; font-weight: 650; line-height: 1.5; }
.showcase-reward-text strong span { white-space: nowrap; }
.showcase-reward-text strong em { color: var(--accent); font-size: 20px; font-style: normal; font-variant-numeric: tabular-nums; }
.showcase-reward-text > span:last-child { color: var(--muted); font-size: 13px; }
#showcaseRulesOpen { flex: none; }
.showcase-rules-link { min-height: 0; padding: 0; border: 0; background: none; color: var(--accent); font: inherit; vertical-align: baseline; white-space: normal; }
.showcase-rules-link:hover { text-decoration: underline; }
.showcase-rules-dialog { width: min(640px, calc(100vw - 32px)); }
.showcase-rules-body { max-height: min(calc(100vh - 220px), 640px); padding: 4px 24px; overflow-y: auto; }
.showcase-rules-section { padding: 14px 0; }
.showcase-rules-section + .showcase-rules-section { border-top: 1px solid var(--line); }
.showcase-rules-section h3 { margin: 0 0 8px; font-size: 15px; font-weight: 600; }
.showcase-rules-section ol { margin: 0; padding-left: 20px; color: var(--text-muted); font-size: 13.5px; line-height: 1.75; }
.showcase-rules-section li + li { margin-top: 4px; }
@media (max-width: 767px) {
  #showcasePage > .showcase-reward { align-items: flex-start; gap: 12px; padding: 14px 16px; }
  .showcase-reward-icon { display: none; }
  .showcase-reward-text strong { font-size: 15px; }
  .showcase-rules-body { padding: 4px 18px; }
}
/* 封面上那颗播放角标：上传作品页的卡片、首页精选作品里能放的那几张同一个样子（home.css 的 .home-work-play 照这个写）。 */
.showcase-play {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(0, 0, 0, .58);
}
.showcase-play::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 3px;
  width: 0;
  height: 0;
  margin: auto;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 10px solid #fff;
}
@media (max-width: 767px) {
  .showcase-form { grid-template-columns: minmax(0, 1fr); }
}
/* 看片框（首页、上传作品页、控制台三处共用 WorkVideoDialog）：标题下面一行写作者 / 状态。 */
.work-video-heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.work-video-heading h2 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.work-video-byline { color: var(--muted); font-size: 13px; }
/* 控制台「作品审核」那张表和「用户」表里那一格 UID · 名字。 */
.console-showcase-work { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
/* 控制台的表是定宽排的（五列各占五分之一的话「作品」那一格只剩封面）：作品那一列给宽，简介、原因在格子里折行，别压到作者那一格上。 */
#consolePaneShowcase .console-table th:nth-child(1),
#consolePaneShowcase .console-table td:nth-child(1) { width: 40%; }
#consolePaneShowcase .console-table th:nth-child(2),
#consolePaneShowcase .console-table td:nth-child(2) { width: 18%; }
#consolePaneShowcase .console-table th:nth-child(3),
#consolePaneShowcase .console-table td:nth-child(3) { width: 10%; }
#consolePaneShowcase .console-table th:nth-child(4),
#consolePaneShowcase .console-table td:nth-child(4) { width: 16%; }
#consolePaneShowcase .console-table th:nth-child(5),
#consolePaneShowcase .console-table td:nth-child(5) { width: 16%; }
.console-showcase-table td { white-space: normal; }
.console-showcase-text { overflow-wrap: anywhere; }
.console-showcase-cover {
  flex: none;
  width: 128px;
  min-height: 0;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: var(--surface-soft);
  overflow: hidden;
  cursor: pointer;
}
.console-showcase-cover img { display: block; width: 100%; height: 100%; object-fit: cover; }
.console-showcase-text,
.console-showcase-author,
.console-showcase-times { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.console-user-uid { display: block; color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.console-showcase-text span,
.console-showcase-author span,
.console-showcase-times span { color: var(--muted); font-size: 12.5px; }
.console-showcase-text .console-showcase-reason { color: var(--red); }

/* 侧栏「精选激励计划」小标题（2026-10-07，用户：「左侧栏给“上传作品（改为：立即参与）”一个副标题“精选激励计划”，样式参考 … 社区 副标题」）：
   上面一根和「帮助」上方同款的横线，下面一行灰的小字，和导航项的图标同一条左线。两边原来各一支稻穗、字 13，同一天改了
   （用户：「精选激励计划不要稻穗了，并且字体小一点，上方加一根横线」）——别把稻穗加回来。
   侧栏收窄成 76 宽的那一档（768–1199）放不下六个字，小字藏起来，横线留着当分组线。 */
.sidebar-section-label {
  height: 18px;
  margin-bottom: -2px;
  padding: 0 12px;
  color: var(--muted);
  /* 2026-10-07 用户要过「透明度给低一点」（.6）；2026-10-08 选侧栏方案一「统一横栏」时一起改回不透明——没选中的行字都退到 --muted 了，
     小标题再淡一层就看不清（方案说明里写明了，用户看过选的）。
     字 12px / 500 照 LibTV 左侧导航的分组小标题（「创作」「社区」，用户 2026-10-12：「改成一样的」）。 */
  font-size: 12px;
  font-weight: 500;
  line-height: 18px;
  white-space: nowrap;
}
.sidebar-section-label.hidden { display: none; }
@media (min-width: 768px) and (max-width: 1199px) {
  .sidebar-section-label { display: none; }
}

/* ==================== 设置页：方案三「设置清单」（2026-10-07） ====================
   用户从三个方案里选的（演示站在 Assets\设置页方案-1007）：个人资料、账号与安全两组并排，一组一张卡，卡里一行一项
   （左边图标 + 名称，右边当前值 + 箭头），整行可点，改的东西都在弹窗里做；退出登录单独一行放在最下面，红字居中。
   **行不是独立的操作按钮，按下不缩**（用户：「点击这些栏位的时候不需要缩放，这种不算独立的按钮」）：按下效果只点名给按钮体系
   （文件顶上那条 button:active），行用自己的类名就不会缩。
   旧页面（index.html 里的 #settingsPage，回退用）还是原来那几块面板，前面 .settings-account-details、.password-form 那几条照旧给它用。 */
:root { --icon-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='15.5' rx='2.5'/%3E%3Cpath d='M3.5 10h17M8 3v4M16 3v4'/%3E%3C/svg%3E"); }
:root { --icon-phone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6' y='2.5' width='12' height='19' rx='2.5'/%3E%3Cpath d='M11 18h2'/%3E%3C/svg%3E"); }
:root { --icon-mail: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2.5'/%3E%3Cpath d='m4 7.5 8 5.5 8-5.5'/%3E%3C/svg%3E"); }
:root { --icon-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4.5' y='10.5' width='15' height='10.5' rx='2.5'/%3E%3Cpath d='M8 10.5v-3a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E"); }
:root { --icon-key: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='7.5' cy='15.5' r='4'/%3E%3Cpath d='m10.4 12.6 9.1-9.1M16.5 6.5 19 9M14 9l2.5 2.5'/%3E%3C/svg%3E"); }
#settingsPage { --settings-row-hover: rgba(255, 255, 255, .035); }
:root:not([data-theme="dark"]) #settingsPage { --settings-row-hover: rgba(0, 0, 0, .028); }
/* 两组并排时下沿齐：组是竖排的 flex，卡片撑满；个人资料那张多出来的高度给头像那一块。 */
.settings-groups {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px 24px;
  align-items: stretch;
  max-width: var(--page-column-width);
  margin: 0 auto;
}
.settings-group { display: flex; flex-direction: column; min-width: 0; }
.settings-group-wide { grid-column: 1 / -1; }
.settings-group-title { margin: 0 0 10px 4px; color: var(--muted); font-size: 13px; font-weight: 500; }
.settings-group-note { max-width: 980px; margin: -2px 0 12px 4px; color: var(--muted); font-size: 13px; line-height: 1.7; }
/* 卡片：深色是无描边的色块，浅色照「浅色标准模板」白底 + 描边。 */
.settings-list {
  display: flex;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: 14px;
  background: var(--surface);
}
:root:not([data-theme="dark"]) .settings-list { border-color: var(--line); }
.settings-list-note { margin: 0; padding: 20px; color: var(--muted); font-size: 13px; }
.settings-identity { display: flex; flex: 1; align-items: center; gap: 16px; min-height: 96px; padding: 20px; border-bottom: 1px solid var(--line); }
.settings-identity-text { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.settings-identity-text strong { overflow: hidden; color: var(--text); font-size: 18px; font-weight: 600; line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; }
.settings-uid { display: flex; align-items: center; gap: 4px; color: var(--muted); font-size: 13px; }
#settingsUid { color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: .02em; }
#settingsPage .settings-copy {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
}
#settingsPage .settings-copy:hover:not(:disabled) { background: var(--btn-ghost-hover); color: var(--text); }
/* 头像：和右上角账户按钮同一身（人形 + 会员的品牌渐变环和钻石角标），只是大一号。 */
.settings-avatar { position: relative; flex: none; display: grid; place-items: center; width: 56px; height: 56px; border-radius: 50%; background: rgba(255, 255, 255, .08); color: var(--muted); }
:root:not([data-theme="dark"]) .settings-avatar { background: var(--surface-soft); }
.settings-avatar-face { position: relative; z-index: 1; width: 26px; height: 26px; background: currentColor; -webkit-mask: var(--icon-account) no-repeat center / contain; mask: var(--icon-account) no-repeat center / contain; }
.settings-avatar.member,
:root:not([data-theme="dark"]) .settings-avatar.member { background: linear-gradient(135deg, #19c3ff 0%, #2f7cff 55%, #8b5cf6 100%); }
.settings-avatar.member::before { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--surface); }
.settings-avatar-badge { position: absolute; right: -1px; bottom: -1px; z-index: 2; display: none; place-items: center; width: 19px; height: 19px; border-radius: 50%; background: var(--surface); }
.settings-avatar.member .settings-avatar-badge { display: grid; }
.settings-avatar-diamond { width: 14px; height: 14px; background: url("/static/icon-tier-member.png") no-repeat center / contain; }
/* 一行：能点的是 <button>，只看的是 <div>，长得一样。全站 button 的边框、圆角、底色、min-height 在这里全部收回。 */
#settingsPage .settings-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 64px;
  margin: 0;
  padding: 0 20px;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 400;
  text-align: left;
  white-space: normal;
}
#settingsPage .settings-list > .settings-row:first-child,
#settingsPage .settings-identity + .settings-row { border-top: 0; }
#settingsPage button.settings-row:hover:not(:disabled) { border-color: var(--line); background: var(--settings-row-hover); }
#settingsPage .settings-list > button.settings-row:first-child:hover:not(:disabled),
#settingsPage .settings-identity + button.settings-row:hover:not(:disabled) { border-color: transparent; }
#settingsPage button.settings-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.settings-row-icon { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px; background: var(--surface-soft); }
.settings-row-icon::before {
  content: "";
  width: 17px;
  height: 17px;
  background: var(--text);
  opacity: .85;
  -webkit-mask: var(--settings-icon) no-repeat center / contain;
  mask: var(--settings-icon) no-repeat center / contain;
}
.settings-icon-user { --settings-icon: var(--icon-account); }
.settings-icon-calendar { --settings-icon: var(--icon-calendar); }
.settings-icon-phone { --settings-icon: var(--icon-phone); }
.settings-icon-mail { --settings-icon: var(--icon-mail); }
.settings-icon-lock { --settings-icon: var(--icon-lock); }
.settings-icon-key { --settings-icon: var(--icon-key); }
/* 微信用它自己的绿色圆标（付款弹窗那一张），和第三方登录的习惯一样。 */
.settings-row-icon.settings-icon-wechat { border-radius: 50%; background: url("/static/pay-wechat.png") no-repeat center / 34px 34px; }
.settings-row-icon.settings-icon-wechat::before { display: none; }
.settings-row-label { flex: none; min-width: 72px; color: var(--text); font-weight: 500; }
.settings-row-value { display: flex; flex: 1; align-items: center; justify-content: flex-end; min-width: 0; color: var(--muted); text-align: right; }
.settings-row-value > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.settings-row-chevron {
  flex: none;
  width: 14px;
  height: 14px;
  margin-right: -4px;
  background: var(--muted);
  opacity: .7;
  -webkit-mask: var(--icon-chevron-right) no-repeat center / contain;
  mask: var(--icon-chevron-right) no-repeat center / contain;
}
.settings-chip { flex: none; display: inline-flex; align-items: center; height: 20px; margin-left: 8px; padding: 0 7px; border-radius: 6px; font-size: 12px; font-weight: 500; line-height: 1; white-space: nowrap; }
.settings-chip.ok { background: var(--success-soft); color: var(--success); }
.settings-chip.warn { background: var(--orange-soft); color: var(--orange); }
.settings-key-filled { display: inline-flex; align-items: center; gap: 6px; color: var(--text); }
.settings-key-filled .check-icon { color: var(--success); }
#settingsPage .settings-row-logout { justify-content: center; color: var(--danger); font-weight: 500; }
#settingsPage .settings-row-logout:hover:not(:disabled) { background: var(--btn-danger-bg); }
/* 弹窗（挂在 body 下）：一列表单，输入框和上传作品页、登录框一样高 38、圆角 10。 */
.settings-dialog,
.settings-dialog[open] { width: min(440px, calc(100vw - 32px)); }
.settings-dialog-body { display: flex; flex-direction: column; gap: 14px; padding-bottom: 4px; }
.settings-dialog-body .captcha-hint { margin: 0; }
.settings-field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.settings-field > span:first-child,
.settings-field > label { color: var(--muted); font-size: 13px; line-height: 1.2; }
.settings-dialog input:not([type="checkbox"]) {
  width: 100%;
  height: 38px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--surface-soft);
  color: var(--text);
  font-size: 14px;
  box-shadow: none;
}
.settings-code-row { display: flex; gap: 8px; }
.settings-code-row input { flex: 1; min-width: 0; }
.settings-code-row button { flex: none; height: 38px; min-height: 0; padding: 0 16px; border-radius: 10px; }
.settings-dialog-status { margin: -4px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.settings-dialog-status:empty { display: none; }
.settings-dialog .dialog-footer { gap: 10px; }
.settings-dialog .settings-dialog-left { margin-right: auto; }
@media (max-width: 1199px) {
  .settings-groups { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 767px) {
  .settings-groups { gap: 20px; }
  #settingsPage .settings-row { min-height: 58px; padding: 0 16px; gap: 12px; }
  .settings-identity { min-height: 0; padding: 18px 16px; }
  .settings-group-note { margin-left: 2px; }
}

/* ==================== 套餐订阅：方案一「页签」（2026-10-08） ====================
   用户在演示站（Assets\套餐订阅方案-1008）里从两版里选的，参考 flova.tv 的定价页：最上面一条会员横幅（右边四格大数字），
   「充值积分 | 创作会员」两个页签，一档一张大圆角卡（名字胶囊 + 多送小签、大号价格、到账积分、一排积分闪电、白色胶囊「购买」、描金框里写折合单价、购买说明），
   最底下常见问题和购买须知。**档位那一排别改回圆点**（演示里照 Flova 画过一排圆点，用户：「一看就是抄Flova的」「把圆圈改成积分的那个闪电icon」）。
   点缀色用全站的品牌青蓝（演示里是 Flova 那样的香槟金，用户 2026-10-08：「改成品牌青蓝」）；变量名还叫 --billing-gold* 是历史原因，值就是 --accent 那一套。
   横幅深浅主题都是深底。自定义积分充值 2026-10-08 撤了（用户：「自定义积分功能代码移除，不再需要」）。
   组件在 frontend/src/features/main/billing/BillingPage.tsx；回退用的旧页面（index.html 的 #billingPage）还是原来那一套 .buy-* / .stat-*，上面那几块留给它。 */
#billingPage:has(> .billing-page) {
  --billing-card: #ffffff;
  --billing-card-line: var(--line);
  --billing-pill: var(--surface-soft);
  --billing-sub: #26302c;
  --billing-faint: var(--line);
  --billing-gold: var(--accent-text);
  --billing-gold-grad: var(--gradient-primary);
  --billing-gold-chip: var(--accent-soft);
  --billing-gold-line: color-mix(in srgb, var(--accent) 38%, transparent);
  --billing-gold-glow: color-mix(in srgb, var(--accent) 18%, transparent);
  --billing-buy-bg: var(--command-create-bg);
  --billing-buy-text: var(--command-create-text);
}
:root[data-theme="dark"] #billingPage:has(> .billing-page) {
  --billing-card: linear-gradient(180deg, rgba(255, 255, 255, .065) 0%, rgba(255, 255, 255, .025) 55%, rgba(255, 255, 255, .02) 100%), #171717;
  --billing-card-line: rgba(255, 255, 255, .085);
  --billing-pill: rgba(255, 255, 255, .075);
  --billing-sub: rgba(255, 255, 255, .88);
  --billing-faint: rgba(255, 255, 255, .09);
  --billing-gold: var(--accent-text);
  --billing-gold-grad: var(--gradient-primary);
  --billing-gold-chip: var(--accent-soft);
  --billing-gold-line: color-mix(in srgb, var(--accent) 34%, transparent);
  --billing-gold-glow: color-mix(in srgb, var(--accent) 22%, transparent);
}
/* 背景和首页分开（用户 2026-10-20：「套餐订阅界面的背景色可以改一下，不要和主页的颜色一样」）：深色比首页的 #141414 再沉一档，接近 LibTV 套餐页的近黑；
   浅色比首页的 #f4f5f5 深一档，白卡片浮得出来。 */
#billingPage:has(> .billing-page):not(.hidden) { padding: 66px 46px 72px; background: #e9ecef; }
:root[data-theme="dark"] #billingPage:has(> .billing-page):not(.hidden) {
  background: radial-gradient(70% 45% at 18% 0%, rgba(255, 255, 255, .045), transparent 70%), #0a0a0a;
}
/* 开合时铺在套餐页底下的遮罩（billing/zoom.ts 现挂现撤），颜色照 antd 的 Modal 遮罩。 */
.billing-zoom-mask { position: fixed; inset: 0; z-index: 89; background: rgba(0, 0, 0, .45); pointer-events: none; }
/* 收起时缩回去的是套餐层的快照（billing/zoom.ts）：克隆出来的元素动画会从头放，一律停在终点，数字滚动那一条藏掉。 */
.billing-ghost * { animation: none !important; }
.billing-ghost .billing-roll-strip { display: none !important; }
.billing-page { max-width: var(--page-column-width); margin: 0 auto; color: var(--text); }
.billing-page h2, .billing-page h4, .billing-page p { margin: 0; }
.billing-gold { background: var(--billing-gold-grad); -webkit-background-clip: text; background-clip: text; color: transparent; }
.billing-bolt {
  flex: none; display: inline-block; width: 16px; height: 16px;
  background: var(--credits-icon-color); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.billing-diamond { flex: none; display: inline-block; width: 18px; height: 18px; background: url("/static/icon-tier-member.png") no-repeat center / contain; }
.billing-icon {
  flex: none; display: inline-block; width: 14px; height: 14px; background: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-position: center; mask-position: center; -webkit-mask-size: contain; mask-size: contain;
}
.billing-icon-check { width: 12px; height: 12px; -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 12.5 4.8 4.8L19.5 7'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 12.5 4.8 4.8L19.5 7'/%3E%3C/svg%3E"); }
.billing-icon-dot { width: 5px; height: 5px; margin: 0 4px 0 3px; border-radius: 50%; -webkit-mask: none; mask: none; background: var(--muted); }
.billing-icon-info { width: 13px; height: 13px; -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-6h2zm0-8h-2V7h2z'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-6h2zm0-8h-2V7h2z'/%3E%3C/svg%3E"); }
.billing-icon-ticket { width: 16px; height: 16px; -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v1.5a2.5 2.5 0 0 0 0 5V17a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1.5a2.5 2.5 0 0 0 0-5z'/%3E%3Cpath d='M14 5v2M14 11v2M14 17v2'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v1.5a2.5 2.5 0 0 0 0 5V17a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1.5a2.5 2.5 0 0 0 0-5z'/%3E%3Cpath d='M14 5v2M14 11v2M14 17v2'/%3E%3C/svg%3E"); }
.billing-icon-orders { width: 15px; height: 15px; -webkit-mask-image: url("/static/icon-orders.png"); mask-image: url("/static/icon-orders.png"); }
.billing-icon-chevron-down { width: 14px; height: 14px; -webkit-mask-image: var(--icon-chevron-right); mask-image: var(--icon-chevron-right); rotate: 90deg; transition: rotate .26s cubic-bezier(.4, 0, .2, 1); }

/* 横幅：深浅主题都是深底（和账户面板的会员卡一个道理），字一律白、渐变字用深色主题那一档青蓝 */
.billing-banner {
  --billing-gold-grad: linear-gradient(90deg, #00c2ff 0%, #1aa0ff 55%, #2f7cff 100%);
  position: relative; isolation: isolate; overflow: hidden; display: flex; align-items: center; justify-content: space-between; gap: 24px 40px;
  min-height: 148px; padding: 24px 36px 24px 56px; border-radius: 24px; color: #ffffff;
  background:
    radial-gradient(34% 130% at 50% 20%, rgba(0, 150, 255, .5), transparent 72%),
    radial-gradient(30% 150% at 68% 85%, rgba(123, 108, 255, .5), transparent 72%),
    radial-gradient(26% 130% at 100% 100%, rgba(0, 196, 160, .3), transparent 72%),
    radial-gradient(30% 120% at 0% 0%, rgba(120, 90, 200, .28), transparent 72%),
    linear-gradient(100deg, #15161d 0%, #151b2a 45%, #111a2a 72%, #0e1618 100%);
}
.billing-banner::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(118deg, transparent 32%, rgba(255, 255, 255, .07) 41%, transparent 50%),
    linear-gradient(118deg, transparent 52%, rgba(255, 255, 255, .05) 59%, transparent 66%);
}
.billing-banner-text { min-width: 0; }
.billing-banner-title { color: #ffffff; font-size: 30px; font-weight: 800; line-height: 1.3; letter-spacing: .3px; }
.billing-banner-title .billing-big { font-size: 40px; line-height: 1; vertical-align: -2px; }
.billing-banner-sub { display: flex; align-items: center; gap: 14px; margin-top: 4px !important; font-size: 15px; font-style: italic; font-weight: 700; }
.billing-banner-sub::before { content: ""; flex: none; width: 240px; height: 1px; background: linear-gradient(90deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .45)); }
.billing-banner-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 14px; color: rgba(255, 255, 255, .88); font-size: 14px; }
/* 悬浮规则只管颜色、不带尺寸和外边距：合在一串的话悬浮那条优先级高，会把别处按场合改的宽度、外边距盖回去（当时自定义积分那一条的「购买」悬浮就换行跑开、点不中） */
.billing-banner-cta {
  display: inline-flex; align-items: center; height: 34px; min-height: 0; padding: 0 18px; border: 0; border-radius: 999px;
  background: #ffffff; color: #111111; font-size: 14px; font-weight: 700; cursor: pointer;
}
.billing-banner-cta:hover:not(:disabled) { border: 0; background: #ffffff; color: #111111; }
.billing-banner-cta:disabled { opacity: .45; cursor: not-allowed; }
.billing-tiles { display: flex; flex: none; gap: 14px; }
.billing-tile {
  display: grid; align-content: center; justify-items: center; gap: 6px; min-width: 86px; height: 88px; padding: 0 12px;
  border: 1px solid rgba(255, 255, 255, .12); border-radius: 16px;
  background: linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, .05)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
}
.billing-tile strong { color: #ffffff; font-size: 30px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.billing-tile span { color: rgba(255, 255, 255, .85); font-size: 12px; font-weight: 700; }

/* 页签（整宽两半、选中那半底下一道线）、说明、右边那一排胶囊 */
.billing-tabs { position: relative; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 34px; border-bottom: 1px solid var(--billing-faint); }
/* 那道线只有一道、画在这一条上，换页签时滑到另一半（用户 2026-10-08：「切换充值积分和创作会员，那个下标也要有滑动动画」）。
   原来是每颗页签各画一道（.billing-tab.active::after），换页签是这道没了、那道冒出来，滑不起来。两颗各占一半，线宽一半、按 data-tab 挪一格，
   不用量按钮；缓动和画风面板页签的下划线一样。只有积分一颗页签时线就停在左半边（原来也是） */
.billing-tabs::after { content: ""; position: absolute; left: 0; bottom: -1px; width: 50%; height: 2px; background: var(--text); opacity: .75; pointer-events: none; transition: transform .26s cubic-bezier(.2, .8, .2, 1); }
.billing-tabs[data-tab="member"]::after { transform: translateX(100%); }
@media (prefers-reduced-motion: reduce) { .billing-tabs::after { transition: none; } }
.billing-tab {
  position: relative; display: flex; align-items: baseline; justify-content: center; gap: 6px; height: 58px; min-height: 0; padding: 18px 8px 0;
  border: 0; border-radius: 0; background: none; color: var(--muted); font-size: 18px; font-weight: 700; cursor: pointer;
}
.billing-tab:hover:not(:disabled) { border: 0; background: none; color: var(--text); }
.billing-tab.active { color: var(--text); }
.billing-tab small { color: var(--muted); font-size: 12px; font-weight: 600; }
.billing-closed { display: flex; align-items: center; gap: 6px; margin-top: 18px !important; color: var(--muted); font-size: 14px; }
.billing-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 16px; margin: 22px 0 26px; }
.billing-note { color: var(--muted); font-size: 14px; }
.billing-pills { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.billing-pill,
.billing-balance {
  display: inline-flex; align-items: center; gap: 7px; height: 40px; min-height: 0; padding: 0 18px; border: 0; border-radius: 999px;
  background: var(--billing-pill); color: var(--text); font-size: 14px; font-weight: 700; cursor: pointer; font-variant-numeric: tabular-nums;
}
.billing-pill:hover:not(:disabled) { border: 0; background: var(--billing-pill); color: var(--text); }
.billing-pill .billing-icon { color: var(--billing-gold); }
.billing-balance { cursor: default; }

/* 卡片 */
.billing-grid { display: grid; align-items: stretch; gap: 22px; }
/* 积分卡一行 4 张（用户 2026-10-08：「宽度小一点，确保每行可以显示4个」，原来 3 张、大屏一张 465 宽）；平板 2 张、手机 1 张。
   会员页签那两张照旧 400 宽居中：收成积分卡那么窄的话，1280 宽时「创作会员」胶囊加「最高可得 2,660 积分」那一行放不下 */
.billing-grid-packs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.billing-grid-pair { grid-template-columns: repeat(2, minmax(0, 400px)); justify-content: center; }
.billing-empty { color: var(--muted); font-size: 14px; }
.billing-card {
  position: relative; display: flex; flex-direction: column; min-width: 0; padding: 22px 24px 26px;
  border: 1px solid var(--billing-card-line); border-radius: 24px; background: var(--billing-card); color: var(--text);
}
:root:not([data-theme="dark"]) .billing-card { box-shadow: 0 1px 2px rgba(16, 24, 20, .04), 0 10px 30px -18px rgba(16, 24, 20, .18); }
.billing-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 34px; }
.billing-plan {
  display: inline-flex; align-items: center; gap: 8px; min-width: 0; height: 34px; padding: 0 14px 0 11px; border-radius: 999px;
  background: var(--billing-pill); font-size: 16px; font-weight: 700; white-space: nowrap;
}
.billing-plan b { overflow: hidden; font-weight: 700; text-overflow: ellipsis; }
.billing-chip {
  display: inline-flex; flex: none; align-items: center; height: 24px; padding: 0 10px; border-radius: 999px;
  background: var(--billing-gold-chip); font-size: 12px; font-weight: 700; white-space: nowrap;
}
.billing-chip-plain { background: var(--billing-pill); color: var(--muted); }
.billing-chip-live { background: var(--success-soft); color: var(--success); }
.billing-price { display: flex; align-items: baseline; margin-top: 22px; line-height: 1; font-variant-numeric: tabular-nums; }
.billing-price small { margin-right: 2px; font-size: 24px; font-weight: 700; }
.billing-price strong { font-size: 48px; font-weight: 800; letter-spacing: -1px; }
.billing-price i { margin-left: 6px; color: var(--muted); font-size: 14px; font-style: normal; font-weight: 600; }
.billing-credits { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 6px; margin-top: 12px; font-size: 24px; font-weight: 500; line-height: 1.25; font-variant-numeric: tabular-nums; }
.billing-credits em { color: var(--billing-gold); font-style: normal; }
.billing-credits small { color: var(--muted); font-size: 13px; font-weight: 600; }
.billing-credits .billing-credits-plain { font-size: 15px; line-height: 30px; }
/* 档位那一排十个积分闪电（档位越大亮得越多）：用的是全站那枚积分闪电（--icon-credits，紫色照旧）。别改回圆点（用户：「一看就是抄Flova的」）。 */
.billing-level { display: flex; justify-content: space-between; margin-top: 20px; }
.billing-level-bolt {
  width: 15px; height: 15px; opacity: .16;
  background: var(--credits-icon-color); -webkit-mask: var(--icon-credits) no-repeat center / contain; mask: var(--icon-credits) no-repeat center / contain;
}
.billing-level-bolt.on { opacity: 1; }
.billing-member-line { min-height: 20px; margin-top: 16px !important; color: var(--muted); font-size: 13px; line-height: 1.5; }
.billing-buy {
  display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 42px; min-height: 0; margin-top: 24px; padding: 0 20px;
  border: 0; border-radius: 999px; background: var(--billing-buy-bg); color: var(--billing-buy-text); font-size: 15px; font-weight: 700; cursor: pointer;
}
.billing-buy:hover:not(:disabled) { border: 0; background: var(--billing-buy-bg); color: var(--billing-buy-text); }
.billing-member-line + .billing-buy { margin-top: 16px; }
.billing-buy:disabled { opacity: .4; cursor: not-allowed; }
.billing-buy-ghost, .billing-buy-ghost:disabled { background: var(--billing-pill); color: var(--muted); opacity: 1; }
/* 折合单价 / 会员权益那个框：标题压在上边框中间（fieldset + legend 原生就把边框断开） */
.billing-box { min-width: 0; margin: 26px 0 0; padding: 14px 16px 16px; border: 1px solid var(--billing-gold-line); border-radius: 16px; }
.billing-box legend { display: flex; align-items: center; gap: 6px; margin: 0 auto; padding: 0 10px; font-size: 13px; font-weight: 700; }
.billing-box-plain { border-color: var(--billing-faint); }
.billing-box-plain legend { color: var(--muted); }
.billing-info { display: inline-flex; color: var(--muted); cursor: help; }
.billing-item + .billing-item { margin-top: 12px; }
.billing-item p { display: flex; align-items: center; gap: 8px; color: var(--billing-sub); font-size: 13.5px; font-weight: 700; line-height: 1.4; }
.billing-item ul { display: grid; gap: 3px; margin: 5px 0 0 20px; padding: 0; list-style: none; }
.billing-item li { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.billing-item li b { color: var(--billing-sub); font-weight: 600; }
.billing-box-plain .billing-item p { font-weight: 600; }
.billing-notes { margin-top: 20px; }
.billing-notes-title { margin-bottom: 8px !important; color: var(--muted); font-size: 13px; font-weight: 700; }
.billing-notes ul { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }
.billing-notes li { display: flex; align-items: center; gap: 8px; color: var(--billing-sub); font-size: 13px; font-weight: 600; }
.billing-notes li.is-gold .billing-icon-check { color: var(--billing-gold); }
/* 创作会员那张：描金边，外面一圈淡光 */
.billing-member { border-color: var(--billing-gold-line); box-shadow: 0 0 0 1px var(--billing-gold-glow), 0 24px 60px -30px var(--billing-gold-glow); }
:root:not([data-theme="dark"]) .billing-member { box-shadow: 0 0 0 1px var(--billing-gold-glow), 0 24px 60px -30px var(--billing-gold-glow); }
.billing-member .billing-plan { background: var(--billing-gold-chip); }
/* 常见问题、购买须知 */
.billing-faq { max-width: 900px; margin: 84px auto 0; }
.billing-faq h2 { margin-bottom: 30px !important; text-align: center; font-size: 34px; font-weight: 800; letter-spacing: .5px; }
.billing-faq-item { border-top: 1px solid var(--billing-faint); }
.billing-faq-item:last-child { border-bottom: 1px solid var(--billing-faint); }
.billing-faq-item summary { display: flex; align-items: center; gap: 14px; padding: 18px 10px; list-style: none; cursor: pointer; font-size: 16px; font-weight: 700; }
.billing-faq-item summary::-webkit-details-marker { display: none; }
.billing-faq-no { display: grid; flex: none; place-items: center; width: 20px; height: 20px; border: 1.5px solid var(--muted); border-radius: 50%; color: var(--billing-sub); font-size: 11.5px; font-weight: 700; }
.billing-faq-item summary .billing-icon-chevron-down { margin-left: auto; color: var(--muted); }
.billing-faq-item[open] summary .billing-icon-chevron-down { rotate: -90deg; }
.billing-faq-item.is-closing summary .billing-icon-chevron-down { rotate: 90deg; }
.billing-faq-panel { overflow: hidden; }
.billing-faq-body { padding: 0 10px 20px 44px; color: var(--muted); font-size: 14px; line-height: 1.85; }
.billing-faq-body b { color: var(--billing-sub); }
.billing-terms { margin-top: 44px; }
.billing-terms h4 { margin-bottom: 8px; color: var(--billing-sub); font-size: 15px; font-weight: 700; }
.billing-terms p { color: var(--muted); font-size: 13px; line-height: 1.8; }
/* 兑换码弹窗（挂在 body 下，通用弹窗那一身）：一个输入框 */
.billing-redeem-dialog,
.billing-redeem-dialog[open] { width: min(420px, calc(100vw - 32px)); }
.billing-redeem-field { display: flex; flex-direction: column; gap: 7px; padding: 0 24px 6px; }
.billing-redeem-field > span { color: var(--muted); font-size: 13px; }
.billing-redeem-field input { height: 40px; border-radius: 10px; background: var(--surface-soft); }
/* 笔记本这一档照样 4 张：1200 宽时一张只有 260 出头，名字胶囊加「首购多送」那一行要 213、「150,000 + 60,000 积分」要 220，
   卡缝和卡片左右内边距各收一点（卡内从 210 放到 223）。大屏不动 */
@media (min-width: 1200px) and (max-width: 1599px) {
  .billing-grid-packs { gap: 16px; }
  .billing-grid-packs .billing-card { padding-left: 20px; padding-right: 20px; }
}
@media (max-width: 1199px) {
  .billing-banner { flex-direction: column; align-items: flex-start; padding: 26px 30px; }
  .billing-grid-packs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .billing-banner-sub::before { width: 120px; }
}
@media (max-width: 767px) {
  #billingPage:has(> .billing-page):not(.hidden) { padding: 58px 14px calc(40px + env(safe-area-inset-bottom)); }
  .billing-banner { align-items: center; gap: 16px; min-height: 0; padding: 22px 16px 18px; border-radius: 20px; text-align: center; }
  .billing-banner-title { font-size: 21px; }
  .billing-banner-title .billing-big { font-size: 28px; }
  .billing-banner-sub { justify-content: center; font-size: 13px; }
  .billing-banner-sub::before { display: none; }
  .billing-banner-line { justify-content: center; font-size: 12.5px; }
  .billing-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); width: 100%; gap: 8px; }
  .billing-tile { min-width: 0; height: 64px; padding: 0 4px; border-radius: 12px; }
  .billing-tile strong { font-size: 20px; }
  .billing-tile span { font-size: 11px; }
  .billing-tabs { margin-top: 22px; }
  .billing-tab { flex-direction: column; align-items: center; gap: 2px; height: 64px; padding-top: 12px; font-size: 16px; }
  .billing-tab small { font-size: 11px; }
  .billing-controls { flex-direction: column; align-items: stretch; margin: 16px 0 18px; }
  .billing-note { text-align: center; font-size: 13px; }
  .billing-pills { justify-content: center; }
  .billing-pill,
  .billing-balance { height: 36px; padding: 0 14px; font-size: 13px; }
  .billing-grid { gap: 14px; }
  .billing-grid-packs, .billing-grid-pair { grid-template-columns: minmax(0, 1fr); }
  .billing-card { padding: 20px 20px 22px; border-radius: 20px; }
  .billing-price strong { font-size: 44px; }
  .billing-faq { margin-top: 56px; }
  .billing-faq h2 { margin-bottom: 18px !important; font-size: 22px; }
  .billing-faq-item summary { gap: 10px; padding: 16px 4px; font-size: 14.5px; }
  .billing-faq-body { padding: 0 4px 16px 34px; font-size: 13.5px; }
}
/* 打开套餐页时的数字滚动、闪电点亮（2026-10-19，用户：「第一位数字要有上下滚动到当前数字的感觉」「闪电的积分 icon：也要有从左往右填充的动画」）。
   BillingPage.tsx 的 RollNumber / LevelBolts，只在打开那一下放（切页签不放）；开合动画本身在 billing/zoom.ts。
   从张开放完那一刻开始（--roll-start 由页面按 zoom.ts 现算写上；用户：「弹窗弹完之后立马开始滚动」），别再加固定的延迟。
   闪电等这张卡的数字停下来就从左往右亮（用户 2026-10-20：「可以等到滚动数字动画放完之后再开始」，接着「停止滚动后，下方的闪电要立马开始亮」）：
   滚动 .8s 但缓动尾巴很长：400ms 时价格那一位离终点还剩约 4px、一帧挪不到 1px，看着已经停了，闪电就在这一刻亮。
   先按「离终点不到 1px」定在 550ms，用户还是嫌等（「数字的滚动停止之后，要更快地去播放下方的闪电」），往前挪到 400ms；别改回 550 / 800，换缓动或时长要重算。
   点亮本身一颗 .5s（用户 2026-10-20：「闪电点亮速度改为0.5」，原来 .42s）、相邻隔 60ms。
   真正那一位数字留在排版里定宽、定基线（滚的时候透明），滚动的那一条盖在上面、用 clip-path 裁成一格高——
   **别改成 overflow: hidden 的 inline-block**：基线会落到底边，和「¥」、后面几位对不齐。 */
.billing-roll { position: relative; display: inline-block; clip-path: inset(0); }
.billing-roll-digit { animation: billing-roll-reveal .8s steps(1, end) calc(var(--roll-start, 0ms) + var(--roll-delay, 0ms)) both; }
.billing-roll-strip {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  animation:
    billing-roll-spin .8s cubic-bezier(.16, 1, .3, 1) calc(var(--roll-start, 0ms) + var(--roll-delay, 0ms)) both,
    billing-roll-hide .8s steps(1, end) calc(var(--roll-start, 0ms) + var(--roll-delay, 0ms)) both;
}
.billing-roll-strip > span { flex: none; display: block; height: 100%; }
.billing-roll-strip > span::before { content: attr(data-digit); }
@keyframes billing-roll-spin { from { transform: translateY(0); } to { transform: translateY(calc(var(--roll-to) * -100%)); } }
@keyframes billing-roll-reveal { from { opacity: 0; } to { opacity: 1; } }
@keyframes billing-roll-hide { from { opacity: 1; } to { opacity: 0; } }
.billing-level.is-filling .billing-level-bolt.on {
  animation: billing-bolt-fill .5s ease-out calc(var(--roll-start, 0ms) + var(--roll-delay, 0ms) + 400ms + var(--i) * 60ms) both;
}
@keyframes billing-bolt-fill {
  from { opacity: .16; transform: scale(1); }
  45% { opacity: 1; transform: scale(1.3); }
  to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .billing-roll-digit, .billing-level.is-filling .billing-level-bolt.on { animation: none; }
  .billing-roll-strip { display: none; }
}

/* ==================== 项目卡与「我的项目」页（2026-10-08 改版） ====================
   项目卡首页「最近项目」和「我的项目」共用（works/ProjectCard.tsx），用户给的参考是 Flova 的项目卡：16:9 封面 + 左上角状态签 + 右上角「•••」
   + 项目名 + 「最后编辑于 …」。「我的项目」页（用户：「我的项目页，也改成最新项目中的样式，并且内容区域，和首页一样保持居中」）：
   居中一栏（和首页同宽）、一行四张、不分页往下滑接着画。只挂在新外壳的 #worksPage.works-page 和新类名上：回退用的旧页面
   （breakdown.js 画的 .work-card、.works-panel 那一版）一个像素不动，上面那几条旧规则留给它。 */

.project-card { display: flex; flex-direction: column; gap: 10px; min-width: 0; color: var(--text); cursor: pointer; outline: none; }
/* 封面底下不铺颜色：圆角抗锯齿那一圈会把底色透出来，看着像一道线框（用户 2026-10-08：「为什么精选作品的图片边缘感觉有线框？」，同一个问题） */
.project-card-cover { position: relative; aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden; background: transparent; }
.project-card-cover img { display: block; width: 100%; height: 100%; object-fit: cover; transition: scale .3s ease, filter .2s ease; }
.project-card:hover .project-card-cover img { scale: 1.04; filter: brightness(1.05); }
.project-card:focus-visible .project-card-cover { outline: 3px solid var(--btn-ring); outline-offset: 2px; }
/* 项目自己没出过画面（或者图过了保存期取不到）：一块底色，中间半透明的 MinimoTV（用户：「如果作品没有封面，那么默认给一个背景底色，
   中间用半透明的字体：MinimoTV」）。底色和字色跟着主题走，按卡片宽度定字号；留一道细边，不然浅色下它和页面底色分不开 */
.project-card-cover:has(> .project-card-blank), .cover-option.is-blank { display: grid; place-items: center; container-type: inline-size; background: var(--surface-soft); box-shadow: inset 0 0 0 1px var(--line); }
.project-card-blank { color: color-mix(in srgb, var(--text) 24%, transparent); font-size: clamp(14px, 9cqw, 30px); font-weight: 800; letter-spacing: .04em; line-height: 1; user-select: none; }
/* 状态签：有任务在跑是绿点呼吸，失败是红点；点它进对应的模式（原来卡片上那颗状态签的去处） */
.project-card-tag { position: absolute; top: 8px; left: 8px; display: inline-flex; align-items: center; gap: 6px; height: 24px; min-height: 0; padding: 0 10px; border: 0; border-radius: 999px; background: rgba(0, 0, 0, .52); color: #fff; font-size: 12px; cursor: pointer; backdrop-filter: blur(6px); }
.project-card-tag:hover { background: rgba(0, 0, 0, .66); }
.project-card-tag i { width: 6px; height: 6px; border-radius: 50%; background: var(--success); }
.project-card-tag.is-live i { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 60%, transparent); animation: project-card-live 1.6s ease-out infinite; }
.project-card-tag.is-failed i { background: var(--danger); }
@keyframes project-card-live { 70% { box-shadow: 0 0 0 6px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
/* 「•••」悬浮才露出来（触屏一直在，没有悬浮可言） */
.project-card-more { position: absolute; top: 6px; right: 6px; display: grid; place-items: center; width: 30px; height: 30px; min-height: 0; padding: 0; border: 0; border-radius: 50%; background: rgba(0, 0, 0, .52); color: #fff; opacity: 0; backdrop-filter: blur(6px); transition: opacity .15s ease; }
.project-card-more .line-icon { width: 18px; height: 18px; }
.project-card:hover .project-card-more, .project-card:focus-within .project-card-more, .project-card-more.is-open { opacity: 1; }
.project-card-more:hover { background: rgba(0, 0, 0, .7); }
@media (hover: none) { .project-card-more { opacity: 1; } }
/* 「开始创作」那格框底下的标题和项目名同一身（只共用样式，不共用类名：列项目名的地方按 .project-card-title 找）。
   display: block：项目名包在 .project-card-name 里、不再直接是卡片那一列的格子，行内元素截不出省略号 */
.project-card-title, .project-card-new-title { display: block; overflow: hidden; color: var(--text); font-size: 14px; font-weight: 650; line-height: 1.4; text-overflow: ellipsis; white-space: nowrap; }
.project-card-time { margin-top: -5px; overflow: hidden; color: var(--muted); font-size: 12.5px; text-overflow: ellipsis; white-space: nowrap; }
/* 卡片上就地改名：项目名照旧占着这一行（藏起来），输入框盖在上面、上下居中。框比一行字高，原来直接换掉项目名，
   卡片长高六七像素、下面那一排跟着往下挪（用户 2026-10-08：「项目名的输入框会导致下方的项目卡片往下移」）。
   框从 26 收到 24：盖在上面之后多出来的高度占的是上下的空隙，首页最近项目名字和「最后编辑于」只隔 4 像素；
   别改回换掉项目名再用负外边距抵：一行高 = 字号 × 1.4 不是整数，抵完还差 1/32 像素（量过），不如让项目名自己占着 */
.project-card-name { position: relative; }
.project-card-name.is-renaming .project-card-title { visibility: hidden; }
input.project-card-rename { position: absolute; top: 50%; left: 0; width: 100%; height: 24px; min-height: 0; margin: -12px 0 0; padding: 0 8px; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--input-bg); color: var(--text); font-size: 13.5px; font-weight: 600; }
/* 聚焦不高亮（用户 2026-10-08：「输入框选中时不需要高亮」）：全站输入框聚焦是强调色描边 + 外框 + 光圈，这里都不要，和没聚焦时一个样 */
input.project-card-rename:focus { border-color: var(--line-strong); outline: none; box-shadow: none; }
/* 第一格「开始创作」：和项目卡一个排法（.project-card 的一列、间距 10）——虚线框和封面一样 16:9，框里加号和「开始创作」，
   框底下「创建一个新的项目」用项目名那一身（.project-card-new-title 和 .project-card-title 共用一条），和同一排的项目名对齐（用户 2026-10-08：「创建一个新的项目 放到卡片下方作为标题」）。
   整格是一颗按钮：悬浮、聚焦的样子画在虚线框上，和项目卡画在封面上一样。 */
.project-card-new { display: flex; flex-direction: column; align-items: stretch; gap: 10px; min-width: 0; min-height: 0; padding: 0; border: 0; background: transparent; color: var(--text); text-align: left; }
.project-card-new-box { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; aspect-ratio: 16 / 9; border: 1.5px dashed var(--line-strong); border-radius: 12px; color: var(--muted); font-size: 13.5px; transition: border-color .15s ease, color .15s ease; }
.project-card-new:hover:not(:disabled) { background: transparent; }
.project-card-new:hover:not(:disabled) .project-card-new-box { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-strong)); color: var(--text); }
.project-card-new:focus-visible { outline: none; }
.project-card-new:focus-visible .project-card-new-box { outline: 3px solid var(--btn-ring); outline-offset: 2px; }
.project-card-new:disabled { cursor: not-allowed; opacity: .5; }
.project-card-new-plus { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: var(--surface-soft); color: var(--text); }
.project-card-new-plus .line-icon { width: 18px; height: 18px; }
.project-card-new-label { color: var(--text); font-size: 14px; font-weight: 600; }

/* 「•••」点开的小菜单：挂在 body 上用 fixed 定位（首页手机上最近项目那一排横着滑，画在卡片里会被裁掉） */
.project-menu { position: fixed; z-index: 90; width: 132px; padding: 14px; border-radius: 18px; background: var(--surface); color: var(--text); box-shadow: 0 0 0 1px var(--dialog-ring), 0 22px 54px rgba(0, 0, 0, .34); animation: home-create-pop-in .14s ease-out; transform-origin: top left; }
:root:not([data-theme="dark"]) .project-menu { box-shadow: 0 0 0 1px var(--dialog-ring), 0 18px 44px rgba(20, 30, 26, .16); }
.project-menu-item { display: flex; align-items: center; width: 100%; height: 34px; min-height: 0; padding: 0 10px; border: 0; border-radius: 9px; background: transparent; color: var(--text); font-size: 13px; text-align: left; }
.project-menu-item:hover:not(:disabled) { background: var(--surface-soft); }
.project-menu-item.is-danger { color: var(--danger); }
.project-menu-item:disabled { color: var(--muted); opacity: .55; cursor: default; }

/* ---- 「我的项目」页 ---- */
/* 页面本身滚（原来是卡片在面板里滚、分页条钉在面板底边），滚动条的位置一直留着 */
#worksPage.works-page:not(.hidden) { overflow-y: auto; scrollbar-gutter: stable; }
.works-board { position: relative; display: flex; flex: 1 0 auto; flex-direction: column; width: 100%; max-width: var(--page-column-width, 1440px); margin: 0 auto; }
/* 往下挪一截（用户：「我的项目页，可以下移一点，顶的太高了」）：标题行原来为了和右上角那一排齐平贴在最顶上（距顶 14），
   现在落到那一排下面，静止时和「上传作品」的标题同一个高度（同一天用户：「把“我的项目”标题高度和 上传作品 的标题高度对齐」，
   各档的上外边距在「页头和顶栏齐平」那一块和下面两条媒体查询里）；标题下面留 26（≥ 1200 页头和顶栏齐平那一块定的），更窄留 28 */
.works-board .works-board-head { margin-top: 32px; padding-bottom: 28px; }
/* 往下滚时这一行吸在顶上（用户 2026-10-08：「我的项目中，顶部搜索的那一栏，也固定」），和首页精选作品那一行一个做法：铺页面底色盖住滚上来的卡片，
   吸住时底下一层淡影子（is-stuck 由 WorksPage.tsx 看它被推离原位没有来挂）。≥ 1200 停在右上角按钮那一条里（「页头和顶栏齐平」那一块），
   更窄停在顶栏下面；手机上这一页有 12 的上内边距，吸在 -12 才不从那道缝里露出卡片 */
.works-board .works-board-head { position: sticky; top: 0; z-index: 6; background: var(--bg); transition: box-shadow .2s ease; }
.works-board .works-board-head.is-stuck { box-shadow: 0 14px 16px -14px rgba(0, 0, 0, .55); }
:root:not([data-theme="dark"]) .works-board .works-board-head.is-stuck { box-shadow: 0 14px 16px -14px rgba(20, 30, 26, .22); }
@media (max-width: 767px) { .works-board .works-board-head { top: -12px; } }
.works-board-count { color: var(--muted); font-size: var(--font-base); font-weight: 400; }
/* 一行四张（「在我的项目中，尺寸可以大一点，每行4个」），行距、左右间距放大（「项目之间的高度间隔可以多一点」「项目左右的间距也可以多一点」） */
.works-board-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); align-items: start; gap: 34px 24px; margin-bottom: 30px; }
.works-board-empty { margin: 36px 0; color: var(--muted); text-align: center; }
/* 不分页：往下滑接着画（那一小格是给 IntersectionObserver 盯的），全画完了底下一行字 */
.works-board-more { height: 1px; margin-top: -30px; pointer-events: none; }
.works-board-end { margin: 4px 0 28px; color: var(--muted); font-size: 13px; text-align: center; }
@media (min-width: 1200px) { body.page-flush-header .works-board .works-board-head { padding-bottom: 26px; } }
/* 平板、手机：标题和「上传作品」的标题同一个高度（同一天用户要的对齐；原来平板高 2px） */
@media (max-width: 1199px) { .works-board .works-board-head { margin-top: 10px; } }
@media (max-width: 767px) { .works-board .works-board-head { margin-top: 0; } }
@media (min-width: 768px) and (max-width: 1199px) { .works-board-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 18px; } }
@media (max-width: 767px) { .works-board-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 12px; } }

/* ---- 「修改封面」弹窗：外形吃全站通用弹窗那一身 ---- */
.cover-dialog { width: min(820px, calc(100vw - 32px)); max-width: none; padding: 0; color: var(--text); }
.cover-dialog .cover-dialog-head { align-items: flex-start; padding: 20px 18px 16px 24px; }
.cover-dialog-body { max-height: min(60vh, 520px); padding: 18px 24px; overflow-y: auto; scrollbar-gutter: stable; }
.cover-dialog-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.cover-dialog-note { grid-column: 1 / -1; margin: 6px 0 0; color: var(--muted); font-size: 13px; text-align: center; }
.cover-option { position: relative; display: block; aspect-ratio: 16 / 9; min-height: 0; padding: 0; border: 0; border-radius: 10px; background: transparent; overflow: hidden; cursor: pointer; }
.cover-option img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cover-option:hover img { filter: brightness(1.06); }
.cover-option.is-on { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
.cover-option.is-blank.is-on { box-shadow: inset 0 0 0 1px var(--line), 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
.cover-option.is-upload { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; border: 1.5px dashed var(--line-strong); background: transparent; color: var(--muted); font-size: 13px; box-shadow: none; }
.cover-option.is-upload:hover:not(:disabled) { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-strong)); color: var(--text); }
.cover-option.is-upload .line-icon { width: 20px; height: 20px; }
.cover-option-label { position: absolute; bottom: 6px; left: 6px; padding: 0 7px; border-radius: 999px; background: rgba(0, 0, 0, .55); color: #fff; font-size: 11.5px; line-height: 20px; }
@media (max-width: 767px) { .cover-dialog-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .cover-dialog-body { padding: 14px 16px; } }

/* ==== 分镜生成页 · 镜头清单 + 详情（用户 2026-10-09 在本机演示站「分镜生成方案-1009」上选的方案一 + 方案三的详情：「按照镜头清单+详情的这个方案上」）====
   1200 宽及以上：左边分镜目录栏（breakdown/Binder.tsx，和剧本页目录栏同一身：封面、逐集、新建分集、底部「生成全集分镜」和两条进度）、
   中间标题行（集号集名 → 统计 → 重新拆分；分镜设置、主按钮靠右）+「本集分镜 | 出镜资产」+ 内容区（breakdown/ShotList.tsx）、右边 Minimo 助手。
   清单一镜一张卡：勾选 · 画面缩略图（宽高比跟项目画幅）· 镜号镜头名秒数 · 景别运镜 · 概要 · 出镜资产 · 台词 · 下一步 · 首帧 | 直出 · 详情；
   点缩略图 / 镜头名 / 「详情」在这一行下面展开这一镜（左边画面卡、右边镜头信息），一次只开一行；展开 / 收起的动画在 ShotList.tsx。
   勾了镜头才在本集那一栏底部正中浮出批量那一条。原来的分集卡片、本集信息三行、分镜表、底部「生成全集分镜」照旧画着、在这一档藏着。
   只挂在新版页面的 .breakdown-layout-binder 上（回退的旧页面没有这些标记）；1199 以下一条都不生效：手机、平板照旧分栏页签 + 分镜表。
   扣积分的独立按钮（生成提示词、生成分镜、重新拆分）用主按钮那一身、悬浮不变样（用户 2026-10-09：「独立的这些主题按钮，不需要悬浮样式」）。 */
#workflowView :is(.sb-binder, .sb-stats, .sb-tabs, .sb-main, .sb-bulk) { display: none; }
#workflowView .detail-head .episode-redo-command .cost-chip { display: none; }

@media (min-width: 1200px) {
  /* ---- 骨架：三栏照剧本创作目录栏（268 / 自适应 / 540），两张卡圆角 14 ---- */
  #workflowView .breakdown-layout-binder { grid-template-columns: 268px minmax(0, 1fr) 540px; }
  #mainView #workflowView .breakdown-layout-binder > .breakdown-col-episodes { display: flex; flex-direction: column; padding: 0; border-radius: 14px; background: var(--surface);
    overflow: hidden; }
  #mainView #workflowView .breakdown-layout-binder > .breakdown-col-episodes > :not(.sb-binder) { display: none; }
  #mainView #workflowView .breakdown-layout-binder > #breakdownDetail { position: relative; gap: 0; padding: 0; border-radius: 14px; }
  /* 分镜表那一版的几块：照旧画着（手机、平板用），这一档不画 */
  #mainView #workflowView .breakdown-layout-binder > #breakdownDetail > :is(.breakdown-look-band, .detail-beats, .episode-section-row, .episode-timing-note,
    .breakdown-stale-band, .episode-shot-controls, .shot-table-wrap, .breakdown-foot) { display: none; }
  #mainView #workflowView .breakdown-layout-binder > #breakdownDetail[data-tab="shots"] > .detail-cta { display: none; }

  /* ---- 目录栏 ---- */
  #workflowView .breakdown-layout-binder .sb-binder { display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; }
  #workflowView .breakdown-layout-binder .sb-cover { flex: none; padding: 20px 20px 16px; border-bottom: 1px solid var(--line); }
  #workflowView .breakdown-layout-binder .sb-cover b { display: block; color: var(--text); font-size: 15px; font-weight: 700; line-height: 1.5; }
  #workflowView .breakdown-layout-binder .sb-cover span { display: block; margin-top: 8px; color: var(--muted); font-size: 12px; line-height: 1.6; }
  #workflowView .breakdown-layout-binder .sb-nav { display: flex; flex: 1 1 auto; flex-direction: column; gap: 6px; min-height: 0; padding: 10px 10px 12px; overflow-y: auto; }
  #workflowView .breakdown-layout-binder .sb-nav > * { flex: none; }
  /* 目录栏里所有按钮的底子：优先级用 :where 压低，下面每一种行（和行尾的「⋯」）自己的尺寸、显隐才盖得过它 */
  #workflowView .breakdown-layout-binder :where(.sb-binder) button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 0; margin: 0; padding: 0; border: 0;
    background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
  #workflowView .breakdown-layout-binder :where(.sb-binder) button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  #workflowView .breakdown-layout-binder :where(.sb-binder) button:disabled { cursor: not-allowed; opacity: .5; }
  #workflowView .breakdown-layout-binder .sb-ep { position: relative; }
  #workflowView .breakdown-layout-binder .sb-ep-open { height: 36px; padding: 0 10px 0 12px; border-radius: 8px; font-size: 13px; }
  #workflowView .breakdown-layout-binder .sb-ep-open b { flex: none; width: 18px; color: var(--muted); font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder .sb-ep-open span { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-ep.blank .sb-ep-open span { color: var(--muted); }
  /* 悬浮只给没选中的行（选中的那一行鼠标移上去不变色，和剧本页目录栏一样） */
  #workflowView .breakdown-layout-binder .sb-ep:not(.on) .sb-ep-open:hover:not(:disabled),
  #workflowView .breakdown-layout-binder .sb-add:hover:not(:disabled) { background: var(--surface-hover); }
  #workflowView .breakdown-layout-binder .sb-ep.on .sb-ep-open { background: var(--accent-soft); }
  /* 状态图标：和剧本目录栏同一套（拆完打勾、要处理的橙点、没拆空心圈），多一枚「拆了、提示词写了几成」的进度环 */
  #workflowView .breakdown-layout-binder .sb-state { flex: none; width: 16px; height: 16px; margin-left: 2px;
    background: radial-gradient(circle, transparent 3px, var(--line-strong) 3.5px, var(--line-strong) 4.5px, transparent 5px); }
  #workflowView .breakdown-layout-binder .sb-state.done { background: var(--success);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat; }
  #workflowView .breakdown-layout-binder .sb-state.part { box-sizing: border-box; width: 14px; height: 14px; margin: 1px 1px 1px 3px; padding: 2.5px; border: 1.5px solid var(--accent);
    border-radius: 50%; background: conic-gradient(var(--accent) var(--done, 0%), transparent 0) content-box; }
  #workflowView .breakdown-layout-binder .sb-state.warn { background: radial-gradient(circle, var(--warn, #d9a441) 3.5px, transparent 4px); }
  #workflowView .breakdown-layout-binder .sb-state.blank { background: radial-gradient(circle, transparent 3px, var(--line) 3.5px, var(--line) 4.5px, transparent 5px); opacity: .7; }
  /* 行尾的「⋯」（新建分集 / 删除分集，EpisodeMenu.tsx）：悬浮、键盘移到这一行、菜单开着时顶替状态图标露出来（24×24，离右边 6） */
  #workflowView .breakdown-layout-binder .sb-ep .episode-more { position: absolute; top: 6px; right: 6px; display: none; place-items: center; width: 24px; height: 24px; padding: 0;
    border-radius: 6px; color: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-ep .episode-more:hover:not(:disabled), #workflowView .breakdown-layout-binder .sb-ep.menu-open .episode-more {
    background: var(--surface-hover); color: var(--text); }
  /* 选中的那一行底色是 --accent-soft，--surface-hover 压上去看不出来 */
  #workflowView .breakdown-layout-binder .sb-ep.on .episode-more:hover:not(:disabled), #workflowView .breakdown-layout-binder .sb-ep.on.menu-open .episode-more {
    background: color-mix(in srgb, var(--text) 10%, transparent); }
  #workflowView .breakdown-layout-binder .sb-ep .episode-more:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  #workflowView .breakdown-layout-binder .sb-ep:is(:hover, :focus-within, .menu-open) .episode-more { display: grid; }
  #workflowView .breakdown-layout-binder .sb-ep:is(:hover, :focus-within, .menu-open) .sb-state { visibility: hidden; }
  /* 菜单开着的那一行保持悬浮的样子（不然鼠标移进菜单，这一行就像没被点过） */
  #workflowView .breakdown-layout-binder .sb-ep.menu-open:not(.on) .sb-ep-open { background: var(--surface-hover); }
  /* 触屏上没有悬浮：「⋯」一直在，状态图标往左让一格 */
  @media (hover: none) {
    #workflowView .breakdown-layout-binder .sb-ep .episode-more { display: grid; }
    #workflowView .breakdown-layout-binder .sb-ep .sb-ep-open { padding-right: 36px; }
    #workflowView .breakdown-layout-binder .sb-ep:is(:hover, :focus-within, .menu-open) .sb-state { visibility: visible; }
  }
  #workflowView .breakdown-layout-binder .sb-add { height: 36px; padding: 0 12px; border-radius: 8px; color: var(--muted) !important; font-size: 13px; }
  #workflowView .breakdown-layout-binder .sb-add:hover:not(:disabled) { color: var(--text) !important; }
  #workflowView .breakdown-layout-binder .sb-add i { flex: none; width: 18px; height: 18px; background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") left center / 14px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") left center / 14px no-repeat; }
  #workflowView .breakdown-layout-binder .sb-foot { flex: none; display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 18px; border-top: 1px solid var(--line); }
  #workflowView .breakdown-layout-binder .sb-meter { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 12px; }
  #workflowView .breakdown-layout-binder .sb-meter span { color: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-meter b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder .sb-meter i { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }
  /* 「生成全集分镜」管的是所有集，挪到目录栏底部（原来浮在本集那一栏底部正中，像是本集的操作） */
  #mainView #workflowView .breakdown-layout-binder .sb-all { justify-content: center; gap: 0; height: 34px; margin-bottom: 2px; border: 1px solid var(--line-strong); border-radius: 999px;
    color: var(--text); font-size: 13px; font-weight: 500; text-align: center; }
  #mainView #workflowView .breakdown-layout-binder .sb-all:hover:not(:disabled) { background: var(--surface-hover); }
  #mainView #workflowView .breakdown-layout-binder .sb-all .cost-chip { margin-left: 8px; background: var(--surface-soft); font-size: 12px; }

  /* ---- 标题行：集号集名 → 统计 → 重新拆分；分镜设置、主按钮靠右 ---- */
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head { flex: none; order: 0; flex-wrap: nowrap; gap: 12px; min-height: 68px; margin: 0;
    padding: 14px 20px 12px 26px; box-sizing: border-box; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head h3 { flex: none; order: 0; gap: 8px; font-size: 18px; font-weight: 700; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .detail-episode-code { color: var(--muted); font-size: 15px; font-weight: 600;
    font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .sb-stats { display: inline; order: 1; color: var(--muted); font-size: 13px; white-space: nowrap;
    font-variant-numeric: tabular-nums; }
  /* 1707 宽一行放不下时这一段先让（省略号收起），右上角「生成剩余提示词」不被挤出栏外；放得下的宽屏不收、一个像素不变（2026-10-09 实测） */
  #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .sb-stats { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .episode-redo-command { order: 2; display: inline-flex; align-items: center; height: 28px; min-height: 0;
    padding: 0 10px 0 12px; border: 0; border-radius: 999px; background: var(--surface-soft); color: var(--muted); font-size: 12px; font-weight: 500; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .episode-redo-command:hover:not(:disabled) { color: var(--text); }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .episode-redo-command .cost-chip { display: inline-flex; margin-left: 6px; padding: 0 6px 0 4px;
    background: var(--surface); font-size: 12px; line-height: 20px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .episode-shot-type { order: 3; margin-left: auto; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .episode-shot-type-button { height: 38px; min-height: 38px; border-radius: 999px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head #breakdownOneClickButton { order: 4; height: 38px; min-height: 38px; padding: 0 8px 0 20px; gap: 8px;
    border: 0; border-radius: 999px; background: var(--command-create-bg); color: var(--command-create-text); font-weight: 400; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head #breakdownOneClickButton:not(:has(.cost-chip)) { padding-right: 20px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head #breakdownOneClickButton .one-click-title { font-size: 14px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head #breakdownOneClickButton .cost-chip { margin: 0; background: var(--command-create-text);
    color: var(--command-create-bg); }

  /* ---- 本集分镜 | 出镜资产（标题行下面一条，不跟着内容滚） ---- */
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-tabs { display: flex; flex: none; order: 1; align-items: center; gap: 14px; min-height: 44px; padding: 0 20px 10px 26px; }
  #workflowView .breakdown-layout-binder .sb-seg { position: relative; display: inline-flex; flex: none; gap: 6px; padding: 4px; border-radius: 999px; background: var(--tab-rail-strong); }
  #workflowView .breakdown-layout-binder .sb-seg-thumb { position: absolute; top: 4px; bottom: 4px; left: 0; width: 0; border-radius: 999px; background: var(--primary-bg);
    transition: transform .24s cubic-bezier(.4, 0, .2, 1), width .24s cubic-bezier(.4, 0, .2, 1); }
  #mainView #workflowView .breakdown-layout-binder .sb-seg button { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 6px; min-height: 0; padding: 7px 14px;
    border: 0; border-radius: 999px; background: transparent; color: var(--text); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; transition: color .2s; }
  #mainView #workflowView .breakdown-layout-binder .sb-seg button.on { color: var(--on-primary); }
  #mainView #workflowView .breakdown-layout-binder .sb-seg button small { color: inherit; font-size: 12px; font-weight: 500; opacity: .72; font-variant-numeric: tabular-nums; }
  #mainView #workflowView .breakdown-layout-binder .sb-seg button:disabled { cursor: not-allowed; opacity: .5; }
  #workflowView .breakdown-layout-binder .sb-tabs-info { display: inline-flex; align-items: center; gap: 14px; margin-left: auto; color: var(--muted); font-size: 12px; white-space: nowrap;
    font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder .sb-tabs-info b { color: var(--text); font-weight: 600; }
  /* 全选：一颗小方框加字，勾了一部分是半选 */
  #mainView #workflowView .breakdown-layout-binder .sb-pickall { display: inline-flex; align-items: center; gap: 8px; height: 28px; min-height: 0; padding: 0 10px 0 8px; border: 0;
    border-radius: 8px; background: transparent; color: var(--muted); font-size: 12px; cursor: pointer; }
  #mainView #workflowView .breakdown-layout-binder .sb-pickall:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
  #workflowView .breakdown-layout-binder .sb-box { position: relative; flex: none; width: 16px; height: 16px; box-sizing: border-box; border: 1.5px solid var(--line-strong); border-radius: 4px; }
  #workflowView .breakdown-layout-binder .sb-box.on { border-color: var(--accent); background: var(--accent); }
  #workflowView .breakdown-layout-binder .sb-box.on::after { content: ""; position: absolute; inset: 0; background: #fff;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat; }
  #workflowView .breakdown-layout-binder .sb-box.some { border-color: var(--accent); }
  #workflowView .breakdown-layout-binder .sb-box.some::after { content: ""; position: absolute; left: 3px; right: 3px; top: 50%; height: 2px; margin-top: -1px; border-radius: 1px;
    background: var(--accent); }

  /* ---- 内容区：滚动的是这一块（不是带圆角的那一栏，免得左上角画出方角缺口） ---- */
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-main { display: flex; flex: 1 1 auto; order: 2; flex-direction: column; gap: 16px; min-height: 0;
    padding: 4px 20px 28px 26px; overflow-y: auto; scrollbar-gutter: stable; }
  #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > .sb-main { display: none; }
  #workflowView .breakdown-layout-binder .sb-main > * { flex: none; }

  /* 出镜资产那一侧照旧是分镜表那一版的角色 / 场景 / 道具和资产卡，只收一下排布 */
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > .asset-subtabs { order: 2; flex: none; margin: 0; padding: 0 20px 12px 26px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > :is(.episode-asset-grid, .detail-cta) { order: 3; flex: 1 1 auto; min-height: 0; margin: 0;
    padding: 0 20px 28px 26px; overflow-y: auto; align-content: start; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > .detail-cta { flex: none; margin: 0 20px 0 26px; padding: 20px 18px; }

  /* ---- 提醒（正文改过、时长超出设定） ---- */
  #workflowView .breakdown-layout-binder .sb-alert { display: flex; align-items: center; gap: 12px; padding: 12px 14px 12px 16px; border-radius: 12px;
    background: color-mix(in srgb, var(--warn, #d9a441) 13%, var(--surface)); color: var(--text); font-size: 13px; line-height: 1.6; }
  #workflowView .breakdown-layout-binder .sb-alert::before { content: ""; flex: none; width: 16px; height: 16px; background: var(--warn, #d9a441);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.8 15 14H1z' fill='none' stroke='black' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M8 6.2v3.6' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.7' r='.95'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.8 15 14H1z' fill='none' stroke='black' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M8 6.2v3.6' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.7' r='.95'/%3E%3C/svg%3E") center / contain no-repeat; }
  #workflowView .breakdown-layout-binder .sb-alert.info { background: var(--surface-soft); color: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-alert.info::before { background: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-alert p { flex: 1 1 auto; min-width: 0; margin: 0; }

  /* ---- 本集概要（钩子 / 转折 / 悬念一行三格；视觉设计收在下面一行） ---- */
  #workflowView .breakdown-layout-binder .sb-brief { padding: 16px 20px 14px; border-radius: 14px; background: var(--surface-soft); }
  #workflowView .breakdown-layout-binder .sb-beats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; margin: 0; }
  #workflowView .breakdown-layout-binder .sb-beats div { min-width: 0; }
  #workflowView .breakdown-layout-binder .sb-beats dt { margin: 0 0 4px; color: var(--muted); font-size: 12px; }
  #workflowView .breakdown-layout-binder .sb-beats dd { display: -webkit-box; margin: 0; overflow: hidden; color: var(--text); font-size: 13px; line-height: 1.65; -webkit-line-clamp: 2;
    -webkit-box-orient: vertical; }
  #workflowView .breakdown-layout-binder .sb-look { display: flex; align-items: center; gap: 10px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); font-size: 13px; }
  #workflowView .breakdown-layout-binder .sb-look strong { flex: none; font-weight: 600; }
  #workflowView .breakdown-layout-binder .sb-look span { min-width: 0; overflow: hidden; color: var(--muted); white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-look em { color: var(--warn, #d9a441); font-style: normal; }
  #workflowView .breakdown-layout-binder .sb-look-actions { display: flex; flex: none; gap: 6px; margin-left: auto; }
  #mainView #workflowView .breakdown-layout-binder .sb-text-btn { display: inline-flex; align-items: center; gap: 4px; height: 28px; min-height: 0; padding: 0 10px; border: 0; border-radius: 8px;
    background: transparent; color: var(--muted); font-size: 12px; font-weight: 500; cursor: pointer; white-space: nowrap; }
  #mainView #workflowView .breakdown-layout-binder .sb-text-btn:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
  #mainView #workflowView .breakdown-layout-binder .sb-text-btn:disabled { cursor: not-allowed; opacity: .5; }
  #mainView #workflowView .breakdown-layout-binder .sb-text-btn .cost-chip { margin-left: 2px; background: var(--surface-soft); font-size: 12px; }

  /* ---- 按钮：下一步（一镜一颗）、首帧 | 直出 ---- */
  #mainView #workflowView .breakdown-layout-binder .sb-next { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; min-height: 0; padding: 0 14px;
    border: 1px solid var(--btn-outline-border); border-radius: 999px; background: var(--btn-outline-bg); color: var(--text); font-size: 13px; font-weight: 400; white-space: nowrap;
    cursor: pointer; transition: background-color .15s, translate .15s, scale .15s; }
  #mainView #workflowView .breakdown-layout-binder .sb-next:hover:not(:disabled) { background: var(--btn-outline-hover); }
  #mainView #workflowView .breakdown-layout-binder .sb-next:active:not(:disabled) { translate: 0 1px; scale: .98; }
  #mainView #workflowView .breakdown-layout-binder .sb-next .jump-icon { width: 12px; height: 12px; margin: 0; }
  #mainView #workflowView .breakdown-layout-binder .sb-next .cost-chip { margin: 0 -6px 0 2px; padding: 0 6px 0 4px; background: var(--surface-soft); font-size: 12px; line-height: 20px; }
  /* 生成类（要扣积分的）用主按钮那一身：深色白底黑字、浅色黑底白字；悬浮不变样，按下照旧下沉一点 */
  #mainView #workflowView .breakdown-layout-binder .sb-next.gen { border-color: transparent; background: var(--command-create-bg); color: var(--command-create-text); }
  #mainView #workflowView .breakdown-layout-binder .sb-next.gen:hover:not(:disabled) { background: var(--command-create-bg); }
  #mainView #workflowView .breakdown-layout-binder .sb-next.gen .cost-chip { background: var(--command-create-text); color: var(--command-create-bg); }
  #mainView #workflowView .breakdown-layout-binder .sb-next:disabled { cursor: not-allowed; opacity: .45; }
  #workflowView .breakdown-layout-binder .sb-running { display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 14px; border-radius: 999px; background: var(--accent-soft);
    color: var(--accent-text); font-size: 13px; font-weight: 500; white-space: nowrap; }
  #workflowView .breakdown-layout-binder .sb-running::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; animation: script-pulse 1.2s ease-in-out infinite; }
  /* 出片方式：首帧 | 直出两段（全站滑轨那一身的小号）；两段一样宽，滑块按 is-direct 摆，勾的几镜方式不一时没有滑块 */
  #workflowView .breakdown-layout-binder .sb-mode { padding: 3px; gap: 2px; }
  #workflowView .breakdown-layout-binder .sb-mode .sb-seg-thumb { top: 3px; bottom: 3px; left: 3px; width: calc((100% - 8px) / 2); }
  #workflowView .breakdown-layout-binder .sb-mode.is-direct .sb-seg-thumb { transform: translateX(calc(100% + 2px)); }
  #workflowView .breakdown-layout-binder .sb-mode.is-mixed .sb-seg-thumb { display: none; }
  #mainView #workflowView .breakdown-layout-binder .sb-mode button { flex: 1 1 0; justify-content: center; padding: 3px 10px; font-size: 12px; font-weight: 600; }

  /* ---- 画面：出过图看图；没出图按状态写一句；直出另一种底。宽高比一律跟项目画幅（--sb-ratio） ---- */
  #workflowView .breakdown-layout-binder .sb-frame { position: relative; display: grid; place-items: center; overflow: hidden; aspect-ratio: var(--sb-ratio, 9 / 16); border-radius: 8px;
    background: var(--surface); color: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-frame > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  /* 出过的图比例和项目画幅对不上（改画幅之前出的）：完整显示不裁切，两边垫一层同一张图的模糊放大版，详情里那张大图右上角写「比例不一致」 */
  #workflowView .breakdown-layout-binder .sb-frame > .sb-frame-bg { display: none; filter: blur(16px) brightness(.5); transform: scale(1.2); }
  #workflowView .breakdown-layout-binder .sb-frame[data-misfit] > .sb-frame-bg { display: block; }
  #workflowView .breakdown-layout-binder .sb-frame[data-misfit] > .sb-frame-img { object-fit: contain; }
  #workflowView .breakdown-layout-binder .sb-misfit-tag { display: none; position: absolute; z-index: 1; top: 8px; right: 8px; padding: 0 7px; border-radius: 6px; background: rgba(0, 0, 0, .66);
    color: #fff; font-size: 11px; line-height: 20px; white-space: nowrap; }
  #workflowView .breakdown-layout-binder .sb-visual .sb-frame[data-misfit] .sb-misfit-tag { display: block; }
  #workflowView .breakdown-layout-binder .sb-frame > .sb-frame-img.image-zoomable { cursor: zoom-in; }
  /* 没出图：三分线当底纹（像一张空的分镜格），中间写景别 */
  #workflowView .breakdown-layout-binder .sb-frame:is([data-state="ready"], [data-state="todo"]) { background:
    linear-gradient(var(--line), var(--line)) 33.333% 0 / 1px 100% no-repeat,
    linear-gradient(var(--line), var(--line)) 66.666% 0 / 1px 100% no-repeat,
    linear-gradient(var(--line), var(--line)) 0 33.333% / 100% 1px no-repeat,
    linear-gradient(var(--line), var(--line)) 0 66.666% / 100% 1px no-repeat,
    var(--surface); }
  /* 直出：虚线框（本来就不出分镜图，和「还没出图」分得开） */
  #workflowView .breakdown-layout-binder .sb-frame[data-state="direct"] { background-color: var(--surface);
    background-image: repeating-linear-gradient(90deg, var(--line-strong) 0 6px, transparent 6px 11px), repeating-linear-gradient(90deg, var(--line-strong) 0 6px, transparent 6px 11px),
      repeating-linear-gradient(0deg, var(--line-strong) 0 6px, transparent 6px 11px), repeating-linear-gradient(0deg, var(--line-strong) 0 6px, transparent 6px 11px);
    background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%; background-position: 0 0, 0 100%, 0 0, 100% 0; background-repeat: no-repeat; }
  /* 直出：框里只写「直出」，出过的图、要用的参考图都不摆（用户 2026-10-09）；清单里的小图字小一号 */
  #workflowView .breakdown-layout-binder .sb-direct-text { color: var(--text); font-size: 16px; font-weight: 600; letter-spacing: .08em; }
  #workflowView .breakdown-layout-binder .sb-row .sb-direct-text { font-size: 12px; letter-spacing: 0; }
  #workflowView .breakdown-layout-binder .sb-frame-label { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 6px; text-align: center; }
  #workflowView .breakdown-layout-binder .sb-frame-label i { width: 18px; height: 18px; background: currentColor; -webkit-mask: url("/static/icon-mode-image.png") center / contain no-repeat;
    mask: url("/static/icon-mode-image.png") center / contain no-repeat; opacity: .8; }
  #workflowView .breakdown-layout-binder .sb-frame-label b { color: var(--text); font-size: 13px; font-weight: 600; }
  #workflowView .breakdown-layout-binder .sb-frame-label small { max-width: 26em; color: var(--muted); font-size: 12px; line-height: 1.6; }
  #workflowView .breakdown-layout-binder .sb-frame-label em { color: var(--muted); font-size: 12px; font-style: normal; }

  /* ---- 出镜资产（小头像 + 名字） ---- */
  #workflowView .breakdown-layout-binder .sb-av { flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; overflow: hidden; border-radius: 50%; background: var(--surface);
    color: var(--muted); font-size: 11px; box-shadow: 0 0 0 2px var(--surface-soft); }
  #workflowView .breakdown-layout-binder .sb-av img { width: 100%; height: 100%; object-fit: cover; }
  #workflowView .breakdown-layout-binder .sb-av.scenes, #workflowView .breakdown-layout-binder .sb-av.props { border-radius: 6px; }
  #workflowView .breakdown-layout-binder .sb-asset { display: grid; grid-template-columns: 36px minmax(0, 1fr); grid-template-rows: auto auto; align-items: center; column-gap: 10px; min-width: 0; }
  #workflowView .breakdown-layout-binder .sb-asset > :first-child { grid-row: 1 / 3; display: grid; place-items: center; width: 36px; height: 36px; overflow: hidden; border-radius: 8px;
    background: var(--surface); color: var(--muted); font-size: 13px; }
  #workflowView .breakdown-layout-binder .sb-asset img { width: 100%; height: 100%; object-fit: cover; }
  #workflowView .breakdown-layout-binder .sb-asset b { min-width: 0; overflow: hidden; color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-asset small { color: var(--muted); font-size: 12px; }

  /* ---- 详情左边那张「画面」卡：画面、出片方式、下一步、出镜资产 ---- */
  #workflowView .breakdown-layout-binder .sb-visual { display: flex; flex-direction: column; gap: 12px; min-width: 0; padding: 14px 16px 16px; border-radius: 14px; background: var(--surface-soft); }
  #workflowView .breakdown-layout-binder .sb-visual-head { display: flex; align-items: center; gap: 10px; min-height: 30px; }
  #workflowView .breakdown-layout-binder .sb-visual-head b { font-size: 14px; font-weight: 700; }
  #workflowView .breakdown-layout-binder .sb-visual-head .sb-mode { margin-left: auto; }
  #workflowView .breakdown-layout-binder .sb-visual-frame { position: relative; display: flex; justify-content: center; }
  #workflowView .breakdown-layout-binder .sb-visual-frame .sb-frame { border-radius: 10px; }
  #workflowView .breakdown-layout-binder .sb-visual-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  #workflowView .breakdown-layout-binder .sb-visual-actions .sb-next { margin-right: 4px; }
  #workflowView .breakdown-layout-binder .sb-info-sec h4 { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px;
    color: var(--muted); font-size: 12px; font-weight: 600; letter-spacing: 0; }
  #workflowView .breakdown-layout-binder .sb-info-sec h4 small { color: var(--muted); font-size: 12px; font-weight: 400; }
  #workflowView .breakdown-layout-binder .sb-asset-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); gap: 10px 12px; }
  #workflowView .breakdown-layout-binder .sb-muted { margin: 0; color: var(--muted); font-size: 13px; }

  /* ---- 详情右边：镜头信息（切镜整宽；下面两栏：概要、台词、意图 | 场景光位、出镜资产）。生图提示词不在这里常驻，点画面卡上的「提示词详情」才看（用户 2026-10-09） ---- */
  #workflowView .breakdown-layout-binder .sb-info { display: flex; flex-direction: column; gap: 20px; min-width: 0; container: sb-info / inline-size; }
  /* 顶上那一行：第几镜、时长、上一镜 / 下一镜 / 收起（原来挂在右边那一栏的标题上，窄了上下排时跑到画面卡底下去） */
  #workflowView .breakdown-layout-binder .sb-detail-head { display: flex; align-items: center; gap: 10px; min-height: 30px; }
  #workflowView .breakdown-layout-binder .sb-detail-head > b { flex: none; font-size: 14px; font-weight: 700; }
  #workflowView .breakdown-layout-binder .sb-info-meta { min-width: 0; overflow: hidden; color: var(--muted); font-size: 13px; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-info-tools { display: flex; flex: none; align-items: center; gap: 2px; margin-left: auto; align-self: center; }
  #mainView #workflowView .breakdown-layout-binder .sb-icon-btn { display: inline-grid; place-items: center; width: 28px; height: 28px; min-height: 0; padding: 0; border: 0; border-radius: 8px;
    background: transparent; color: var(--muted); cursor: pointer; }
  #mainView #workflowView .breakdown-layout-binder .sb-icon-btn:hover { background: var(--surface-hover); color: var(--text); }
  #workflowView .breakdown-layout-binder .sb-icon-btn .close-icon { width: 12px; height: 12px; }
  #workflowView .breakdown-layout-binder .sb-info-sec p { margin: 0; color: var(--text); font-size: 14px; line-height: 1.8; }
  #workflowView .breakdown-layout-binder .sb-info-sec p.sb-muted { color: var(--muted); font-size: 13px; }
  /* 切镜：一刀一行（刀号 · 时间 · 景别运镜 · 动作），不加色条 */
  /* 第三列「景别 · 运镜」最多占三分之一、超出换行：用户自己写的运镜会很长（《代驾》第 3 镜「第一辆车 · 机位在车内后视镜的位置、朝后拍后排右座」），
     原来这一列按内容定宽又不换行，把动作那一列挤成一个字宽、详情拉成三千多像素高（2026-10-09 用户：「怎么文字变成1列了？」）。短的照旧按内容宽 */
  #workflowView .breakdown-layout-binder .sb-cuts { display: grid; grid-template-columns: max-content max-content fit-content(33%) minmax(0, 1fr); margin: 0; padding: 0; list-style: none;
    border-radius: 10px; background: var(--surface-soft); }
  #workflowView .breakdown-layout-binder .sb-cuts li { display: contents; }
  #workflowView .breakdown-layout-binder .sb-cuts li > * { padding: 10px 0 10px 16px; border-top: 1px solid var(--line); font-size: 13px; line-height: 1.6; }
  #workflowView .breakdown-layout-binder .sb-cuts li:first-child > * { border-top: 0; }
  #workflowView .breakdown-layout-binder .sb-cuts li > :last-child { padding-right: 16px; }
  #workflowView .breakdown-layout-binder .sb-cut-no, #workflowView .breakdown-layout-binder .sb-cut-time { color: var(--muted); font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder .sb-cuts b { font-weight: 600; }
  #workflowView .breakdown-layout-binder .sb-cut-action { color: var(--muted); overflow-wrap: anywhere; }
  /* 镜头信息那一栏窄（不到 520）：四列并排各剩一小截，改成一刀两行——
     第一行「第 N 刀 | 景别 · 运镜」，第二行「时长 | 动作」 */
  @container sb-info (max-width: 520px) {
    #workflowView .breakdown-layout-binder .sb-cuts { display: block; }
    #workflowView .breakdown-layout-binder .sb-cuts li { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px 12px; padding: 10px 16px; border-top: 1px solid var(--line); }
    #workflowView .breakdown-layout-binder .sb-cuts li:first-child { border-top: 0; }
    #workflowView .breakdown-layout-binder .sb-cuts li > *, #workflowView .breakdown-layout-binder .sb-cuts li > :last-child { padding: 0; border-top: 0; }
    #workflowView .breakdown-layout-binder .sb-cut-no { grid-area: 1 / 1; }
    #workflowView .breakdown-layout-binder .sb-cuts b { grid-area: 1 / 2; }
    #workflowView .breakdown-layout-binder .sb-cut-time { grid-area: 2 / 1; }
    #workflowView .breakdown-layout-binder .sb-cut-action { grid-area: 2 / 2; }
  }
  #workflowView .breakdown-layout-binder .sb-dlg { display: grid; grid-template-columns: fit-content(8em) minmax(0, 1fr); gap: 8px 14px; font-size: 14px; line-height: 1.7; }
  #workflowView .breakdown-layout-binder .sb-dlg b { min-width: 0; color: var(--muted); font-weight: 500; overflow-wrap: anywhere; }
  #workflowView .breakdown-layout-binder .sb-dlg span { min-width: 0; overflow-wrap: anywhere; }
  #workflowView .breakdown-layout-binder .sb-dlg-cue { margin-right: 4px; color: var(--muted); font-size: 12px; font-style: normal; }
  /* 切镜下面两栏：左边讲什么（概要、台词、意图），右边怎么拍、谁出镜（场景光位、出镜资产）；一栏不到 300 宽就自动上下排 */
  #workflowView .breakdown-layout-binder .sb-info-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 20px 32px; align-items: start; }
  #workflowView .breakdown-layout-binder .sb-info-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
  #workflowView .breakdown-layout-binder .sb-lookgrid { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 14px; margin: 0; font-size: 13px; line-height: 1.7; }
  #workflowView .breakdown-layout-binder .sb-lookgrid dt { color: var(--muted); }
  #workflowView .breakdown-layout-binder .sb-lookgrid dd { margin: 0; }

  /* ---- 勾了镜头才出来的那一条（浮在本集那一栏底部正中） ---- */
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-bulk { position: absolute; z-index: 5; left: 0; right: 0; bottom: 18px; display: flex; width: max-content; max-width: calc(100% - 40px);
    margin: 0 auto; align-items: center; gap: 6px; padding: 8px 8px 8px 16px; border: 1px solid var(--line-strong); border-radius: 16px; background: var(--surface);
    box-shadow: 0 14px 40px rgba(0, 0, 0, .34); font-size: 13px; }
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-bulk[hidden] { display: none; }
  /* 两组（已选 + 出片方式 | 删除、取消选择、生成提示词）平时不占位，和原来排成一行一模一样；窄的那几档见下面「批量条两行」 */
  #workflowView .breakdown-layout-binder .sb-bulk-group { display: contents; }
  #workflowView .breakdown-layout-binder .sb-bulk-count { margin-right: 6px; font-weight: 600; white-space: nowrap; }
  #workflowView .breakdown-layout-binder .sb-bulk-sep { width: 1px; height: 20px; margin: 0 4px; background: var(--line-strong); }
  #workflowView .breakdown-layout-binder .sb-bulk-label { color: var(--muted); white-space: nowrap; }
  #mainView #workflowView .breakdown-layout-binder .sb-bulk .sb-text-btn { height: 32px; font-size: 13px; }
  #mainView #workflowView .breakdown-layout-binder .sb-bulk .sb-text-btn.danger { color: var(--danger); }
  #mainView #workflowView .breakdown-layout-binder .sb-bulk .sb-text-btn.danger:hover:not(:disabled) { background: var(--btn-danger-bg); color: var(--danger); }
  #mainView #workflowView .breakdown-layout-binder .sb-bulk .sb-next { margin-left: 4px; }
  /* 那一条浮在内容上：内容区底下多留一截，最后一行不被它压住 */
  #workflowView .breakdown-layout-binder #breakdownDetail[data-picked]:not([data-picked="0"]) > .sb-main { padding-bottom: 96px; }

  /* ---- 没拆的集、没正文的集：铺满的空态，按钮就在眼前 ---- */
  #workflowView .breakdown-layout-binder .sb-empty { display: grid; justify-items: center; gap: 10px; margin-top: 8px; padding: 64px 24px 68px; border: 1px dashed var(--line-strong);
    border-radius: 14px; text-align: center; }
  #workflowView .breakdown-layout-binder .sb-empty i { width: 40px; height: 40px; margin-bottom: 6px; background: var(--muted);
    -webkit-mask: url("/static/icon-mode-storyboard.png") center / contain no-repeat; mask: url("/static/icon-mode-storyboard.png") center / contain no-repeat; opacity: .7; }
  #workflowView .breakdown-layout-binder .sb-empty b { font-size: 18px; font-weight: 700; }
  #workflowView .breakdown-layout-binder .sb-empty p { max-width: 34em; margin: 0; color: var(--muted); font-size: 13px; line-height: 1.75; }
  #workflowView .breakdown-layout-binder .sb-empty-actions { display: flex; gap: 10px; margin-top: 10px; }
  #mainView #workflowView .breakdown-layout-binder .sb-empty .sb-next { height: 40px; padding: 0 22px; font-size: 14px; }
  #mainView #workflowView .breakdown-layout-binder .sb-empty .sb-next.gen { padding-right: 10px; }
  #mainView #workflowView .breakdown-layout-binder .sb-empty .sb-next .cost-chip { margin: 0 0 0 4px; }

  /* ---- 勾选框（行上那一颗；和别的列表一样，点行的空白处也是勾选） ---- */
  #workflowView .breakdown-layout-binder .sb-check { position: relative; display: inline-grid; place-items: center; width: 28px; height: 28px; cursor: pointer; }
  #workflowView .breakdown-layout-binder .sb-check input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
  #workflowView .breakdown-layout-binder .sb-check .sb-box { background: var(--surface); }
  #workflowView .breakdown-layout-binder .sb-check .sb-box.on { background: var(--accent); }
  #workflowView .breakdown-layout-binder .sb-check:has(input:focus-visible) .sb-box { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* ---- 镜头清单：一镜一张卡（这一行 + 展开的详情都在卡里，底色、悬浮、勾选挂在卡上，开合时圆角不变、不出缺角） ---- */
  #workflowView .breakdown-layout-binder .sb-list { display: flex; flex-direction: column; gap: 8px; }
  #workflowView .breakdown-layout-binder .sb-item { border-radius: 12px; background: var(--surface-soft); transition: background-color .15s ease; }
  #workflowView .breakdown-layout-binder .sb-item:not(.open):hover { background: color-mix(in srgb, var(--surface-soft) 70%, var(--surface-hover)); }
  #workflowView .breakdown-layout-binder .sb-item.picked { background: color-mix(in srgb, var(--accent) 10%, var(--surface-soft));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent); }
  #workflowView .breakdown-layout-binder .sb-row { --thumb-h: 96px; --thumb-max: 210px; display: grid; grid-template-columns: 28px auto minmax(0, 1.35fr) minmax(0, 1fr) 204px; align-items: center;
    gap: 18px; min-height: 0; padding: 10px 16px 10px 8px; border-radius: 12px; cursor: default; }
  /* 缩略图高度固定、宽度跟画幅（9:16 是窄条，21:9 是宽条，封顶后按比例收矮） */
  #workflowView .breakdown-layout-binder .sb-row .sb-frame { width: min(var(--thumb-max), calc(var(--thumb-h) * var(--sb-r, .5625))); height: auto; border-radius: 8px; }
  #workflowView .breakdown-layout-binder .sb-row .sb-frame[data-open] { cursor: pointer; }
  #workflowView .breakdown-layout-binder .sb-row .sb-frame[data-open]:hover { box-shadow: 0 0 0 2px var(--line-strong); }
  #workflowView .breakdown-layout-binder .sb-row .sb-frame-label { gap: 4px; padding: 4px; }
  #workflowView .breakdown-layout-binder .sb-row .sb-frame-label i { width: 16px; height: 16px; }
  #workflowView .breakdown-layout-binder .sb-row .sb-frame-label em { font-size: 11px; line-height: 1.3; }
  #workflowView .breakdown-layout-binder .sb-row-main { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
  #workflowView .breakdown-layout-binder .sb-row-title { display: flex; align-items: baseline; gap: 10px; min-width: 0; white-space: nowrap; }
  #workflowView .breakdown-layout-binder .sb-row-title .sb-no { flex: none; color: var(--muted); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
  #workflowView .breakdown-layout-binder .sb-row-name { flex: none; border-radius: 4px; font-size: 15px; font-weight: 700; cursor: pointer; }
  #workflowView .breakdown-layout-binder .sb-row-name:hover { text-decoration: underline; text-underline-offset: 3px; }
  #workflowView .breakdown-layout-binder .sb-row-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  #workflowView .breakdown-layout-binder .sb-row-title .sb-meta { min-width: 0; overflow: hidden; color: var(--muted); font-size: 12px; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-row-camera { overflow: hidden; color: var(--text); font-size: 13px; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-row-summary { display: -webkit-box; margin: 0; overflow: hidden; color: var(--muted); font-size: 13px; line-height: 1.6; -webkit-line-clamp: 2;
    -webkit-box-orient: vertical; }
  #workflowView .breakdown-layout-binder .sb-row-side { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
  #workflowView .breakdown-layout-binder .sb-refs { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--muted); font-size: 12px; }
  #workflowView .breakdown-layout-binder .sb-refs-avatars { display: flex; flex: none; }
  #workflowView .breakdown-layout-binder .sb-refs-avatars .sb-av + .sb-av { margin-left: -6px; }
  #workflowView .breakdown-layout-binder .sb-refs span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  /* 台词（用户 2026-10-09 问台词多、长时排不排得下）：最多两句、多的写「共 N 句」；说话人只写名字、最多 6 个字宽（超出省略），
     原来说话人那一列按内容定宽，带表演提示的长说话人把旁边的台词挤成 0 宽 */
  #workflowView .breakdown-layout-binder .sb-lines { display: grid; grid-template-columns: fit-content(6em) minmax(0, 1fr); gap: 2px 8px; margin: 0; font-size: 13px; line-height: 1.6; }
  #workflowView .breakdown-layout-binder .sb-lines b { min-width: 0; overflow: hidden; color: var(--muted); font-weight: 500; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-lines-more { grid-column: 1 / -1; color: var(--muted); font-size: 12px; }
  #workflowView .breakdown-layout-binder .sb-lines-more.few { display: none; }
  #workflowView .breakdown-layout-binder .sb-lines span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  #workflowView .breakdown-layout-binder .sb-lines.none { display: block; color: var(--muted); font-size: 12px; }
  /* 右边那一栏（用户 2026-10-09：「右侧按钮：去出片、首帧/直出、详情，不整齐，看起来很奇怪」——原来各按内容宽靠右摆，两行宽窄、高矮都不一样）：
     上下两行一样宽、左右对齐、一样 30 高。上面那颗下一步（去生成视频 / 去生成图片 / 生成提示词 / 生成中）撑满这一栏，下面出片方式占满剩下的宽、「详情」贴右边。
     **这一栏的宽是按第二行量的**：出片方式改叫「首帧生成 / 直接生成」之后（2026-10-09），第二行按内容要 196（切换两段各左右 8 的内边距），
     原来的 176 / 笔记本那档 160 放不下，「详情」超出卡片（用户报的）；各档统一 204，留 8 的余量（别人机器上字宽一点不超）。字再变长，这几个数一起量 */
  #workflowView .breakdown-layout-binder .sb-row-actions { display: flex; flex-direction: column; align-items: stretch; gap: 8px; }
  #mainView #workflowView .breakdown-layout-binder .sb-row-actions .sb-next, #workflowView .breakdown-layout-binder .sb-row-actions .sb-running { width: 100%; height: 30px;
    justify-content: center; }
  #workflowView .breakdown-layout-binder .sb-row-sub { display: flex; align-items: center; gap: 6px; }
  #workflowView .breakdown-layout-binder .sb-row-sub .sb-mode { flex: 1 1 auto; box-sizing: border-box; height: 30px; }
  #mainView #workflowView .breakdown-layout-binder .sb-row-sub .sb-text-btn { height: 30px; padding: 0 8px; }
  #mainView #workflowView .breakdown-layout-binder .sb-row-sub .sb-mode button { padding: 3px 8px; }
  #mainView #workflowView .breakdown-layout-binder .sb-row-more { gap: 2px; padding-right: 6px; }
  #mainView #workflowView .breakdown-layout-binder .sb-row-more .spec-chevron { display: inline-flex; color: var(--muted); transition: transform .3s cubic-bezier(.4, 0, .2, 1); }
  #mainView #workflowView .breakdown-layout-binder .sb-item.open .sb-row-more .spec-chevron { transform: rotate(180deg); }
  #workflowView .breakdown-layout-binder .sb-row-more .sb-more-open, #workflowView .breakdown-layout-binder .sb-item.open .sb-row-more .sb-more-closed { display: none; }
  #workflowView .breakdown-layout-binder .sb-item.open .sb-row-more .sb-more-open { display: inline; }

  /* ---- 展开的详情：和这一行连成一张卡（顶上一行，下面左边画面卡、右边镜头信息）；展开 / 收起的过渡在 ShotList.tsx（高度 + 内边距 + 透明度，.3 秒 / .24 秒）。
     展开区自己当尺寸容器（sb-detail）：够宽左右排，不够宽上下排——按这一块的宽判断、不按屏宽（原来按屏宽分档，1707 宽的屏开着助手、画幅 16:9 时
     右边那一栏被挤到 62 像素，切镜一个字一行，2026-10-09 实测） ---- */
  #workflowView .breakdown-layout-binder .sb-row-detail { position: relative; padding: 18px 20px 22px; container: sb-detail / inline-size; }
  /* 画面卡的宽只跟画幅走（用户 2026-10-09：「左侧的画面在切换首帧和直出时，不能出现变宽的情况」——原来这一列按内容定宽，首帧 / 直出换了按钮、状态字就跟着变）：
     竖幅按高 400 定宽，方幅 / 横幅 / 超宽按宽；上下排时图按 --sb-stack-w 摆在画面卡左边 */
  #workflowView .breakdown-layout-binder .sb-main { --sb-frame-w: calc(400px * var(--sb-r, .5625)); --sb-stack-w: calc(300px * var(--sb-r, .5625)); }
  #workflowView .breakdown-layout-binder .sb-main[data-ratio="square"] { --sb-frame-w: 300px; --sb-stack-w: 220px; }
  #workflowView .breakdown-layout-binder .sb-main[data-ratio="landscape"] { --sb-frame-w: 420px; --sb-stack-w: 300px; }
  #workflowView .breakdown-layout-binder .sb-main[data-ratio="wide"] { --sb-frame-w: 480px; --sb-stack-w: 340px; }
  #workflowView .breakdown-layout-binder .sb-detail-body { display: grid; grid-template-columns: calc(var(--sb-frame-w) + 32px) minmax(0, 1fr);
    grid-template-areas: "head head" "visual info"; gap: 14px 28px; align-items: start; }
  #workflowView .breakdown-layout-binder .sb-detail-body > .sb-detail-head { grid-area: head; }
  #workflowView .breakdown-layout-binder .sb-detail-body > .sb-visual { grid-area: visual; }
  #workflowView .breakdown-layout-binder .sb-detail-body > .sb-info { grid-area: info; }
  #workflowView .breakdown-layout-binder .sb-row-detail::before { content: ""; position: absolute; top: 0; left: 16px; right: 16px; height: 1px; background: var(--line); }
  /* 详情底色和这一行一样：画面卡、切镜换深一档才分得出来 */
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-visual { align-self: start; background: var(--surface); }
  /* 画面卡是 --surface：没出图的空格子、直出的虚线框换成 --surface-soft，不然和画面卡一个颜色、看不出格子在哪 */
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-frame:not([data-state="image"]) { background-color: var(--surface-soft); }
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-cuts { background: var(--surface); }
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-av { box-shadow: 0 0 0 2px var(--surface); }
  /* 出镜资产在右边那一栏：每格至少 160 宽，「角色 · 暂无设定图」一行写得下 */
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-asset-list { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px 14px; }
  /* 「切镜」那一行和画面卡里「画面」那一行对齐 */
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-info { padding-top: 20px; }
  #workflowView .breakdown-layout-binder .sb-row-detail .sb-visual-frame .sb-frame { width: var(--sb-frame-w); height: auto; }
  /* 竖幅、方幅：展开区不到 760 宽换小一号的图（--sb-stack-w）照样左右排，不到 560 宽才上下排——
     顶上一行、画面卡占一整行（图在左、首帧 / 直出和按钮在右）、镜头信息在下面 */
  @container sb-detail (max-width: 759px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-detail-body { grid-template-columns: calc(var(--sb-stack-w) + 32px) minmax(0, 1fr); }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual-frame .sb-frame { width: var(--sb-stack-w); }
  }
  @container sb-detail (max-width: 559px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-detail-body { grid-template-columns: minmax(0, 1fr); grid-template-areas: "head" "visual" "info"; gap: 14px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual { display: grid; grid-template-columns: min(var(--sb-stack-w), 56%) minmax(0, 1fr);
      grid-template-rows: auto auto 1fr; grid-template-areas: "frame head" "frame actions" "frame ."; gap: 12px 20px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual-head { grid-area: head; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual-frame { grid-area: frame; justify-content: flex-start; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual-frame .sb-frame { width: 100%; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-visual-actions { grid-area: actions; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="portrait"], [data-ratio="square"]) .sb-row-detail .sb-info { padding-top: 6px; }
  }
  /* 横幅：展开区不到 900 宽换小一号的图（--sb-stack-w）照样左右排，不到 760 宽才上下排——
     顶上一行、画面卡占一整行（图在左、首帧 / 直出和按钮在右）、镜头信息在下面 */
  @container sb-detail (max-width: 899px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-detail-body { grid-template-columns: calc(var(--sb-stack-w) + 32px) minmax(0, 1fr); }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual-frame .sb-frame { width: var(--sb-stack-w); }
  }
  @container sb-detail (max-width: 759px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-detail-body { grid-template-columns: minmax(0, 1fr); grid-template-areas: "head" "visual" "info"; gap: 14px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual { display: grid; grid-template-columns: min(var(--sb-stack-w), 56%) minmax(0, 1fr);
      grid-template-rows: auto auto 1fr; grid-template-areas: "frame head" "frame actions" "frame ."; gap: 12px 20px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual-head { grid-area: head; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual-frame { grid-area: frame; justify-content: flex-start; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual-frame .sb-frame { width: 100%; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-visual-actions { grid-area: actions; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="landscape"]) .sb-row-detail .sb-info { padding-top: 6px; }
  }
  /* 超宽：展开区不到 980 宽换小一号的图（--sb-stack-w）照样左右排，不到 800 宽才上下排——
     顶上一行、画面卡占一整行（图在左、首帧 / 直出和按钮在右）、镜头信息在下面 */
  @container sb-detail (max-width: 979px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-detail-body { grid-template-columns: calc(var(--sb-stack-w) + 32px) minmax(0, 1fr); }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual-frame .sb-frame { width: var(--sb-stack-w); }
  }
  @container sb-detail (max-width: 799px) {
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-detail-body { grid-template-columns: minmax(0, 1fr); grid-template-areas: "head" "visual" "info"; gap: 14px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual { display: grid; grid-template-columns: min(var(--sb-stack-w), 56%) minmax(0, 1fr);
      grid-template-rows: auto auto 1fr; grid-template-areas: "frame head" "frame actions" "frame ."; gap: 12px 20px; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual-head { grid-area: head; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual-frame { grid-area: frame; justify-content: flex-start; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual-frame .sb-frame { width: 100%; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-visual-actions { grid-area: actions; }
    #workflowView .breakdown-layout-binder .sb-main:is([data-ratio="wide"]) .sb-row-detail .sb-info { padding-top: 6px; }
  }
}
@media (min-width: 1200px) and (prefers-reduced-motion: reduce) {
  #mainView #workflowView .breakdown-layout-binder .sb-row-more .spec-chevron, #workflowView .breakdown-layout-binder .sb-seg-thumb { transition: none; }
}

/* 笔记本（1200–1599）：目录栏 216、助手按屏宽收，内边距收一档；清单那一行把出镜资产、台词挪到概要下面，详情里横幅 / 超宽的画面卡占一整行 */
@media (min-width: 1200px) and (max-width: 1599px) {
  #workflowView .breakdown-layout-binder { grid-template-columns: 216px minmax(0, 1fr) clamp(360px, 28vw, 540px); }
  #workflowView .breakdown-layout-binder .sb-cover { padding: 16px 16px 12px; }
  #workflowView .breakdown-layout-binder .sb-nav { padding: 8px 6px 10px; }
  #workflowView .breakdown-layout-binder .sb-foot { padding: 12px 16px 14px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head { gap: 10px; min-height: 62px; padding: 12px 16px 10px 18px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head h3 { font-size: 16px; }
  #workflowView .breakdown-layout-binder #breakdownDetail > .detail-head .sb-stats { display: none; }
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-tabs { padding: 0 16px 10px 18px; }
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-main { padding: 2px 16px 24px 18px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > .asset-subtabs { padding: 0 16px 12px 18px; }
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail[data-tab="assets"] > :is(.episode-asset-grid, .detail-cta) { padding: 0 16px 24px 18px; }
  #workflowView .breakdown-layout-binder .sb-brief { padding: 14px 16px 12px; }
  #workflowView .breakdown-layout-binder .sb-beats { gap: 16px; }
  #workflowView .breakdown-layout-binder .sb-tabs-info .sb-wide { display: none; }

  #workflowView .breakdown-layout-binder .sb-row { --thumb-h: 88px; --thumb-max: 176px; grid-template-columns: 24px auto minmax(0, 1fr) 204px; gap: 14px; padding: 10px 12px 10px 6px; }
  #workflowView .breakdown-layout-binder .sb-row-side { grid-column: 3; grid-row: 2; flex-direction: row; align-items: center; gap: 14px; margin-top: -4px; }
  #workflowView .breakdown-layout-binder .sb-row > .sb-frame, #workflowView .breakdown-layout-binder .sb-row > .sb-check { grid-row: 1 / 3; }
  #workflowView .breakdown-layout-binder .sb-row-actions { grid-column: 4; grid-row: 1 / 3; }
  #workflowView .breakdown-layout-binder .sb-row-summary { -webkit-line-clamp: 1; }
  #workflowView .breakdown-layout-binder .sb-refs { flex: none; max-width: 34%; }
  #workflowView .breakdown-layout-binder .sb-lines { flex: 1 1 auto; grid-template-columns: fit-content(6em) minmax(0, 1fr) max-content; }
  #workflowView .breakdown-layout-binder .sb-lines > :nth-child(n + 3) { display: none; }
  #workflowView .breakdown-layout-binder .sb-lines > .sb-lines-more { display: block; grid-column: 3; grid-row: 1; }
  #workflowView .breakdown-layout-binder .sb-row-detail { padding: 16px 16px 18px; }
}

/* 台式（≥1600）清单这一栏不到 960 宽时（1707 宽的屏，旁边是 540 的助手栏），一行五列放不下：出镜资产、台词那一栏被挤得只剩几个字（2026-10-09 实测）。
   按清单自己的宽度换成笔记本那档的两行排；不另起视口断点，2560 宽这一栏 1500 多、不触发，一个像素不变。
   清单里没有 fixed 定位的东西，当尺寸容器不影响别的 */
@media (min-width: 1600px) {
  /* 出镜资产一行固定四张：1707 宽中间这栏 740 上下，一张只剩 165 宽，名字那一行塞了编号、名字、改名笔、勾选、删除，名字只剩半个字（2026-10-09 实测）。
     每张至少 240 宽（「陆行舟（劲装）」这种长名字要露全），放不下四张就少排几张；放得下四张的宽屏每张还是四分之一宽，一个像素不变（列间距 12，见 .asset-grid） */
  #mainView #workflowView .breakdown-layout-binder #breakdownDetail > .episode-asset-grid {
    grid-template-columns: repeat(auto-fill, minmax(max(240px, calc((100% - 36px) / 4 - 1px)), 1fr)); }
  #workflowView .breakdown-layout-binder .sb-list { container: sb-list / inline-size; }
  @container sb-list (max-width: 960px) {
    #workflowView .breakdown-layout-binder .sb-row { --thumb-h: 88px; --thumb-max: 176px; grid-template-columns: 24px auto minmax(0, 1fr) 204px; gap: 14px; padding: 10px 12px 10px 6px; }
    #workflowView .breakdown-layout-binder .sb-row-side { grid-column: 3; grid-row: 2; flex-direction: row; align-items: center; gap: 14px; margin-top: -4px; }
    #workflowView .breakdown-layout-binder .sb-row > .sb-frame, #workflowView .breakdown-layout-binder .sb-row > .sb-check { grid-row: 1 / 3; }
    #workflowView .breakdown-layout-binder .sb-row-actions { grid-column: 4; grid-row: 1 / 3; }
    #workflowView .breakdown-layout-binder .sb-row-summary { -webkit-line-clamp: 1; }
    #workflowView .breakdown-layout-binder .sb-refs { flex: none; max-width: 34%; }
    #workflowView .breakdown-layout-binder .sb-lines { flex: 1 1 auto; grid-template-columns: fit-content(6em) minmax(0, 1fr) max-content; }
    #workflowView .breakdown-layout-binder .sb-lines > :nth-child(n + 3) { display: none; }
    #workflowView .breakdown-layout-binder .sb-lines > .sb-lines-more { display: block; grid-column: 3; grid-row: 1; }
  }
}
/* 清单特别窄（不到 560：1280 及以下开着助手栏）：右栏挪到内容下面横着排（下一步撑满 + 出片方式 + 详情），中间那一栏用回全宽。
   右栏为装下「首帧生成 | 直接生成」+「详情」放到 204 之后（2026-10-09），16:9 的缩略图在 1200 宽时中间只剩 0、卡片撑破 13 像素（改名之前也只剩 31）；
   1366 及以上照旧右边两行竖排。1200–1599 也把清单当尺寸容器（原来只在 ≥1600 那条里）：清单里没有 fixed 定位的东西，不影响别的 */
@media (min-width: 1200px) {
  #workflowView .breakdown-layout-binder .sb-list { container: sb-list / inline-size; }
  @container sb-list (max-width: 559px) {
    #workflowView .breakdown-layout-binder .sb-row { grid-template-columns: 24px auto minmax(0, 1fr); }
    #workflowView .breakdown-layout-binder .sb-row-actions { grid-column: 2 / -1; grid-row: 3; flex-direction: row; align-items: center; gap: 8px; }
    #mainView #workflowView .breakdown-layout-binder .sb-row-actions .sb-next, #workflowView .breakdown-layout-binder .sb-row-actions .sb-running { flex: 1 1 auto; width: auto; min-width: 0; }
    #workflowView .breakdown-layout-binder .sb-row-sub { flex: none; }
  }
}
/* 批量条两行：本集那一栏窄（1200–1439，开着助手栏）时一行放不下（一行要 570 上下，1200 宽只有 428、1366 也差 5，
   出片方式改叫四个字之前 1200、1280 就已经伸出去），上一行已选 + 出片方式、下一行删除 / 取消选择 / 生成提示词，中间那道分隔线不要。
   按屏宽分、不按这一栏的宽：#breakdownDetail 里有 position: fixed 的「分镜设置」下拉，设成尺寸容器会把它的位置带歪。1440 及以上照旧一行 */
@media (min-width: 1200px) and (max-width: 1439px) {
  #workflowView .breakdown-layout-binder #breakdownDetail > .sb-bulk { flex-direction: column; align-items: center; gap: 8px; padding: 8px 12px; }
  #workflowView .breakdown-layout-binder .sb-bulk-group { display: flex; align-items: center; gap: 6px; }
  #workflowView .breakdown-layout-binder .sb-bulk-split { display: none; }
}

/* ==== 分集列表 · ⋯ 菜单（用户 2026-10-09）====
   剧本页目录栏「剧本正文」那一列（按钮的位置在上面「目录栏」那块）、分镜页分集列表，每一行右边一颗「⋯」，点开竖排两项「新建分集 / 删除分集」
   （EpisodeMenu.tsx，挂在 body 上、按「⋯」的位置摆）。新建 = 插在这一集下面、后面的集号顺延，删除 = 后面的往前补。
   菜单里的横栏照剧本页目录栏那一身：36 高、圆角 8、13px、行距 6，悬浮 --surface-hover，删除那一项危险色；按下不缩（不是独立的操作按钮）。 */
.episode-menu { position: fixed; z-index: 90; display: grid; gap: 6px; width: 152px; padding: 6px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  box-shadow: var(--shadow-1); animation: episode-menu-in .14s cubic-bezier(.4, 0, .2, 1); }
@keyframes episode-menu-in { from { opacity: 0; translate: 0 -4px; } to { opacity: 1; translate: 0 0; } }
@media (prefers-reduced-motion: reduce) {
  .episode-menu { animation: none; }
}
.episode-menu .episode-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; height: 36px; min-height: 0; margin: 0; padding: 0 12px; border: 0; border-radius: 8px;
  background: transparent; color: var(--text); font: inherit; font-size: 13px; font-weight: 400; text-align: left; cursor: pointer; }
.episode-menu .episode-menu-item:hover:not(:disabled) { background: var(--surface-hover); }
.episode-menu .episode-menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.episode-menu .episode-menu-item.is-danger { color: var(--danger); }
.episode-menu .episode-menu-item.is-danger:hover:not(:disabled) { background: var(--btn-danger-bg); }
.episode-menu .episode-menu-item:disabled { cursor: not-allowed; opacity: .45; }
.episode-menu .episode-menu-item i { flex: none; width: 16px; height: 16px; background: currentColor; }
.episode-menu .episode-menu-item i.add {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") center / 14px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") center / 14px no-repeat; }
.episode-menu .episode-menu-item i.remove { -webkit-mask: url("/static/icon-trash.png") center / 14px no-repeat; mask: url("/static/icon-trash.png") center / 14px no-repeat; }

/* 分镜页分集卡片右上角的「⋯」：悬浮、键盘移到这一张、菜单开着时露出来（触屏上一直在）；集名给它让出一格，不会钻到它底下 */
#workflowView .episode-row .episode-item { padding-right: 38px; }
#workflowView .episode-row .episode-more { position: absolute; top: 8px; right: 8px; z-index: 1; display: none; place-items: center; width: 24px; height: 24px; min-height: 0; margin: 0;
  padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--muted); cursor: pointer; }
#workflowView .episode-row .episode-more:hover:not(:disabled), #workflowView .episode-row.menu-open .episode-more { background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text); }
#workflowView .episode-row .episode-more:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#workflowView .episode-row:is(:hover, :focus-within, .menu-open) .episode-more { display: grid; }
#mainView #workflowView .episode-row.menu-open .episode-item:not(.active) { background: var(--surface-hover); }
@media (hover: none) {
  #workflowView .episode-row .episode-more { display: grid; }
}

/* ==================== 图片生成页 · 画廊 + 详情弹窗（2026-10-09 改版，用户在本机演示站上选的「方案一＋」） ====================
   左边目录栏（照剧本创作页那一栏：设定图 角色 / 场景 / 道具 + 分镜首帧逐集，底部队列和出图进度）| 右边标题行 + 状态页签 + 画廊，
   勾了才在底部浮出批量条；点卡片弹出详情（#imageDetailDialog，挂在 body 上）。标记在 frontend/src/features/main/image/。
   只挂在新版的 #imageView.ig-view 上：回退用的旧页面还是原来那张任务表，那一套样式别删。
   工作台那条 #mainView button:not(…)×5 把按钮描边刷成透明（优先级 (1,5,1)）：页面里的规则一律带两个 id 才压得过；
   弹窗挂在 body 上，和页面共用的那几条写成 :is(页面, #imageDetailDialog#imageDetailDialog)，两边优先级一样。 */
:root { --ig-media-bg: #e8ebea; --ig-float-bg: #ffffff; --ig-pop-shadow: 0 16px 44px rgba(0, 0, 0, .18); --ig-warn: #b7791f; }
:root[data-theme="dark"] { --ig-media-bg: #181818; --ig-float-bg: #2c2c2c; --ig-pop-shadow: 0 16px 44px rgba(0, 0, 0, .45); --ig-warn: #f0bd72; }

@keyframes ig-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@keyframes ig-spin { to { transform: rotate(360deg); } }
@keyframes ig-shimmer { 0% { background-position: -160% 0; } 100% { background-position: 260% 0; } }
@keyframes ig-flash { 0%, 60% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 0 transparent; } }

/* ---------------- 骨架：目录栏 268（笔记本 216、平板 200、手机收起）| 内容 ---------------- */
#mainView #imageView.ig-view:not(.hidden) { display: grid; grid-template-columns: 268px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: 10px; min-height: 0; }

/* ---------------- 目录栏（和剧本创作那一栏同一身：分区行 40 高 / 普通行 36 高，行距 6，选中强调色浅底、选中的悬浮不变） ---------------- */
#mainView #imageView.ig-view .ig-binder { display: flex; flex-direction: column; min-width: 0; min-height: 0; border-radius: 14px; background: var(--surface); overflow: hidden; }
#mainView #imageView.ig-view .ig-binder-in { display: flex; flex: 1 1 auto; flex-direction: column; min-height: 0; }
#mainView #imageView.ig-view .ig-cover { flex: none; padding: 20px 20px 16px; border-bottom: 1px solid var(--line); }
#mainView #imageView.ig-view .ig-cover b { display: block; color: var(--text); font-size: 15px; font-weight: 700; line-height: 1.5; }
#mainView #imageView.ig-view .ig-cover span { display: block; margin-top: 8px; color: var(--muted); font-size: 12px; line-height: 1.6; }
#mainView #imageView.ig-view .ig-nav { display: flex; flex: 1 1 auto; flex-direction: column; gap: 6px; min-height: 0; padding: 10px 10px 12px; overflow-y: auto; }
#mainView #imageView.ig-view :is(.ig-nav, .ig-subs, .ig-eps) > * { flex: none; }
#mainView #imageView.ig-view :is(.ig-subs, .ig-eps) { display: flex; flex-direction: column; gap: 6px; }
#mainView #imageView.ig-view :where(.ig-binder) button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 0; margin: 0; border: 0; background: transparent; color: var(--text);
  font: inherit; text-align: left; cursor: pointer; }
#mainView #imageView.ig-view :where(.ig-binder) button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#mainView #imageView.ig-view :where(.ig-binder) button:disabled { cursor: not-allowed; opacity: .5; }
#mainView #imageView.ig-view .ig-item { height: 40px; padding: 0 12px; border-radius: 10px; font-size: 14px; font-weight: 650; }
#mainView #imageView.ig-view :is(.ig-item, .ig-sub) em { margin-left: auto; color: var(--muted); font-size: 12px; font-style: normal; font-weight: 400; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-sub { height: 36px; padding: 0 12px; border-radius: 8px; color: var(--muted); font-size: 13px; }
#mainView #imageView.ig-view .ig-sub:hover:not(.on), #mainView #imageView.ig-view .ig-sub.on { color: var(--text); }
#mainView #imageView.ig-view :is(.ig-item, .ig-sub, .ig-ep):not(.on):hover { background: var(--surface-hover); }
#mainView #imageView.ig-view :is(.ig-item, .ig-sub, .ig-ep).on { background: var(--accent-soft); }
#mainView #imageView.ig-view .ig-dot-ring { flex: none; width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid var(--line-strong); border-radius: 50%; }
#mainView #imageView.ig-view .ig-dot-ring.done { border-color: var(--success); background: var(--success) center / 11px no-repeat
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
#mainView #imageView.ig-view .ig-dot-ring.part { padding: 2px; border-color: var(--accent); background: conic-gradient(var(--accent) var(--done, 0%), transparent 0) content-box; }
#mainView #imageView.ig-view .ig-ep { height: 36px; padding: 0 10px 0 12px; border-radius: 8px; font-size: 13px; }
#mainView #imageView.ig-view .ig-ep b { flex: none; width: 18px; color: var(--muted); font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-ep span { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
#mainView #imageView.ig-view .ig-ep.blank span { color: var(--muted); }
#mainView #imageView.ig-view .ig-state { flex: none; width: 16px; height: 16px; margin-left: 2px;
  background: radial-gradient(circle, transparent 3px, var(--line-strong) 3.5px, var(--line-strong) 4.5px, transparent 5px); }
#mainView #imageView.ig-view .ig-state.done { background: var(--success);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat; }
#mainView #imageView.ig-view .ig-state.part { box-sizing: border-box; width: 14px; height: 14px; margin: 1px 1px 1px 3px; padding: 2.5px; border: 1.5px solid var(--accent); border-radius: 50%;
  background: conic-gradient(var(--accent) var(--done, 0%), transparent 0) content-box; }
#mainView #imageView.ig-view .ig-state.warn { background: radial-gradient(circle, var(--ig-warn) 3.5px, transparent 4px); }
#mainView #imageView.ig-view .ig-state.running { background: radial-gradient(circle, var(--accent) 3.5px, transparent 4px); animation: ig-pulse 1.2s ease-in-out infinite; }
#mainView #imageView.ig-view .ig-state.blank { background: radial-gradient(circle, transparent 3px, var(--line) 3.5px, var(--line) 4.5px, transparent 5px); opacity: .7; }
#mainView #imageView.ig-view .ig-foot { flex: none; display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 18px; border-top: 1px solid var(--line); }
#mainView #imageView.ig-view .ig-meter { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 12px; }
#mainView #imageView.ig-view .ig-meter span { color: var(--muted); }
#mainView #imageView.ig-view .ig-meter b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-meter i { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }
/* 生成队列：在跑的时候目录栏底部多一张小卡（原来是顶栏那枚徽标 + 进度条） */
#mainView #imageView.ig-view .ig-queue { display: flex; flex-direction: column; gap: 4px; margin: -4px -8px 2px; padding: 10px 10px 10px 12px; border-radius: 10px; background: var(--surface-soft); font-size: 12px; }
#mainView #imageView.ig-view .ig-queue p { display: flex; align-items: center; gap: 8px; min-width: 0; margin: 0; }
#mainView #imageView.ig-view .ig-queue b { color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap; }
#mainView #imageView.ig-view .ig-queue-stop { width: auto; height: 24px; margin-left: auto; padding: 0 8px; border: 1px solid var(--line-strong); border-radius: 7px; color: var(--text); font-size: 12px; }
#mainView #imageView.ig-view .ig-queue-stop:hover:not(:disabled) { background: var(--surface-hover); }
/* 带上 p：压过上面 .ig-queue p 的 flex（flex 容器上省略号不生效，名字原来是被硬裁掉的） */
#mainView #imageView.ig-view .ig-queue p.ig-queue-names { display: block; overflow: hidden; color: var(--muted); white-space: nowrap; text-overflow: ellipsis; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); animation: ig-pulse 1.2s ease-in-out infinite; }

/* ---------------- 内容区：标题行 + 状态页签（不跟着滚），下面画廊自己滚 ---------------- */
#mainView #imageView.ig-view .ig-main { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; border-radius: 14px; background: var(--surface);
  overflow: hidden; container: igmain / inline-size; }
#mainView #imageView.ig-view .ig-head { flex: none; padding: 0 20px 0 26px; }
#mainView #imageView.ig-view .ig-titlebar { display: flex; align-items: center; gap: 12px; min-width: 0; min-height: 68px; }
#mainView #imageView.ig-view .ig-scope-select { display: none; }
#mainView #imageView.ig-view .ig-title { display: flex; flex: none; align-items: baseline; gap: 8px; margin: 0; color: var(--text); font-size: 18px; font-weight: 600; white-space: nowrap; }
#mainView #imageView.ig-view .ig-ep-code { color: var(--muted); font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-stats { overflow: hidden; color: var(--muted); font-size: 13px; white-space: nowrap; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-pill { display: inline-flex; flex: none; align-items: center; height: 28px; min-height: 0; padding: 0 6px 0 12px; border: 0; border-radius: 999px;
  background: var(--surface-soft); color: var(--muted); font: inherit; font-size: 12px; font-weight: 500; white-space: nowrap; cursor: pointer; }
#mainView #imageView.ig-view .ig-pill:hover { color: var(--text); }
#mainView #imageView.ig-view .ig-pill .cost-chip { margin-left: 6px; padding: 0 6px 0 4px; background: var(--surface); font-size: 12px; line-height: 20px; }
#mainView #imageView.ig-view .ig-titlebar-tools { display: flex; flex: none; align-items: center; gap: 10px; margin-left: auto; }
#mainView #imageView.ig-view .ig-settings { display: inline-flex; align-items: center; gap: 8px; height: 38px; min-height: 0; padding: 0 12px 0 14px; border: 1px solid var(--line-strong);
  border-radius: 999px; background: transparent; color: var(--text); font: inherit; font-size: 13px; font-weight: 500; white-space: nowrap; cursor: pointer; }
#mainView #imageView.ig-view .ig-settings:hover, #mainView #imageView.ig-view .ig-settings[aria-expanded="true"] { background: var(--surface-hover); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ic { display: inline-flex; flex: none; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) :is(.ig-ic, .ig-chev) svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
#mainView #imageView.ig-view .ig-chev { display: inline-flex; color: var(--muted); }
#mainView #imageView.ig-view .ig-chev svg { width: 12px; height: 12px; }
/* 标题行主按钮「生成剩余 N 张」、批量条的「生成 N 张」：主按钮那一身（深色白底、浅色黑底），悬浮不变样 */
#mainView #imageView.ig-view .ig-primary { display: inline-flex; flex: none; align-items: center; gap: 8px; height: 38px; min-height: 0; padding: 0 8px 0 20px; border: 0; border-radius: 999px;
  background: var(--command-create-bg); color: var(--command-create-text); font: inherit; font-size: 14px; font-weight: 600; white-space: nowrap; cursor: pointer; --credits-icon-color: currentColor; }
#mainView #imageView.ig-view .ig-primary:not(:has(.cost-chip)) { padding-right: 20px; }
#mainView #imageView.ig-view .ig-primary .cost-chip { margin: 0; background: var(--command-create-text); color: var(--command-create-bg); }
#mainView #imageView.ig-view .ig-primary .jump-icon { margin-left: -2px; }
#mainView #imageView.ig-view .ig-primary:disabled { cursor: not-allowed; opacity: .45; }
#mainView #imageView.ig-view .ig-filterbar { display: flex; align-items: center; gap: 14px; min-height: 46px; padding-bottom: 10px; }
/* 状态页签是全站那份滑轨页签（2026-10-09 用户：「点击时没有滑动的动画」——原来是这里另写的一套，选中那颗自己铺底色、没有滑块）：
   槽色、滑块、选中字色、按下缩小全走「滑轨页签」共用块，这里只留排布——按字定宽、颗与颗挨近一点、一颗 30 高 */
#mainView #imageView.ig-view .ig-tabs { display: inline-flex; flex: none; gap: 4px; }
#mainView #imageView.ig-view .ig-tab { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 14px; font-size: 13px; white-space: nowrap; }
#mainView #imageView.ig-view .ig-tab small { font-size: 12px; font-weight: 500; opacity: .62; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-tab.active small { opacity: .85; }
#mainView #imageView.ig-view .ig-search { display: inline-flex; align-items: center; gap: 8px; width: 240px; height: 34px; margin-left: auto; padding: 0 12px; box-sizing: border-box;
  border-radius: 10px; background: var(--surface-soft); color: var(--muted); }
#mainView #imageView.ig-view .ig-search input { flex: 1 1 auto; min-width: 0; height: 100%; min-height: 0; padding: 0; border: 0; outline: 0; background: transparent; box-shadow: none;
  color: var(--text); font: inherit; font-size: 13px; }
#mainView #imageView.ig-view .ig-pickall { display: inline-flex; flex: none; align-items: center; gap: 8px; height: 34px; min-height: 0; padding: 0 10px; border: 0; border-radius: 10px;
  background: transparent; color: var(--text); font: inherit; font-size: 13px; cursor: pointer; }
#mainView #imageView.ig-view .ig-pickall:hover:not(:disabled) { background: var(--surface-hover); }
#mainView #imageView.ig-view .ig-pickall:disabled { color: var(--muted); cursor: not-allowed; }
#mainView #imageView.ig-view :is(.ig-pickall, .ig-check) span { position: relative; display: block; box-sizing: border-box; width: 16px; height: 16px; border: 1.5px solid var(--line-strong);
  border-radius: 4px; background: var(--surface); }
#mainView #imageView.ig-view :is(.ig-pickall.on, .ig-pickall.half, .ig-check.on) span { border-color: var(--accent); background: var(--accent); }
#mainView #imageView.ig-view :is(.ig-pickall.on, .ig-check.on) span::after { content: ""; position: absolute; inset: 0; background: center / 11px no-repeat
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='white' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
#mainView #imageView.ig-view .ig-pickall.half span::after { content: ""; position: absolute; left: 3px; right: 3px; top: 6px; height: 1.5px; border-radius: 1px; background: #fff; }
#mainView #imageView.ig-view .ig-body { flex: 1 1 auto; min-height: 0; padding: 4px 26px 28px; overflow-y: auto; scroll-padding-top: 12px; }
#mainView #imageView.ig-view .ig-main:has(.ig-bulk) .ig-body { padding-bottom: 96px; }
#mainView #imageView.ig-view .ig-empty-line { margin: 30px 0; color: var(--muted); font-size: 13px; text-align: center; }
/* 没拆分镜的那一集：一句话 + 去分镜生成 */
#mainView #imageView.ig-view .ig-empty { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 80px 0; color: var(--muted); font-size: 14px; text-align: center; }
#mainView #imageView.ig-view .ig-empty p { margin: 0; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-muted { margin: 0; color: var(--muted); font-size: 12px; }

/* ---------------- 通用小件：小菜单键、名字、头像、勾选 ---------------- */
#mainView #imageView.ig-view .ig-more { display: inline-grid; flex: none; place-items: center; width: 26px; height: 26px; min-height: 0; padding: 0; border: 0; border-radius: 7px;
  background: transparent; color: var(--muted); cursor: pointer; }
#mainView #imageView.ig-view .ig-more:hover, #mainView #imageView.ig-view .ig-more[aria-expanded="true"] { background: var(--surface-hover); color: var(--text); }
#mainView #imageView.ig-view .ig-more svg { width: 16px; height: 16px; fill: currentColor; }
#mainView #imageView.ig-view .ig-link { min-height: 0; padding: 0; border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer; }
#mainView #imageView.ig-view .ig-link:hover { color: var(--text); }
#mainView #imageView.ig-view .ig-code { color: var(--muted); font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-name { overflow: hidden; border-radius: 4px; color: var(--text); font-size: 14px; font-weight: 600; white-space: nowrap; text-overflow: ellipsis; cursor: pointer; }
#mainView #imageView.ig-view .ig-name:hover { text-decoration: underline; text-underline-offset: 3px; }
#mainView #imageView.ig-view .ig-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#mainView #imageView.ig-view .ig-sec { flex: none; margin-left: auto; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 头像：参考图（点开还是原来那个菜单）。没出图的写首字、虚线圈；取消选中的淡下去；资产卡删了的打横杠 */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-avs { display: inline-flex; flex: none; align-items: center; min-width: 0; }
#mainView #imageView.ig-view .ig-avs.none { color: var(--muted); font-size: 12px; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av { position: relative; display: inline-grid; flex: none; place-items: center; width: 24px; height: 24px; min-height: 0; margin: 0 0 0 -6px;
  padding: 0; overflow: hidden; border: 2px solid var(--surface-soft); border-radius: 50%; background: var(--tab-rail-strong); color: var(--muted); font: inherit; font-size: 11px;
  font-weight: 600; cursor: pointer; box-sizing: content-box; }
#mainView #imageView.ig-view .ig-avs .ig-av:first-child { margin-left: 0; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av img { width: 100%; height: 100%; object-fit: cover; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av:not(.ready) { background: var(--surface); box-shadow: inset 0 0 0 1.5px var(--line-strong); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av:is(.waiting, .missing, .ambiguous) { box-shadow: inset 0 0 0 1.5px var(--ig-warn); color: var(--ig-warn); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av.dropped { opacity: .45; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av.removed span { text-decoration: line-through; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av.removed { box-shadow: inset 0 0 0 1.5px var(--danger); color: var(--danger); }
#mainView #imageView.ig-view .ig-av[data-noop] { cursor: default; }
#mainView #imageView.ig-view .ig-av:hover:not([data-noop]) { z-index: 1; transform: translateY(-1px); }
#mainView #imageView.ig-view .ig-av-more { margin-left: 4px; color: var(--muted); font-size: 11px; font-weight: 600; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-av.lg { width: 40px; height: 40px; margin: 0; border-radius: 9px; border-width: 0; font-size: 14px; cursor: default; }
/* 勾选：卡片左上角一颗小方框（鼠标移上去、勾了、已经在勾的时候才露出来） */
#mainView #imageView.ig-view .ig-check { position: absolute; z-index: 2; top: 8px; left: 8px; display: grid; place-items: center; width: 26px; height: 26px; margin: 0; border-radius: 8px;
  background: rgba(0, 0, 0, .45); cursor: pointer; opacity: 0; transition: opacity .15s; }
#mainView #imageView.ig-view .ig-check input { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0; margin: 0; opacity: 0; cursor: pointer; }
#mainView #imageView.ig-view .ig-check span { width: 16px; height: 16px; border-color: rgba(255, 255, 255, .85); background: transparent; }
#mainView #imageView.ig-view .ig-check.on span { border-color: var(--accent); background: var(--accent); }
#mainView #imageView.ig-view .ig-check.off { display: none; }
#mainView #imageView.ig-view :is(.ig-card:hover, .ig-card:focus-within) .ig-check, #mainView #imageView.ig-view .ig-check.on, #mainView #imageView.ig-view .ig-main:has(.ig-bulk) .ig-check { opacity: 1; }

/* ---------------- 画面（宽高比由外面那层给，一律不裁不拉伸） ---------------- */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) :is(.ig-media, .ig-visual-media) { position: relative; overflow: hidden; background: var(--ig-media-bg); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pic { position: absolute; inset: 0; display: block; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pic-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
/* 比例对不上的老图：完整显示，两边垫同一张图的模糊底 */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pic-bg { position: absolute; inset: -12%; width: 124%; height: 124%; object-fit: cover; filter: blur(16px) brightness(.5) saturate(1.1); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pic.soft .ig-pic-img { opacity: .5; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 14px;
  box-sizing: border-box; color: var(--muted); text-align: center; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph b { color: var(--text); font-size: 13px; font-weight: 600; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph small { display: -webkit-box; max-width: 100%; overflow: hidden; font-size: 12px; line-height: 1.5; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph-ic svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* 待生成：空的分镜格（三分线）+ 景别 */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.grid { background-image:
    linear-gradient(90deg, transparent calc(33.33% - .5px), var(--line) calc(33.33% - .5px), var(--line) calc(33.33% + .5px), transparent calc(33.33% + .5px),
      transparent calc(66.66% - .5px), var(--line) calc(66.66% - .5px), var(--line) calc(66.66% + .5px), transparent calc(66.66% + .5px)),
    linear-gradient(180deg, transparent calc(33.33% - .5px), var(--line) calc(33.33% - .5px), var(--line) calc(33.33% + .5px), transparent calc(33.33% + .5px),
      transparent calc(66.66% - .5px), var(--line) calc(66.66% - .5px), var(--line) calc(66.66% + .5px), transparent calc(66.66% + .5px)); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.dashed { margin: 10px; inset: 0; border: 1.5px dashed var(--line-strong); border-radius: 10px; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.bad :is(b, .ig-ph-ic) { color: var(--danger); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.warn :is(b, .ig-ph-ic) { color: var(--ig-warn); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.queued .ig-ph-ic { color: var(--accent); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph:is(.running, .busy) { background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 80%) 0 0 / 60% 100% no-repeat;
  animation: ig-shimmer 1.8s linear infinite; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.running small { color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.running small em { font-style: normal; }
/* 直出：不出首帧，画面框里只写「直出」（用户 2026-10-09：「选择直出，就不要显示具体的图片了，就在图片框中显示直出文本即可」） */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-ph.direct b { font-size: 15px; letter-spacing: .08em; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-spin { width: 22px; height: 22px; box-sizing: border-box; border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent);
  border-radius: 50%; animation: ig-spin .9s linear infinite; }
/* 画面上的小标：左下角状态（生成中带耗时 / 排队第几位 / 提示词已更新 / 比例不一致），右下角第几版 */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-tags { position: absolute; z-index: 1; left: 8px; bottom: 8px; display: flex; flex-wrap: wrap; gap: 6px; max-width: calc(100% - 16px); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-tags.right { left: auto; right: 8px; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-tag { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; border-radius: 6px; background: rgba(0, 0, 0, .64); color: #fff;
  font-size: 12px; font-weight: 500; white-space: nowrap; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-tag.warn { color: #ffd38f; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-tag em { font-style: normal; font-variant-numeric: tabular-nums; }
/* 首帧 | 直出：卡片画面右上角一条深色小滑轨（压在图上，深浅主题都是深底白字）；鼠标移上去、或者这一镜是直出才露出来 */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-mode { display: inline-flex; flex: none; gap: 2px; padding: 2px; border-radius: 8px; background: rgba(0, 0, 0, .58);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-mode button { height: 22px; min-height: 0; padding: 0 8px; border: 0; border-radius: 6px; background: transparent;
  color: rgba(255, 255, 255, .78); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-mode button:hover:not(.on) { color: #fff; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-mode button.on { background: #fff; color: #161616; }
#mainView #imageView.ig-view .ig-mode.on-media { position: absolute; z-index: 2; top: 8px; right: 8px; opacity: 0; transition: opacity .15s ease; }
#mainView #imageView.ig-view .ig-card:is(:hover, :focus-within, [data-state="direct-new"], [data-state="direct-old"]) .ig-mode.on-media { opacity: 1; }
/* 宽的那一版（弹窗左栏画面卡）：放在面板底色上，跟主题走 */
#imageDetailDialog#imageDetailDialog .ig-mode.wide { padding: 3px; border-radius: 999px; background: var(--tab-rail-strong); -webkit-backdrop-filter: none; backdrop-filter: none; }
#imageDetailDialog#imageDetailDialog .ig-mode.wide button { height: 28px; padding: 0 16px; border-radius: 999px; color: var(--text); font-size: 13px; }
#imageDetailDialog#imageDetailDialog .ig-mode.wide button.on { background: var(--primary-bg); color: var(--on-primary); }

/* ---------------- 画廊：一条一张卡，画面按画幅铺满；一排几张跟着画幅走（大屏 9:16 一排 9 张、16:9 4 张，设定图 4 张） ---------------- */
#mainView #imageView.ig-view .ig-grid { --cols: 4; display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 18px 14px; align-items: start; }
#mainView #imageView.ig-view .ig-main[data-ar="9x16"] .ig-grid[data-kind="storyboard"] { --cols: 9; }
#mainView #imageView.ig-view .ig-main[data-ar="2x3"] .ig-grid[data-kind="storyboard"] { --cols: 8; }
#mainView #imageView.ig-view .ig-main[data-ar="3x4"] .ig-grid[data-kind="storyboard"] { --cols: 7; }
#mainView #imageView.ig-view .ig-main[data-ar="1x1"] .ig-grid[data-kind="storyboard"] { --cols: 6; }
#mainView #imageView.ig-view .ig-main:is([data-ar="4x3"], [data-ar="3x2"]) .ig-grid[data-kind="storyboard"] { --cols: 5; }
#mainView #imageView.ig-view .ig-main[data-ar="16x9"] .ig-grid[data-kind="storyboard"] { --cols: 4; }
#mainView #imageView.ig-view .ig-main[data-ar="21x9"] .ig-grid[data-kind="storyboard"] { --cols: 3; }
#mainView #imageView.ig-view .ig-cell { min-width: 0; }
#mainView #imageView.ig-view .ig-card { position: relative; display: flex; flex-direction: column; min-width: 0; border-radius: 12px; background: var(--surface-soft); overflow: hidden;
  container: igcard / inline-size; }
#mainView #imageView.ig-view .ig-card.picked { box-shadow: 0 0 0 2px var(--accent); }
#mainView #imageView.ig-view .ig-card.flash { animation: ig-flash 1.8s ease-out; }
#mainView #imageView.ig-view .ig-media { display: block; width: 100%; border-radius: 12px 12px 0 0; cursor: pointer; }
#mainView #imageView.ig-view .ig-media:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#mainView #imageView.ig-view .ig-card-foot { display: flex; flex-direction: column; gap: 6px; padding: 10px 10px 10px 12px; }
#mainView #imageView.ig-view .ig-card-line { display: flex; align-items: center; gap: 6px; min-width: 0; min-height: 26px; }
#mainView #imageView.ig-view .ig-card-line .ig-sec { margin-left: auto; }
#mainView #imageView.ig-view .ig-card-line > .ig-more { margin-left: auto; }
#mainView #imageView.ig-view .ig-card-line > .ig-sec + .ig-more { margin-left: 0; }
#mainView #imageView.ig-view .ig-card-sum { margin: -4px 0 0; overflow: hidden; color: var(--muted); font-size: 12px; line-height: 1.5; white-space: nowrap; text-overflow: ellipsis; }
#mainView #imageView.ig-view .ig-card-line.act { min-height: 28px; }
#mainView #imageView.ig-view .ig-card-tools { display: inline-flex; flex: none; align-items: center; gap: 4px; margin-left: auto; }
#mainView #imageView.ig-view .ig-card .ig-av { border-color: var(--surface-soft); }
@container igcard (max-width: 205px) {
  #mainView #imageView.ig-view .ig-avs > .ig-av:nth-child(n+3), #mainView #imageView.ig-view .ig-avs > .ig-av-more { display: none; }
}
/* 分组（全部集 / 全部设定图）：组头带进度和「生成本集剩余」 */
#mainView #imageView.ig-view .ig-group { display: flex; align-items: center; gap: 12px; margin: 26px 0 12px; }
#mainView #imageView.ig-view .ig-body > .ig-group-block:first-of-type > .ig-group { margin-top: 6px; }
#mainView #imageView.ig-view .ig-group-name { display: inline-flex; align-items: baseline; gap: 8px; white-space: nowrap; }
#mainView #imageView.ig-view .ig-group-name b { color: var(--text); font-size: 15px; font-weight: 700; }
#mainView #imageView.ig-view .ig-group-name span { color: var(--text); font-size: 15px; font-weight: 600; }
#mainView #imageView.ig-view .ig-group em { color: var(--muted); font-size: 12px; font-style: normal; font-variant-numeric: tabular-nums; white-space: nowrap; }
#mainView #imageView.ig-view .ig-group i { width: 120px; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }
#mainView #imageView.ig-view .ig-group .ig-pillbtn { margin-left: auto; }
#mainView #imageView.ig-view .ig-group .ig-pillbtn + .ig-link { margin-left: 4px; }
#mainView #imageView.ig-view .ig-group > .ig-link:not(.ig-pillbtn + .ig-link) { margin-left: auto; }
/* 本集出镜设定图（看某一集时）：这一集要用的设定图出没出，没出的就地生成。
   标题行写进度和要做的那一步；下面角色、场景、道具每类单独起一行（类别名在左边一列，用户 2026-10-09：「标签每个单独一行，我又没说不准内容换行」），
   类里面排满了往下换行；收着时每类最多两行高（一行 40 + 行距 8），多出来的往下淡出、三类一直都看得见，底下「展开全部 N 张」。
   **别改回一行横滚**：原来设定图那一段横着滚、滚动条藏着，右边的提示和按钮固定占将近 500 宽，一多后面的就被挡住、鼠标也滚不到（同一天用户：「这里需要重新设计」） */
#mainView #imageView.ig-view .ig-cast { margin: 0 0 18px; padding: 10px 12px 12px 16px; border-radius: 12px; background: var(--surface-soft); }
#mainView #imageView.ig-view .ig-cast-head { display: flex; align-items: center; gap: 14px; min-width: 0; min-height: 34px; }
#mainView #imageView.ig-view .ig-cast h4 { display: inline-flex; flex: none; align-items: baseline; gap: 6px; margin: 0; color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap; }
#mainView #imageView.ig-view .ig-cast h4 small { color: var(--muted); font-size: 12px; font-weight: 400; }
#mainView #imageView.ig-view .ig-cast-progress { display: inline-flex; flex: none; align-items: center; gap: 8px; }
#mainView #imageView.ig-view .ig-cast-progress em { color: var(--muted); font-size: 12px; font-style: normal; font-variant-numeric: tabular-nums; white-space: nowrap; }
#mainView #imageView.ig-view .ig-cast-progress i { width: 72px; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }
#mainView #imageView.ig-view .ig-cast-tail { display: flex; flex: none; align-items: center; gap: 10px; margin-left: auto; }
#mainView #imageView.ig-view .ig-cast-tip { color: var(--ig-warn); font-size: 12px; white-space: nowrap; }
#mainView #imageView.ig-view .ig-cast-tip.ok { color: var(--muted); }
#mainView #imageView.ig-view .ig-cast-groups { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 14px; margin-top: 10px; }
#mainView #imageView.ig-view .ig-cast-group { display: contents; }
#mainView #imageView.ig-view .ig-cast-kind { display: inline-flex; align-items: center; gap: 5px; height: 40px; color: var(--muted); font-size: 12px; font-weight: 600; white-space: nowrap; }
#mainView #imageView.ig-view .ig-cast-kind small { font-size: 11.5px; font-weight: 400; font-variant-numeric: tabular-nums; }
#mainView #imageView.ig-view .ig-cast-chips { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
#mainView #imageView.ig-view .ig-cast:not(.open) .ig-cast-chips { max-height: 88px; overflow: hidden; }
#mainView #imageView.ig-view .ig-cast-chips.clipped { -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 24px), transparent); mask-image: linear-gradient(180deg, #000 calc(100% - 24px), transparent); }
#mainView #imageView.ig-view .ig-cast-chip { display: inline-flex; flex: none; align-items: center; gap: 8px; max-width: 100%; height: 40px; min-height: 0; padding: 4px 10px 4px 4px; box-sizing: border-box;
  border: 0; border-radius: 9px; background: var(--surface); color: var(--text); font: inherit; font-size: 12.5px; cursor: pointer; }
#mainView #imageView.ig-view .ig-cast-chip:hover { background: var(--surface-hover); }
#mainView #imageView.ig-view .ig-cast-pic { display: grid; flex: none; place-items: center; width: 56px; height: 32px; overflow: hidden; border-radius: 6px; background: var(--ig-media-bg); color: var(--muted); }
#mainView #imageView.ig-view .ig-cast-pic img { width: 100%; height: 100%; object-fit: cover; }
#mainView #imageView.ig-view .ig-cast-pic .ig-ic svg { width: 15px; height: 15px; }
#mainView #imageView.ig-view .ig-cast-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
#mainView #imageView.ig-view .ig-cast-chip em { flex: none; color: var(--ig-warn); font-size: 11.5px; font-style: normal; font-weight: 600; }
#mainView #imageView.ig-view .ig-cast-chip em:is(.running, .queued) { color: var(--accent-text); }
#mainView #imageView.ig-view .ig-cast-more { display: flex; align-items: center; gap: 4px; height: 28px; min-height: 0; margin: 6px auto 0; padding: 0 8px 0 10px; border: 0; border-radius: 8px;
  background: transparent; color: var(--muted); font: inherit; font-size: 12.5px; white-space: nowrap; cursor: pointer; }
#mainView #imageView.ig-view .ig-cast-more:hover { background: var(--surface-hover); color: var(--text); }
#mainView #imageView.ig-view .ig-cast-more[aria-expanded="true"] .ig-chev { rotate: 180deg; }
/* 内容区窄的时候（平板、手机、笔记本开着目录栏）：标题那一行折成两行，类别名挪到设定图上面 */
@container igmain (max-width: 900px) {
  #mainView #imageView.ig-view .ig-cast-head { flex-wrap: wrap; row-gap: 8px; }
  #mainView #imageView.ig-view .ig-cast-tail { flex-wrap: wrap; margin-left: 0; }
  #mainView #imageView.ig-view .ig-cast-tip { white-space: normal; }
  #mainView #imageView.ig-view .ig-cast-groups { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  #mainView #imageView.ig-view .ig-cast-kind { height: 22px; }
  #mainView #imageView.ig-view .ig-cast-group:not(:first-child) .ig-cast-kind { margin-top: 6px; }
}

/* ---------------- 批量条：勾了才浮出来，压在内容区底部正中 ---------------- */
#mainView #imageView.ig-view .ig-bulk-host { position: absolute; z-index: 5; left: 0; right: 0; bottom: 18px; display: flex; justify-content: center; pointer-events: none; }
#mainView #imageView.ig-view .ig-bulk { display: flex; align-items: center; gap: 8px; padding: 8px 8px 8px 18px; border: 1px solid var(--line-strong); border-radius: 14px;
  background: var(--ig-float-bg); box-shadow: var(--ig-pop-shadow); pointer-events: auto; white-space: nowrap; }
#mainView #imageView.ig-view .ig-bulk b { margin-right: 4px; color: var(--text); font-size: 13px; font-weight: 600; }
#mainView #imageView.ig-view .ig-bulk-sep { width: 1px; height: 18px; margin: 0 4px; background: var(--line-strong); }
#mainView #imageView.ig-view .ig-bulk-label { color: var(--muted); font-size: 12px; }
#mainView #imageView.ig-view .ig-primary.sm { height: 34px; margin-left: 6px; padding: 0 6px 0 16px; font-size: 13px; }

/* ---------------- 按钮：一条一颗主操作（和分镜页「下一步」那一颗同一身）、文字按钮 ---------------- */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn { display: inline-flex; flex: none; align-items: center; justify-content: center; gap: 6px; height: 32px; min-height: 0; padding: 0 14px;
  border: 1px solid var(--btn-outline-border, var(--line-strong)); border-radius: 999px; background: var(--btn-outline-bg, transparent); color: var(--text); font: inherit; font-size: 13px;
  font-weight: 400; white-space: nowrap; cursor: pointer; transition: background-color .15s, translate .15s, scale .15s; --credits-icon-color: currentColor; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn:hover:not(:disabled) { background: var(--btn-outline-hover, var(--surface-hover)); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn:active:not(:disabled) { translate: 0 1px; scale: .98; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn .jump-icon { width: 12px; height: 12px; margin: 0; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn .cost-chip { margin: 0 -6px 0 2px; padding: 0 6px 0 4px; background: var(--surface-soft); font-size: 12px; line-height: 20px; }
/* 要扣积分的（生成、重试、重新生成、生成提示词）用主按钮那一身：深色白底黑字、浅色黑底白字；悬浮不变样（用户 2026-10-09：主题色的独立按钮不要悬浮样式） */
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn.gen { border-color: transparent; background: var(--command-create-bg); color: var(--command-create-text); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn.gen:hover:not(:disabled) { background: var(--command-create-bg); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn.gen .cost-chip { background: var(--command-create-text); color: var(--command-create-bg); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn:is(:disabled, [aria-disabled="true"]) { cursor: not-allowed; opacity: .45; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn.sm { height: 28px; padding: 0 11px; font-size: 12px; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-pillbtn.sm .cost-chip { margin-right: -5px; font-size: 11px; line-height: 18px; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-text-btn { display: inline-flex; align-items: center; gap: 4px; height: 28px; min-height: 0; padding: 0 10px; border: 0; border-radius: 8px;
  background: transparent; color: var(--muted); font: inherit; font-size: 13px; white-space: nowrap; cursor: pointer; }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-text-btn:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
:is(#mainView #imageView.ig-view, #imageDetailDialog#imageDetailDialog) .ig-text-btn:disabled { cursor: not-allowed; opacity: .5; }

/* ---------------- 弹层：生成设置、卡片「⋯」（挂在 body 上，fixed 定位） ---------------- */
.ig-pop { position: fixed; z-index: 76; }
.ig-pop[hidden] { display: none; }
.ig-settings-pop { display: flex; flex-direction: column; gap: 10px; width: 340px; padding: 14px 16px 12px; box-sizing: border-box; border: 1px solid var(--line-strong);
  border-radius: 14px; background: var(--ig-float-bg); box-shadow: var(--ig-pop-shadow); color: var(--text); font-size: 13px; }
.ig-settings-pop h4 { margin: 0 0 2px; font-size: 14px; font-weight: 600; }
.ig-settings-pop .ig-field { display: grid; grid-template-columns: 64px minmax(0, 1fr) auto; align-items: center; gap: 10px; margin: 0; }
.ig-settings-pop .ig-field[hidden] { display: none; }
.ig-settings-pop .ig-field > span { color: var(--muted); }
.ig-settings-pop .ig-field select { width: 100%; height: 34px; min-height: 0; padding: 0 10px; border: 1px solid var(--line-strong); border-radius: 9px; background: var(--surface-soft);
  color: var(--text); font: inherit; font-size: 13px; }
.ig-settings-pop .ig-field small { color: var(--muted); font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ig-settings-pop .ig-field-tip { margin: -4px 0 0 74px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.ig-settings-pop .ig-field-tip.warn { color: var(--ig-warn); }
.ig-settings-pop .ig-pop-foot { margin: 4px 0 0; padding-top: 10px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }
.ig-settings-pop .ig-pop-foot b { color: var(--text); }
.ig-more-pop.ratio-popover { min-width: 156px; padding: 6px; }
.ig-more-pop .ratio-option { grid-template-columns: 1fr; padding: 8px 12px; font-size: 13.5px; }
.ig-more-pop .ratio-option-label { display: inline-flex; align-items: center; gap: 6px; }

/* ---------------- 详情弹窗：大小固定（翻上一镜 / 下一镜时不跳）；左栏画面铺满，右栏提示词、参考图、镜头信息自己滚 ----------------
   高度按画幅给：竖幅 1100、横幅 1000（右栏提示词框定高、下面的文字沉到底，横幅再矮就放不下）；同一集画幅一样，翻页时照样不变 */
/* 竖排写在不带 [open] 的那一条上：只在 [open] 里写的话，一关弹窗 [open] 先摘掉、立刻回成横排，收起那段动画里版式跳一下 */
dialog.new-work-dialog.ig-dialog { flex-direction: column; width: min(1280px, calc(100vw - 48px)); height: min(calc(100vh - 48px), clamp(1000px, calc(1500px - 400px * var(--r, .5625)), 1100px));
  max-height: none; overflow: hidden; }
dialog.new-work-dialog.ig-dialog[open] { display: flex; }
#imageDetailDialog#imageDetailDialog:focus { outline: none; }
#imageDetailDialog#imageDetailDialog > .dialog-header { flex: none; align-items: center; gap: 14px; box-sizing: border-box; min-height: 56px; margin: 0; padding: 12px 58px 12px 22px; }
#imageDetailDialog#imageDetailDialog .ig-dlg-title { display: flex; align-items: baseline; gap: 12px; min-width: 0; margin-right: auto; }
#imageDetailDialog#imageDetailDialog .ig-dlg-title h2 { display: flex; align-items: baseline; gap: 10px; margin: 0; overflow: hidden; color: var(--text); font-size: 17px; font-weight: 600; white-space: nowrap;
  text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-dlg-title h2 span { color: var(--muted); font-size: 14px; font-weight: 500; }
#imageDetailDialog#imageDetailDialog .ig-dlg-meta { overflow: hidden; color: var(--muted); font-size: 12.5px; white-space: nowrap; text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-dlg-nav { display: inline-flex; flex: none; align-items: center; gap: 2px; }
#imageDetailDialog#imageDetailDialog .ig-dlg-count { min-width: 52px; color: var(--muted); font-size: 12.5px; text-align: center; font-variant-numeric: tabular-nums; }
/* 全站弹窗的关闭键钉在右上角（top 8 / right 8）：这个弹窗的标题行高一些，挪到和标题同一条中线上 */
#imageDetailDialog#imageDetailDialog .ig-dlg-close { top: 14px; right: 14px; }
#imageDetailDialog#imageDetailDialog .ig-dlg-body { display: grid; flex: 1 1 auto; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: 24px; min-height: 0;
  padding: 18px 22px 20px; overflow: hidden; container-type: size; }
/* 左栏宽度按画幅算：竖幅按高铺满后有多宽就多宽（最窄 400），横幅最多占六成 */
#imageDetailDialog#imageDetailDialog .ig-visual { display: flex; flex-direction: column; gap: 12px; min-width: 0; padding: 14px 16px 16px; border-radius: 14px; }
#imageDetailDialog#imageDetailDialog .ig-dlg-visual { box-sizing: border-box; width: clamp(400px, calc((100cqh - 196px) * var(--r, .5625) + 32px), 60cqw); height: 100%; align-self: stretch; overflow: hidden;
  background: var(--surface-soft); }
#imageDetailDialog#imageDetailDialog .ig-visual-head { display: flex; align-items: center; gap: 10px; min-height: 30px; }
#imageDetailDialog#imageDetailDialog .ig-visual-head b { color: var(--text); font-size: 14px; font-weight: 700; }
#imageDetailDialog#imageDetailDialog .ig-visual-state { color: var(--muted); font-size: 12px; }
#imageDetailDialog#imageDetailDialog .ig-visual-head .ig-mode { margin-left: auto; }
#imageDetailDialog#imageDetailDialog .ig-dlg-stage { display: grid; flex: 1 1 auto; place-items: center; min-height: 0; container-type: size; border-radius: 10px; background: var(--ig-media-bg); }
#imageDetailDialog#imageDetailDialog .ig-dlg-stage > .ig-visual-media { width: min(100cqw, calc(100cqh * var(--r, .5625))); height: auto; border-radius: 10px; }
#imageDetailDialog#imageDetailDialog .ig-visual-media[role="button"] { cursor: zoom-in; }
#imageDetailDialog#imageDetailDialog .ig-visual-media .ig-ph b { font-size: 14px; }
#imageDetailDialog#imageDetailDialog .ig-visual-media .ig-ph.direct b { font-size: 18px; }
#imageDetailDialog#imageDetailDialog .ig-visual-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
#imageDetailDialog#imageDetailDialog .ig-visual-actions .ig-pillbtn { margin-right: 6px; }
#imageDetailDialog#imageDetailDialog .ig-versions { display: flex; flex: none; align-items: center; gap: 8px; overflow-x: auto; }
#imageDetailDialog#imageDetailDialog .ig-versions .ig-muted { flex: none; margin-right: 2px; }
#imageDetailDialog#imageDetailDialog .ig-ver { position: relative; flex: none; height: 54px; min-height: 0; padding: 0; overflow: hidden; border: 2px solid transparent; border-radius: 8px;
  background: var(--ig-media-bg); cursor: pointer; }
#imageDetailDialog#imageDetailDialog .ig-ver img { width: 100%; height: 100%; object-fit: contain; }
#imageDetailDialog#imageDetailDialog .ig-ver b { position: absolute; left: 3px; bottom: 3px; padding: 0 5px; border-radius: 4px; background: rgba(0, 0, 0, .66); color: #fff; font-size: 11px; line-height: 16px; }
#imageDetailDialog#imageDetailDialog .ig-ver.on { border-color: var(--accent); }
#imageDetailDialog#imageDetailDialog .ig-info { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
#imageDetailDialog#imageDetailDialog .ig-dlg-info { box-sizing: border-box; height: 100%; padding: 2px 6px 0 0; overflow-y: auto; }
/* 下面那几段文字沉到底：最后一行的底边就是这一栏的底边、也就是左边画面那一栏的底边；放不下时 margin 归零，照常往下排、这一栏自己滚 */
#imageDetailDialog#imageDetailDialog .ig-dlg-lower { display: flex; flex-direction: column; gap: 14px; margin-top: auto; }
#imageDetailDialog#imageDetailDialog .ig-side-error { margin: 0; padding: 8px 12px; border-radius: 10px; background: color-mix(in srgb, var(--ig-warn) 12%, transparent); color: var(--ig-warn);
  font-size: 12.5px; line-height: 1.6; }
#imageDetailDialog#imageDetailDialog .ig-sec-block { display: flex; flex-direction: column; gap: 10px; }
#imageDetailDialog#imageDetailDialog .ig-sec-block h4 { display: flex; align-items: baseline; gap: 6px; margin: 0; color: var(--text); font-size: 13px; font-weight: 600; }
#imageDetailDialog#imageDetailDialog .ig-sec-block h4 small { color: var(--muted); font-size: 12px; font-weight: 400; }
#imageDetailDialog#imageDetailDialog .ig-refs { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 8px; margin: 0; padding: 0; list-style: none; }
#imageDetailDialog#imageDetailDialog .ig-refs li { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 6px 6px 6px 6px; border-radius: 10px; background: var(--surface-soft); }
#imageDetailDialog#imageDetailDialog .ig-ref-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
#imageDetailDialog#imageDetailDialog .ig-ref-text b { overflow: hidden; color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap; text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-ref-text small { overflow: hidden; color: var(--muted); font-size: 12px; white-space: nowrap; text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-ref-text small.warn { color: var(--ig-warn); }
#imageDetailDialog#imageDetailDialog .ig-ref-text small.bad { color: var(--danger); }
#imageDetailDialog#imageDetailDialog .ig-ref-acts { display: inline-flex; flex: none; gap: 2px; margin-left: auto; }
#imageDetailDialog#imageDetailDialog .ig-ref-acts .ig-text-btn { height: 26px; padding: 0 8px; font-size: 12px; }
/* 提示词框一直摆着（没生成提示词也不藏，空着、灰着）、定高、不能拖（用户 2026-10-09）；字多了框里自己滚 */
#imageDetailDialog#imageDetailDialog .ig-prompt { flex: none; width: 100%; height: 260px; min-height: 0; padding: 12px 14px; box-sizing: border-box; resize: none; border: 1px solid var(--line);
  border-radius: 10px; background: var(--surface-soft); color: var(--text); font: inherit; font-size: 13px; line-height: 1.7; }
#imageDetailDialog#imageDetailDialog .ig-prompt:disabled { cursor: not-allowed; }
#imageDetailDialog#imageDetailDialog .ig-prompt::placeholder { color: var(--muted); opacity: 1; }
/* 框下面一行：左边字数，右边「重新生成提示词」（用户 2026-10-09：不放弹窗左下角，放到提示词框下面） */
#imageDetailDialog#imageDetailDialog .ig-dlg-prompt-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: -2px; }
#imageDetailDialog#imageDetailDialog .ig-dlg-count-chars { min-width: 0; overflow: hidden; font-size: 12px; white-space: nowrap; text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-text { margin: 0; color: var(--text); font-size: 13px; line-height: 1.75; }
#imageDetailDialog#imageDetailDialog :is(.ig-cuts, .ig-lines) { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; list-style: none; }
#imageDetailDialog#imageDetailDialog .ig-cuts li { display: grid; grid-template-columns: 4.2em fit-content(40%) minmax(0, 1fr); gap: 10px; padding: 7px 10px; border-radius: 8px; background: var(--surface-soft);
  font-size: 12.5px; }
#imageDetailDialog#imageDetailDialog .ig-cuts li b { color: var(--muted); font-weight: 500; }
/* 「景别 · 运镜」最多占四成、超出换行（运镜写得长时原来把动作那一格挤没了，和分镜页那一栏同一个问题） */
#imageDetailDialog#imageDetailDialog .ig-cuts li span { color: var(--text); font-weight: 600; }
#imageDetailDialog#imageDetailDialog .ig-cuts li em { overflow: hidden; color: var(--muted); font-style: normal; white-space: nowrap; text-overflow: ellipsis; }
#imageDetailDialog#imageDetailDialog .ig-lines li { display: grid; grid-template-columns: 3.6em minmax(0, 1fr); gap: 8px; font-size: 12.5px; }
#imageDetailDialog#imageDetailDialog .ig-lines li b { color: var(--muted); font-weight: 500; }
#imageDetailDialog#imageDetailDialog .ig-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 14px; margin: 0; padding-top: 12px; border-top: 1px solid var(--line); font-size: 12.5px; }
#imageDetailDialog#imageDetailDialog .ig-fact { display: contents; }
#imageDetailDialog#imageDetailDialog .ig-facts dt { color: var(--muted); }
#imageDetailDialog#imageDetailDialog .ig-facts dd { overflow: hidden; margin: 0; color: var(--text); white-space: nowrap; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
/* 底栏只留「复制描述」「保存」（靠右，全站弹窗底栏的排法） */
#imageDetailDialog#imageDetailDialog > .dialog-footer { flex: none; align-items: center; margin: 0; border-top: 1px solid var(--line); }

/* ---------------- 笔记本（1200–1599）：目录栏 216（和剧本页一样）；画廊一排少几张；弹窗提示词框矮一档 ---------------- */
@media (min-width: 1200px) and (max-width: 1599px) {
  #mainView #imageView.ig-view:not(.hidden) { grid-template-columns: 216px minmax(0, 1fr); }
  #mainView #imageView.ig-view .ig-cover { padding: 16px 16px 12px; }
  #mainView #imageView.ig-view .ig-nav { padding: 8px 6px 10px; }
  #mainView #imageView.ig-view .ig-foot { padding: 12px 16px 14px; }
  #mainView #imageView.ig-view .ig-head { padding: 0 16px 0 18px; }
  #mainView #imageView.ig-view .ig-titlebar { min-height: 62px; gap: 10px; }
  #mainView #imageView.ig-view .ig-title { font-size: 16px; }
  #mainView #imageView.ig-view .ig-ep-code { font-size: 14px; }
  #mainView #imageView.ig-view .ig-filterbar { gap: 10px; min-height: 42px; padding-bottom: 8px; }
  #mainView #imageView.ig-view .ig-tab { padding: 0 11px; }
  #mainView #imageView.ig-view .ig-search { width: 180px; }
  #mainView #imageView.ig-view .ig-body { padding: 2px 18px 22px; }
  #mainView #imageView.ig-view .ig-grid { --cols: 3; gap: 14px 12px; }
  #mainView #imageView.ig-view .ig-main[data-ar="9x16"] .ig-grid[data-kind="storyboard"] { --cols: 6; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="2x3"], [data-ar="3x4"]) .ig-grid[data-kind="storyboard"] { --cols: 5; }
  #mainView #imageView.ig-view .ig-main[data-ar="1x1"] .ig-grid[data-kind="storyboard"] { --cols: 4; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="4x3"], [data-ar="3x2"], [data-ar="16x9"]) .ig-grid[data-kind="storyboard"] { --cols: 3; }
  #mainView #imageView.ig-view .ig-main[data-ar="21x9"] .ig-grid[data-kind="storyboard"] { --cols: 2; }
  #mainView #imageView.ig-view .ig-card-foot { padding: 8px 8px 8px 10px; gap: 5px; }
  #mainView #imageView.ig-view .ig-cast { padding: 10px 10px 10px 12px; }
  #imageDetailDialog#imageDetailDialog .ig-prompt { height: 200px; }
}

/* 台式屏的画廊不到 1600 宽（1707、1920 宽的屏）：首帧一行 9 张只剩 130 来宽，标题剩一两个字、失败那张的「重试」被裁掉（2026-10-09 实测），
   列数按笔记本那档排。2560 宽的屏画廊 2180 上下，不触发、一个像素不变 */
@container igmain (max-width: 1600px) {
  #mainView #imageView.ig-view .ig-main[data-ar="9x16"] .ig-grid[data-kind="storyboard"] { --cols: 6; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="2x3"], [data-ar="3x4"]) .ig-grid[data-kind="storyboard"] { --cols: 5; }
  #mainView #imageView.ig-view .ig-main[data-ar="1x1"] .ig-grid[data-kind="storyboard"] { --cols: 4; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="4x3"], [data-ar="3x2"], [data-ar="16x9"]) .ig-grid[data-kind="storyboard"] { --cols: 3; }
  #mainView #imageView.ig-view .ig-main[data-ar="21x9"] .ig-grid[data-kind="storyboard"] { --cols: 2; }
}

/* 内容区窄的时候：统计字收掉、生成设置只留图标、搜索框收窄 */
@container igmain (max-width: 900px) {
  #mainView #imageView.ig-view .ig-stats { display: none; }
  #mainView #imageView.ig-view .ig-settings > span:not(.ig-ic) { display: none; }
  #mainView #imageView.ig-view .ig-search { width: 128px; }
}

/* ---------------- 平板（768–1199）：目录栏收窄到 200；画廊按卡片最小宽度自己排 ---------------- */
@media (max-width: 1199px) {
  #mainView #imageView.ig-view .ig-grid { grid-template-columns: repeat(auto-fill, minmax(var(--card-min, 220px), 1fr)); gap: 14px 12px; }
  #mainView #imageView.ig-view .ig-main[data-ar="9x16"] .ig-grid[data-kind="storyboard"] { --card-min: 150px; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="2x3"], [data-ar="3x4"]) .ig-grid[data-kind="storyboard"] { --card-min: 170px; }
  #mainView #imageView.ig-view .ig-main[data-ar="1x1"] .ig-grid[data-kind="storyboard"] { --card-min: 190px; }
  #mainView #imageView.ig-view .ig-main:is([data-ar="4x3"], [data-ar="3x2"]) .ig-grid[data-kind="storyboard"] { --card-min: 220px; }
  #mainView #imageView.ig-view .ig-main[data-ar="16x9"] .ig-grid[data-kind="storyboard"] { --card-min: 260px; }
  #mainView #imageView.ig-view .ig-main[data-ar="21x9"] .ig-grid[data-kind="storyboard"] { --card-min: 320px; }
  #mainView #imageView.ig-view .ig-grid:not([data-kind="storyboard"]) { --card-min: 260px; }
  #mainView #imageView.ig-view .ig-head { padding: 0 14px 0 16px; }
  #mainView #imageView.ig-view .ig-titlebar { flex-wrap: wrap; row-gap: 8px; min-height: 0; padding: 12px 0 8px; }
  #mainView #imageView.ig-view .ig-title { font-size: 16px; }
  #mainView #imageView.ig-view .ig-filterbar { flex-wrap: wrap; gap: 8px 10px; }
  #mainView #imageView.ig-view .ig-tabs { overflow-x: auto; max-width: 100%; scrollbar-width: none; }
  #mainView #imageView.ig-view .ig-tab { padding: 0 10px; }
  #mainView #imageView.ig-view .ig-body { padding: 2px 14px 20px; }
  #mainView #imageView.ig-view .ig-bulk { flex-wrap: wrap; justify-content: center; max-width: calc(100% - 24px); white-space: normal; }
  dialog.new-work-dialog.ig-dialog { width: calc(100vw - 24px); height: calc(100dvh - 24px); }
  /* 窄屏上下排、整块一起滚（网格的 auto 行会被 overflow: hidden 的画面卡压成一条缝） */
  #imageDetailDialog#imageDetailDialog .ig-dlg-body { display: flex; flex-direction: column; gap: 18px; overflow-y: auto; container-type: normal; }
  #imageDetailDialog#imageDetailDialog :is(.ig-dlg-visual, .ig-dlg-info) { flex: none; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-visual { width: auto; height: auto; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-stage { flex: none; height: min(56dvh, 520px); }
  #imageDetailDialog#imageDetailDialog .ig-dlg-stage > .ig-visual-media { width: min(100%, calc(min(56dvh, 520px) * var(--r, .5625))); }
  #imageDetailDialog#imageDetailDialog .ig-dlg-info { height: auto; overflow: visible; }
  #imageDetailDialog#imageDetailDialog .ig-refs { grid-template-columns: minmax(0, 1fr); }
  #imageDetailDialog#imageDetailDialog .ig-prompt { height: 200px; }
}
@media (min-width: 768px) and (max-width: 1199px) {
  #mainView #imageView.ig-view:not(.hidden) { grid-template-columns: 200px minmax(0, 1fr); }
  #mainView #imageView.ig-view .ig-cover { padding: 14px 14px 10px; }
  #mainView #imageView.ig-view .ig-nav { padding: 8px 6px 10px; }
  #mainView #imageView.ig-view .ig-foot { padding: 12px 14px 14px; }
}
/* 没有鼠标悬浮的设备（手机、平板）：勾选框和「首帧 | 直出」一直露着 */
@media (hover: none) {
  #mainView #imageView.ig-view .ig-check { opacity: 1; }
  #mainView #imageView.ig-view .ig-mode.on-media { opacity: 1; }
}
/* ---------------- 手机（≤ 767）：目录栏收起，标题行最前面换成「看哪一块」那颗下拉；弹窗铺满一屏、上下排 ---------------- */
@media (max-width: 767px) {
  #mainView #imageView.ig-view:not(.hidden) { grid-template-columns: minmax(0, 1fr); }
  #mainView #imageView.ig-view .ig-binder { display: none; }
  #mainView #imageView.ig-view .ig-scope-select { display: block; flex: 1 1 100%; height: 36px; min-height: 0; padding: 0 10px; border: 1px solid var(--line-strong); border-radius: 10px;
    background: var(--surface-soft); color: var(--text); font: inherit; font-size: 14px; }
  #mainView #imageView.ig-view .ig-title { display: none; }
  #mainView #imageView.ig-view .ig-titlebar-tools { width: 100%; margin-left: 0; }
  #mainView #imageView.ig-view .ig-titlebar-tools .ig-primary { margin-left: auto; }
  #mainView #imageView.ig-view .ig-search { order: 3; width: 100%; margin-left: 0; }
  #mainView #imageView.ig-view .ig-main[data-ar="9x16"] .ig-grid[data-kind="storyboard"] { --card-min: 130px; }
  #mainView #imageView.ig-view .ig-cast-tail { flex: 1 1 100%; min-width: 0; }
  #mainView #imageView.ig-view .ig-cast-tip { flex: 1 1 auto; min-width: 0; white-space: normal; }
  dialog.new-work-dialog.ig-dialog { width: 100vw; height: 100dvh; max-width: none; border-radius: 0; }
  #imageDetailDialog#imageDetailDialog > .dialog-header { flex-wrap: wrap; padding: 10px 52px 10px 16px; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-title { flex-basis: 100%; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-body { padding: 12px 14px 16px; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-stage { height: 46dvh; }
  #imageDetailDialog#imageDetailDialog .ig-dlg-stage > .ig-visual-media { width: min(100%, calc(46dvh * var(--r, .5625))); }
  #imageDetailDialog#imageDetailDialog .ig-ref-acts { flex-wrap: wrap; justify-content: flex-end; }
}

/* ================= 剧本创作页 · 目录栏（用户 2026-10-08 在本机演示站上选的方案二：「就用方案二，做成正式版本」） =================
   1200 宽及以上：左边一栏目录（script/Binder.tsx）、中间内容、右边 Minimo 助手。顶上常驻的简介、那排滑轨页签藏掉（目录栏替它们），
   「下一步」那颗从底部正中挪到内容区右上角；剧本设定、分集大纲按宽屏排成卡片，单集正文按阅读宽度居中、本集大纲放右边一栏。
   只挂在新版页面的 .script-layout-binder 上——回退的旧页面没有目录栏，样子不变。1199 以下一条都不生效：手机、平板照旧是
   「剧本 | Minimo 助手」分栏页签 + 滑轨页签，目录栏在 DOM 里、藏着。
   **全站竖排可点的横栏以这里为准**（用户 2026-10-08：「以剧本创作的横栏点击样式为准，不要一个模块一个样式」）：
   分区行 40 高 / 圆角 10 / 14px 650，普通行 36 高 / 圆角 8 / 13px，选中 --accent-soft、悬浮 --surface-hover，不描边、按下不缩；
   行与行之间一律 6px（「栏位1点击时，下方栏位2的悬浮样式，离的很近是不行的」），别改回 2、3px。 */
.script-binder { display: none; }
.script-look { display: contents; }
.script-outline-head { display: none; }
@media (min-width: 1200px) {
  #scriptView .script-layout-binder { grid-template-columns: 268px minmax(0, 1fr) 540px; }
  #scriptView .script-layout-binder > .script-binder { display: flex; flex-direction: column; min-width: 0; min-height: 0; border-radius: 14px; background: var(--surface); overflow: hidden; }

  /* ---- 目录栏 ---- */
  #scriptView .script-binder-cover { flex: none; padding: 20px 20px 16px; border-bottom: 1px solid var(--line); }
  #scriptView .script-binder-cover b { display: block; color: var(--text); font-size: 15px; font-weight: 700; line-height: 1.5; }
  #scriptView .script-binder-cover span { display: block; margin-top: 8px; color: var(--muted); font-size: 12px; line-height: 1.6; }
  #scriptView .script-binder-nav { display: flex; flex: 1 1 auto; flex-direction: column; gap: 6px; min-height: 0; padding: 10px 10px 12px; overflow-y: auto; }
  #scriptView .script-binder-nav > *, #scriptView .script-binder-subs > *, #scriptView .script-binder-episodes > * { flex: none; }
  #scriptView .script-binder-subs, #scriptView .script-binder-episodes { display: flex; flex-direction: column; gap: 6px; }
  /* 目录栏里所有按钮的底子：优先级压到 (1,0,1)（:where 不算分），下面每一种行自己的高度、字号、颜色、删除键的大小和显隐才盖得过它
     （写成 #scriptView .script-binder button 是 (1,1,1)，删除键被撑成整行宽、一直盖在集名上，分区行的字号粗细也被 font: inherit 盖掉） */
  #scriptView :where(.script-binder) button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 0; margin: 0; border: 0; background: transparent; color: var(--text);
    font: inherit; text-align: left; cursor: pointer; }
  #scriptView :where(.script-binder) button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  #scriptView :where(.script-binder) button:disabled { cursor: not-allowed; opacity: .5; }
  #scriptView .script-binder-item { height: 40px; padding: 0 12px; border-radius: 10px; font-size: 14px; font-weight: 650; }
  #scriptView .script-binder-item em { margin-left: auto; color: var(--muted); font-size: 12px; font-style: normal; font-weight: 400; font-variant-numeric: tabular-nums; }
  /* 「剧本设定」底下几项、「剧本正文」底下逐集，从上面那颗圆点的左边起（用户 2026-10-08：「剧本设定下的文本，也左对齐保持一致」） */
  #scriptView .script-binder-sub { height: 36px; padding: 0 12px; border-radius: 8px; color: var(--muted); font-size: 13px; }
  /* 悬浮只给没选中的行：选中的那一行鼠标移上去不变色（用户 2026-10-08：「左侧的列表栏，在选中后，就不要有悬浮样式了」） */
  #scriptView .script-binder-item:not(.on):hover:not(:disabled), #scriptView .script-binder-sub:not(.on):hover:not(:disabled),
  #scriptView .script-binder-episode:not(.on) .script-binder-episode-open:hover:not(:disabled), #scriptView .script-binder-add:hover:not(:disabled) { background: var(--surface-hover); }
  #scriptView .script-binder-sub:hover:not(:disabled), #scriptView .script-binder-sub.on { color: var(--text); }
  #scriptView .script-binder-item.on, #scriptView .script-binder-sub.on, #scriptView .script-binder-episode.on .script-binder-episode-open { background: var(--accent-soft); }
  #scriptView .script-binder-dot { flex: none; width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid var(--line-strong); border-radius: 50%; }
  #scriptView .script-binder-dot.done { border-color: var(--success); background: var(--success) center / 11px no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
  #scriptView .script-binder-dot.part { padding: 2px; border-color: var(--accent); background: conic-gradient(var(--accent) var(--done, 0%), transparent 0) content-box; }
  /* 逐集：集号靠左、占圆点那么宽（18），集名和「剧本设定 / 分集大纲 / 剧本正文」的字齐；集名一行放不下末尾省略（用户：「超过了长度就在尾部省略：... 不要换行」），
     全名、字数、状态在悬浮提示里；右边一枚状态图标：写完打勾、要处理的橙点、生成中蓝点呼吸、没写空心圈 */
  #scriptView .script-binder-episode { position: relative; }
  #scriptView .script-binder-episode-open { height: 36px; padding: 0 10px 0 12px; border-radius: 8px; font-size: 13px; }
  #scriptView .script-binder-episode-open b { flex: none; width: 18px; color: var(--muted); font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; text-align: left; }
  #scriptView .script-binder-episode-open span { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  #scriptView .script-binder-state { flex: none; width: 16px; height: 16px; margin-left: 2px;
    background: radial-gradient(circle, transparent 3px, var(--line-strong) 3.5px, var(--line-strong) 4.5px, transparent 5px); }
  #scriptView .script-binder-state.done { background: var(--success);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat; }
  #scriptView .script-binder-state.warn { background: radial-gradient(circle, var(--warn, #d9a441) 3.5px, transparent 4px); }
  #scriptView .script-binder-state.running { background: radial-gradient(circle, var(--accent) 3.5px, transparent 4px); animation: script-pulse 1.2s ease-in-out infinite; }
  /* 每一集右边的「⋯」（新建分集 / 删除分集，EpisodeMenu.tsx，用户 2026-10-09）：悬浮、键盘移到这一行、菜单开着时顶替状态图标露出来 */
  #scriptView .script-binder-episode .episode-more { position: absolute; top: 6px; right: 6px; display: none; place-items: center; width: 24px; height: 24px; padding: 0;
    border-radius: 6px; color: var(--muted); }
  #scriptView .script-binder-episode .episode-more:hover:not(:disabled), #scriptView .script-binder-episode.menu-open .episode-more { background: var(--surface-hover); color: var(--text); }
  /* 选中的那一行底色是 --accent-soft，--surface-hover 压上去看不出来 */
  #scriptView .script-binder-episode.on .episode-more:hover:not(:disabled), #scriptView .script-binder-episode.on.menu-open .episode-more {
    background: color-mix(in srgb, var(--text) 10%, transparent); }
  #scriptView .script-binder-episode:is(:hover, :focus-within, .menu-open) .episode-more { display: grid; }
  #scriptView .script-binder-episode:is(:hover, :focus-within, .menu-open) .script-binder-state { visibility: hidden; }
  /* 菜单开着的那一行保持悬浮的样子：鼠标移进菜单，这一行不该像没被点过 */
  #scriptView .script-binder-episode.menu-open:not(.on) .script-binder-episode-open { background: var(--surface-hover); }
  /* 触屏上没有悬浮：「⋯」一直在，状态图标往左让一格 */
  @media (hover: none) {
    #scriptView .script-binder-episode .episode-more { display: grid; }
    #scriptView .script-binder-episode .script-binder-episode-open { padding-right: 36px; }
    #scriptView .script-binder-episode:is(:hover, :focus-within, .menu-open) .script-binder-state { visibility: visible; }
  }
  /* 新建分集：逐集那一段的最后一行，加号落在集号那一列 */
  #scriptView .script-binder-add { height: 36px; padding: 0 12px; border-radius: 8px; color: var(--muted); font-size: 13px; }
  #scriptView .script-binder-add:hover:not(:disabled) { color: var(--text); }
  #scriptView .script-binder-add i { flex: none; width: 18px; height: 18px; background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") left center / 14px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") left center / 14px no-repeat; }
  #scriptView .script-binder-foot { flex: none; display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 18px; border-top: 1px solid var(--line); }
  #scriptView .script-binder-meter { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 12px; }
  #scriptView .script-binder-meter span { color: var(--muted); }
  #scriptView .script-binder-meter b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
  #scriptView .script-binder-meter i { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }

  /* ---- 中间那一栏：简介、滑轨页签藏掉，「下一步」挪到右上角，每一页的第一行标题给它让出位置 ---- */
  /* 中间这一栏挂成容器（scriptmain）：单集正文按这一栏的宽度排（见本段末尾那条 @container），不按视口——同一个视口，目录栏、助手栏宽度一变，中间剩多少就不一样。
     里面没有 fixed 定位的东西（历史版本弹层是 absolute、贴着本栏，⋯ 菜单、弹窗都挂在 body 上），容器带的布局隔离不碍事。 */
  #scriptView .script-layout-binder .script-main { position: relative; gap: 0; padding: 0; border-radius: 14px; container: scriptmain / inline-size; }
  #scriptView .script-layout-binder .script-main > :is(.script-title-block, .script-tabs-bar) { display: none; }
  #scriptView .script-layout-binder .script-tab-content { padding: 20px 26px 30px; }
  /* 外面这层铺一块和底板同色的实心底（圆角跟着胶囊）：它压在吸顶的标题行（单独合成的一层）上面，透明的话也被拎进合成层、
     「生成剧本正文」那几个字从彩色亚像素掉成灰度（1 倍屏量过：418 → 0 个彩色像素）；铺了底，1 倍回到 418、1.5 倍从 0 到 754 */
  /* right 和内容区（.script-tab-content）的右内边距一样（大屏 26、笔记本 18），按钮右边和下面那几块卡片对齐；原来写的 20 / 16，比卡片多出去 6 / 2
     （用户 2026-10-10：「补写分集大纲 这里的按钮，要和下方的背景右对齐」）。改内容区的内边距这里跟着改。
     滚动条落在内边距里、卡片不挪（见笔记本那档后面「有没有竖滚动条，内容都不挪」），按钮照旧离面板边 --script-inset（一般就是 26）就和卡片对齐 */
  #scriptView .script-layout-binder .script-main-foot { position: absolute; z-index: 2; top: 16px; right: var(--script-inset, 26px); padding: 0; border-radius: 999px; background: var(--surface); }
  #scriptView .script-layout-binder .script-main-foot .generate-command { height: 38px; min-height: 38px; padding: 0 20px; gap: 8px; }
  #scriptView .script-layout-binder .script-tab-content > section > h3:first-child { min-height: 38px; padding-right: 230px; font-size: 18px; }
  #scriptView .script-layout-binder .script-tab-content > section > .script-list-hint:first-child { margin-right: 230px; }
  /* 点「剧本简介 / 人物」滚过去时停在吸顶那一行下面，别被它盖住 */
  #scriptView .script-layout-binder .script-tab-content { scroll-padding-top: 80px; }
  /* 每一页的标题行连同右上角「下一步」吸在顶上（用户 2026-10-08：「分集大纲 所在的行，包括右侧的 生成x集分镜 按钮，上下滚动时固定在顶部」；
     同一天又要「剧本设定的顶部也要固定」：剧本设定页原来第一行是「剧本简介」这一节的标题，吸住会一直写着剧本简介，所以给这一页加了自己的标题行
     「剧本设定」，「重新生成剧本设定」挪到它上面；影像风格那一页同理）。
     标题行自己 sticky、铺一层底色，用负外边距撑到内容区的边，滚上来的卡片、表格从它底下过；「下一步」本来就钉在这一栏右上角（不在滚动区里），
     标题行上内边距 16 = 按钮的 top，两边中线对齐成一条。
     top 写成负的内容区上内边距：sticky 是从滚动区内边距里面那条线算的，写 0 会停在离顶边 20 的地方（上面漏一条、和按钮也错开）。 */
  #scriptView .script-layout-binder .script-tab-content > section > h3:first-child { position: sticky; z-index: 1; top: -20px; box-sizing: border-box;
    min-height: 68px; margin: -20px -26px 0; padding: 16px 230px 14px 26px; background: var(--surface); }

  /* ---- 剧本设定：这一页自己的标题行（吸顶）、基本设定整宽一行两项（2026-10-09 起挪到最上面，用户：「把基本设定，移到顶部」）、简介整宽、人物三列卡片（先后不动，按类名排位置）。
     影像风格有了单独那一页（目录栏点「影像风格」），这里不再放那张卡；「重新生成剧本设定」只挂在标题行上，简介 / 人物那两行的藏掉 ---- */
  #scriptView .script-layout-binder .script-outline { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 12px 16px; align-items: start; }
  #scriptView .script-layout-binder .script-outline > * { grid-column: 1 / -1; }
  #scriptView .script-layout-binder .script-outline > .script-outline-head { grid-row: 1; display: flex; }
  #scriptView .script-layout-binder .script-outline > :is(.script-synopsis-head, .script-cast-head) .script-regen { display: none; }
  #scriptView .script-layout-binder .script-outline > .script-synopsis-head { grid-row: 3; margin-top: 12px; }
  #scriptView .script-layout-binder .script-outline > .script-synopsis { grid-row: 4; padding: 20px 24px; border-radius: 14px; background: var(--surface-soft); font-size: 15px; line-height: 1.95; }
  #scriptView .script-layout-binder .script-outline > .script-cast-head { grid-row: 5; margin-top: 12px; }
  #scriptView .script-layout-binder .script-outline > .script-cast-list { grid-row: 6; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
  #scriptView .script-layout-binder .script-outline > .script-brief { grid-row: 2; grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr); gap: 10px 16px;
    margin-top: 4px; padding: 20px 22px; border-radius: 14px; background: var(--surface-soft); }
  #scriptView .script-layout-binder .script-outline > .script-brief::before { content: "基本设定"; grid-column: 1 / -1; margin-bottom: 2px; font-size: 15px; font-weight: 700; }
  #scriptView .script-layout-binder .script-outline > .script-look { display: none; }

  /* ---- 影像风格单独那一页（用户 2026-10-08：「影像风格 点击后给一个独立的显示页面」）：一句依据、七项一项一张卡、禁令单独一张 ---- */
  #scriptView .script-layout-binder .script-look-page { display: flex; flex-direction: column; gap: 14px; }
  #scriptView .script-layout-binder .script-look-basis { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.7; }
  #scriptView .script-layout-binder .script-look-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; }
  #scriptView .script-layout-binder .script-look-card { display: flex; flex-direction: column; gap: 8px; padding: 18px 20px; border-radius: 14px; background: var(--surface-soft); }
  #scriptView .script-layout-binder .script-look-card h4 { margin: 0; color: var(--muted); font-size: 13px; font-weight: 600; }
  #scriptView .script-layout-binder .script-look-card p { margin: 0; color: var(--text); font-size: 14px; line-height: 1.8; }
  #scriptView .script-layout-binder .script-look-bans-card ul { margin: 0; padding-left: 18px; color: var(--text); font-size: 14px; line-height: 1.9; }
  #scriptView .script-layout-binder .script-person-card { gap: 4px; padding: 18px 20px; border-radius: 14px; background: var(--surface-soft); }
  #scriptView .script-layout-binder .script-person-card h4 { margin-bottom: 8px; font-size: 16px; }
  #scriptView .script-layout-binder .script-person-card p { display: grid; grid-template-columns: 5.4em minmax(0, 1fr); gap: 8px; font-size: 13px; line-height: 1.65; }
  #scriptView .script-layout-binder .script-person-card p strong { font-weight: 500; }

  /* ---- 分集大纲：卡片网格 ---- */
  #scriptView .script-layout-binder .script-beats { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 14px; align-items: stretch; }
  #scriptView .script-layout-binder .script-beats > :is(h3, .script-list-hint) { grid-column: 1 / -1; }
  #scriptView .script-layout-binder .script-beat-card { gap: 8px; padding: 18px 20px 16px; border-radius: 14px; background: var(--surface-soft); }
  #scriptView .script-layout-binder .script-beat-card h4 { margin-bottom: 4px; font-size: 16px; }
  #scriptView .script-layout-binder .script-beat-card p { display: grid; grid-template-columns: 2.6em minmax(0, 1fr); gap: 8px; font-size: 13px; line-height: 1.7; }
  #scriptView .script-layout-binder .script-beat-card p strong { font-weight: 500; }
  #scriptView .script-layout-binder .script-beat-card .script-beat-summary { color: var(--muted); }
  #scriptView .script-layout-binder .script-beat-open { margin-top: auto; }

  /* ---- 剧本正文：一张表，行之间一条线；上面那一栏带进度条；「编辑正文」改成描边的次要按钮（十二颗白色实心太抢眼） ---- */
  #scriptView .script-layout-binder .script-episodes { gap: 14px; }
  #scriptView .script-layout-binder .script-episode-table { gap: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 14px; }
  #scriptView .script-layout-binder .script-episode-row { grid-template-columns: 18px 24px 58px 168px minmax(0, 1fr) 118px 88px; gap: 14px; height: 54px; min-height: 0; padding: 0 16px;
    border-top: 1px solid var(--line); border-radius: 0; background: transparent; box-shadow: none; }
  #scriptView .script-layout-binder .script-episode-row:first-child { border-top: 0; }
  #scriptView .script-layout-binder .script-episode-row:hover { background: var(--surface-hover); }
  #scriptView .script-layout-binder .script-episode-row.selected { background: var(--accent-soft); box-shadow: none; }
  #scriptView .script-layout-binder .script-episode-status { display: inline-flex; align-items: center; justify-content: flex-end; gap: 5px; }
  /* 工作台那条 #mainView button:not(…)×5 把按钮描边刷成透明，优先级 (1,5,1)：这里要两个 id 才压得过 */
  #mainView #scriptView .script-layout-binder .script-episode-edit { height: 30px; min-height: 0; padding: 0 12px; border: 1px solid var(--line-strong); border-radius: 8px;
    background: transparent; color: var(--text); font-size: 13px; }
  #mainView #scriptView .script-layout-binder .script-episode-edit:hover:not(:disabled) { border-color: var(--muted); }
  #scriptView .script-layout-binder .script-list-foot { gap: 16px; padding: 0; background: transparent; }
  #scriptView .script-layout-binder .script-list-foot .generate-command { width: auto; min-width: 150px; height: 34px; min-height: 34px; border-radius: 999px; }
  #scriptView .script-layout-binder .script-list-stats { align-items: center; gap: 14px; }
  #scriptView .script-layout-binder .script-list-stats::after { content: ""; width: 140px; height: 6px; border-radius: 3px;
    background: linear-gradient(90deg, var(--success) var(--script-done, 0%), var(--line) 0); }
  #scriptView .script-layout-binder .script-list-stats span:first-child { color: var(--text); }

  /* ---- 单集正文：正文按阅读宽度居中、行距放开，本集大纲放右边一栏 ---- */
  #scriptView .script-layout-binder .script-main > .script-episode { display: grid; grid-template-columns: minmax(0, 940px) 340px; grid-template-rows: auto minmax(0, 1fr);
    justify-content: center; column-gap: 24px; row-gap: 18px; padding: 18px 28px 20px; }
  #scriptView .script-layout-binder .script-episode-head { grid-column: 1 / -1; min-height: 38px; }
  #scriptView .script-layout-binder .script-episode-plan { grid-column: 2; grid-row: 2; align-self: start; gap: 14px; padding: 18px 20px; border-radius: 14px; }
  #scriptView .script-layout-binder .script-episode-plan::before { content: "本集大纲"; color: var(--text); font-size: 14px; font-weight: 700; }
  #scriptView .script-layout-binder .script-episode-plan p { display: grid; grid-template-columns: 3em minmax(0, 1fr); gap: 8px; color: var(--text); line-height: 1.7; }
  #scriptView .script-layout-binder .script-episode-plan p strong { color: var(--muted); font-weight: 500; }
  #scriptView .script-layout-binder .script-episode .episode-script { grid-column: 1; grid-row: 2; display: flex; flex-direction: column; min-height: 0; padding: 0; background: transparent; }
  #scriptView .script-layout-binder .script-episode .episode-script-body { flex: 1 1 auto; min-height: 0; padding: 30px 44px; border-radius: 14px; background: var(--surface-soft);
    font-size: 15px; line-height: 2; }
  #scriptView .script-layout-binder .script-episode .episode-script-foot { padding: 12px 0 0; }

  /* 中间这一栏窄（不到 1200）：单集正文改成一栏，本集大纲是正文框上面一条，正文框铺满这一栏（2026-10-09 走查：1707 宽的屏
     中间栏七百多，大纲占右边 340，正文框只剩 330）；这一集没有大纲就不留那一行。2560 宽中间栏近 1600、不触发，排版一像素不动。
     #mainView 前缀压过后面笔记本那一档的两栏写法。 */
  @container scriptmain (max-width: 1199px) {
    #mainView #scriptView .script-layout-binder .script-main > .script-episode { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); }
    #mainView #scriptView .script-layout-binder .script-main > .script-episode:has(> .script-episode-plan:empty) { grid-template-rows: auto minmax(0, 1fr); }
    #mainView #scriptView .script-layout-binder .script-episode-plan { grid-column: 1; grid-row: 2; gap: 6px; padding: 14px 18px; }
    #mainView #scriptView .script-layout-binder .script-episode-plan p { line-height: 1.6; }
    #mainView #scriptView .script-layout-binder .script-episode .episode-script { grid-column: 1; grid-row: 3; }
    #mainView #scriptView .script-layout-binder .script-main > .script-episode:has(> .script-episode-plan:empty) > .episode-script { grid-row: 2; }
  }
}
/* 笔记本（1200–1599）：目录栏 216 宽，助手照旧 360–540；人物、大纲两列，基本设定和影像风格上下摞，正文旁边的本集大纲收到 260 */
@media (min-width: 1200px) and (max-width: 1599px) {
  #scriptView .script-layout-binder { grid-template-columns: 216px minmax(0, 1fr) clamp(360px, 28vw, 540px); }
  #scriptView .script-binder-cover { padding: 16px 16px 12px; }
  #scriptView .script-binder-nav { padding: 8px 6px 10px; }
  #scriptView .script-binder-foot { padding: 12px 16px 14px; }
  #scriptView .script-layout-binder .script-tab-content { padding: 16px 18px 24px; }
  #scriptView .script-layout-binder .script-main-foot { top: 12px; right: var(--script-inset, 18px); }
  #scriptView .script-layout-binder .script-tab-content > section > h3:first-child { padding-right: 200px; font-size: 16px; }
  #scriptView .script-layout-binder .script-tab-content { scroll-padding-top: 72px; }
  #scriptView .script-layout-binder .script-tab-content > section > .script-list-hint:first-child { margin-right: 200px; }
  /* 吸顶的标题行跟着这一档的内边距（16 / 18）和按钮的 top（12） */
  #scriptView .script-layout-binder .script-tab-content > section > h3:first-child { top: -16px; min-height: 62px; margin: -16px -18px 0; padding: 12px 200px 12px 18px; }
  #scriptView .script-layout-binder .script-outline > .script-cast-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #scriptView .script-layout-binder .script-outline > .script-brief { grid-template-columns: max-content minmax(0, 1fr); }
  #scriptView .script-layout-binder .script-look-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
  #scriptView .script-layout-binder .script-beats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #scriptView .script-layout-binder :is(.script-person-card, .script-beat-card) { padding: 16px 16px 14px; }
  #scriptView .script-layout-binder .script-episode-row { grid-template-columns: 18px 20px 50px 116px minmax(0, 1fr) 84px 76px; gap: 8px; padding: 0 12px; }
  /* 列表上面那一栏要一行放下（不然「去分镜生成」被挤到第二行）：生成键收窄、间距收一点，统计后面那条进度不画（目录栏底部就有正文进度） */
  #scriptView .script-layout-binder .script-list-foot { gap: 12px; }
  #scriptView .script-layout-binder .script-list-foot .generate-command { min-width: 120px; }
  #scriptView .script-layout-binder .script-list-stats { gap: 10px; }
  #scriptView .script-layout-binder .script-list-stats::after { display: none; }
  #scriptView .script-layout-binder .script-main > .script-episode { grid-template-columns: minmax(0, 1fr) 260px; column-gap: 18px; padding: 14px 18px 16px; }
  #scriptView .script-layout-binder .script-episode .episode-script-body { padding: 22px 26px; }
}
/* 剧本页内容区有没有竖滚动条，内容都不挪（用户 2026-10-11：「内容不能被滚动条影响，有无滚动条都不能导致左侧的内容有偏移」）。
   滚动条的位置常驻（scrollbar-gutter: stable，内容不滚也留着），右内边距扣掉它的宽（--script-scrollbar，ScriptPage.tsx 的 useScrollbarInset 量）：
   滚动条落在原来那 26（笔记本 18）的内边距里，卡片右边永远离面板边 --script-inset、和左边一样；吸顶标题行的右外边距、右上角按钮跟着同一个数。
   滚动条比内边距还宽时（--script-inset 取两者大的）按滚动条算。macOS 那种叠在内容上的滚动条宽 0，什么都不扣。
   放在笔记本那档后面：那档的 padding / margin 简写会把右边盖回去 */
@media (min-width: 1200px) {
  #scriptView .script-layout-binder .script-main { --script-inset: max(26px, var(--script-scrollbar, 0px)); }
  #scriptView .script-layout-binder .script-tab-content { scrollbar-gutter: stable; padding-right: calc(var(--script-inset) - var(--script-scrollbar, 0px)); }
  #scriptView .script-layout-binder .script-tab-content > section > h3:first-child { margin-right: calc(var(--script-scrollbar, 0px) - var(--script-inset)); }
}
@media (min-width: 1200px) and (max-width: 1599px) {
  #scriptView .script-layout-binder .script-main { --script-inset: max(18px, var(--script-scrollbar, 0px)); }
}

/* ==================== 视频生成页 · 剪辑台（方案一，2026-10-09） ====================
   用户在本机演示站「视频生成方案-1009」从三个方案里选的方案一（演示站 options.css 的 body.vd-a 那一套，照着做成正式版）：
   左边目录栏 | 中间本集标题行、预览（没出片时按状态给下一步）+ 成片版本、时间线 | 右边镜头面板（抬头、参考素材 + 原来的右栏）。
   只挂在新版的 .studio-reel 上、只在 1200 宽及以上生效：手机、平板照旧是素材栏 + 预览 + 分镜台，回退的旧页面没有这个类。
   平板那几条写的是 max-width: 1200px（含 1200），这里都带 #videoView 压过它们。 */
.reel-binder, .reel-surface, .reel-head, .reel-timeline, .reel-empty, .reel-takes, .reel-stage, .reel-shot-head, .reel-refs { display: none; }

@media (min-width: 1200px) {
  /* ---- 骨架：#videoStudio 排成格子；素材栏、分镜台藏起来（时间线、镜头面板接手） ---- */
  #videoView .video-studio.studio-reel {
    --reel-binder: 248px;
    --reel-side: 400px;
    --reel-mon: #0e0f10;
    display: grid;
    height: 100%;
    grid-template-columns: var(--reel-binder) minmax(0, 1fr) var(--reel-side);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas: "binder head side" "binder mon side" "binder line side";
    gap: 12px;
  }
  /* 预览窗按画幅定宽（reelFit.ts 量了写 --reel-mon-w，用户 2026-10-09：「确保实现16:9的完整显示，左右不会黑边，多出来的空间给到右侧」）：
     中间那一列正好放下画面，右边镜头面板吃剩下的；竖屏 / 方形 / 量不出来时没有 data-fit，照上面那条排 */
  #videoView .video-studio.studio-reel[data-fit] { grid-template-columns: var(--reel-binder) var(--reel-mon-w) minmax(0, 1fr); }
  #videoView .studio-mobile-tabs:has(+ .studio-reel) { display: none; }
  #videoView .studio-reel > .studio-center { display: contents; }
  #videoView .studio-reel .studio-resources,
  #videoView .studio-reel > .studio-board-panel { display: none; }
  #videoView .studio-reel > :is(.reel-head, .reel-timeline, .reel-empty),
  #videoView .studio-reel .studio-preview-panel,
  #videoView .studio-reel > .studio-inspector { position: relative; z-index: 1; min-width: 0; min-height: 0; }
  /* 中间那一列（标题行 + 预览 + 时间线）共用一张底板 */
  #videoView .studio-reel > .reel-surface { display: block; grid-column: 2; grid-row: 1 / -1; z-index: 0; border-radius: 14px; background: var(--surface); }

  /* ---- 目录栏（照分镜生成页那一栏：封面、逐集、底部进度；横栏的样子以剧本页目录栏为准） ---- */
  #videoView .studio-reel > .reel-binder { grid-area: binder; display: flex; flex-direction: column; min-height: 0; overflow: hidden; border-radius: 14px; background: var(--surface); }
  .studio-reel .reel-cover { flex: none; padding: 20px 20px 16px; border-bottom: 1px solid var(--line); }
  .studio-reel .reel-cover b { display: block; color: var(--text); font-size: 15px; font-weight: 700; line-height: 1.5; }
  .studio-reel .reel-cover span { display: block; margin-top: 8px; color: var(--muted); font-size: 12px; line-height: 1.6; }
  .studio-reel .reel-nav { display: flex; flex: 1 1 auto; flex-direction: column; gap: 6px; min-height: 0; padding: 10px 10px 12px; overflow-y: auto; scrollbar-width: thin; }
  .studio-reel .reel-ep { display: flex; flex: none; align-items: center; gap: 10px; width: 100%; height: 36px; min-height: 0; margin: 0; padding: 0 10px 0 12px; border: 0;
    border-radius: 8px; background: transparent; color: var(--text); font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
  .studio-reel .reel-ep b { flex: none; width: 18px; color: var(--muted); font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-ep span { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .studio-reel .reel-ep em { flex: none; color: var(--muted); font-size: 12px; font-style: normal; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-ep.blank span { color: var(--muted); }
  /* 选中那一行悬浮不变色（用户 2026-10-08，剧本页目录栏同一条） */
  .studio-reel .reel-ep:not(.on):hover { background: var(--surface-hover); }
  .studio-reel .reel-ep.on { background: var(--accent-soft); }
  .studio-reel .reel-state { flex: none; width: 16px; height: 16px; margin-left: 2px; background: radial-gradient(circle, transparent 3px, var(--line-strong) 3.5px, var(--line-strong) 4.5px, transparent 5px); }
  .studio-reel .reel-state.done { background: var(--success);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px no-repeat; }
  .studio-reel .reel-state.part { box-sizing: border-box; width: 14px; height: 14px; margin: 1px 1px 1px 3px; padding: 2.5px; border: 1.5px solid var(--accent); border-radius: 50%;
    background: conic-gradient(var(--accent) var(--done, 0%), transparent 0) content-box; }
  .studio-reel .reel-state.warn { background: radial-gradient(circle, var(--warn, #d9a441) 3.5px, transparent 4px); }
  .studio-reel .reel-state.blank { background: radial-gradient(circle, transparent 3px, var(--line) 3.5px, var(--line) 4.5px, transparent 5px); opacity: .7; }
  .studio-reel .reel-foot { flex: none; display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 18px; border-top: 1px solid var(--line); }
  .studio-reel .reel-queue { display: grid; gap: 4px; padding: 10px 12px; border-radius: 10px; background: var(--surface-soft); }
  .studio-reel .reel-queue b { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: 13px; font-weight: 600; }
  .studio-reel .reel-queue b i { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); animation: reel-pulse 1.4s ease-in-out infinite; }
  .studio-reel .reel-queue span { color: var(--muted); font-size: 12px; line-height: 1.5; }
  @keyframes reel-pulse { 50% { opacity: .35; } }
  .studio-reel .reel-meter { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 12px; }
  .studio-reel .reel-meter span { color: var(--muted); }
  .studio-reel .reel-meter b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-meter i { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--success) var(--done, 0%), var(--line) 0); }

  /* ---- 本集标题行：E01 集名、统计；整集连播、生成剩余 N 镜 ---- */
  #videoView .studio-reel > .reel-head { grid-area: head; display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 36px; padding: 18px 20px 0; }
  .studio-reel .reel-title { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
  .studio-reel .reel-code { flex: none; color: var(--muted); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-title b { flex: none; color: var(--text); font-size: 18px; font-weight: 700; }
  .studio-reel .reel-stats { min-width: 0; overflow: hidden; color: var(--muted); font-size: 12px; white-space: nowrap; text-overflow: ellipsis; }
  .studio-reel .reel-head-actions { display: flex; flex: none; align-items: center; gap: 10px; }
  #mainView #videoView .studio-reel .reel-batch { display: inline-flex; align-items: center; gap: 8px; height: 34px; min-height: 0; padding: 0 6px 0 16px; border-radius: 999px; font-size: 13px; white-space: nowrap; }
  /* 右边 6 是给价签留的；不带价签（交不全、正在提交、暂无剩余）时两边一样，不然字偏右 5 像素（用户 2026-10-10：「没在按钮中居中显示」） */
  #mainView #videoView .studio-reel .reel-batch:not(:has(.cost-chip)) { padding: 0 16px; }
  #mainView #videoView .studio-reel .reel-batch b { font-weight: 700; font-variant-numeric: tabular-nums; }
  /* 主按钮上的价签反色（深色主题白底按钮配深底白字），和镜头面板「保存并生成视频」、分镜页 / 图片页的生成按钮一个样；原来是默认的 26% 黑，深色主题下灰底灰字 */
  #mainView #videoView .studio-reel :is(.reel-batch, .reel-cta .primary-command, .reel-tip .primary-command) .cost-chip { background: var(--command-create-text); color: var(--command-create-bg); }
  /* 描边胶囊（整集连播）：配色和「去生图」同一套变量；连播中描强调色 */
  #mainView #videoView .studio-reel .reel-line { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; min-height: 0; padding: 0 14px;
    border: 1px solid var(--btn-outline-border); border-radius: 999px; background: var(--btn-outline-bg); color: var(--text); font-size: 13px; font-weight: 400; white-space: nowrap;
    cursor: pointer; transition: background-color .15s, translate .15s, scale .15s; }
  #mainView #videoView .studio-reel .reel-line:hover:not(:disabled) { background: var(--btn-outline-hover); }
  #mainView #videoView .studio-reel .reel-line:active:not(:disabled) { translate: 0 1px; scale: .98; }
  #mainView #videoView .studio-reel .reel-line.on { border-color: var(--accent); color: var(--accent-text); }
  .studio-reel .reel-ico-play { width: 12px; height: 12px; background: currentColor; clip-path: polygon(15% 5%, 95% 50%, 15% 95%); }

  /* ---- 下一步 / 跳转按钮（去生成图片、去生成分镜、改为首帧生成 / 直接生成、停止生成）：和分镜页「去生图」（.sb-next）一个样 ----
     两个 id 起头：全站 #mainView button:not(…) 会把按钮的边框改成透明，只写类名压不过它 */
  #mainView #videoView .studio-reel .reel-next { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; min-height: 0; padding: 0 14px;
    border: 1px solid var(--btn-outline-border); border-radius: 999px; background: var(--btn-outline-bg); color: var(--text); font-size: 13px; font-weight: 400; white-space: nowrap;
    cursor: pointer; transition: background-color .15s, translate .15s, scale .15s; }
  #mainView #videoView .studio-reel .reel-next:hover:not(:disabled) { background: var(--btn-outline-hover); }
  #mainView #videoView .studio-reel .reel-next:active:not(:disabled) { translate: 0 1px; scale: .98; }
  /* 图标也是 span：提示句那几条「… span { color }」会套到它头上、比字淡一截，颜色跟回按钮的字 */
  #mainView #videoView .studio-reel .reel-next .jump-icon { width: 12px; height: 12px; margin: 0; color: inherit; }
  #mainView #videoView .studio-reel .reel-next:disabled { cursor: not-allowed; opacity: .45; }

  /* ---- 镜头面板：原来的右栏，上面多一行抬头（镜号、镜头名、秒数和版本、出片方式）和一排参考素材 ---- */
  #videoView .studio-reel > .studio-inspector { grid-area: side; display: flex; flex-direction: column; overflow: hidden; border-radius: 14px; background: var(--surface); }
  .studio-reel .reel-shot-head { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 26px; padding: 16px 20px 12px;
    container: reelsh / inline-size; }
  .studio-reel .reel-sh-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
  /* 出片方式是四个字（首帧生成 | 直接生成）：面板内容宽不到 420（1536 及以下）时镜号、镜头名一行，秒数和版本挪到下面一行，不截字 */
  @container reelsh (max-width: 420px) {
    .studio-reel .reel-sh-title { flex-wrap: wrap; row-gap: 2px; }
    .studio-reel .reel-sh-meta { flex-basis: 100%; }
  }
  .studio-reel .reel-no { flex: none; color: var(--muted); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-sh-title b { flex: none; color: var(--text); font-size: 16px; font-weight: 700; }
  .studio-reel .reel-sh-meta { min-width: 0; overflow: hidden; color: var(--muted); font-size: 12px; white-space: nowrap; text-overflow: ellipsis; }
  /* 右上角「首帧生成 | 直接生成」的样子走「滑轨页签」共用块（.reel-seg），这里不另写 */
  /* 两行：参考素材（角色 / 场景 / 道具图）、白模预演（3D 导演台）；行距 12，比一行里图片之间的 6 宽一倍，两类东西分得开（用户 2026-10-09：「要和上方的图片等隔开」） */
  .studio-reel .reel-refs { flex: none; display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: start; gap: 12px 10px; padding: 0 20px 14px;
    border-bottom: 1px solid var(--line); }
  .studio-reel .reel-refs-label { flex: none; padding-top: 7px; color: var(--muted); font-size: 12px; }
  .studio-reel .reel-refs-list { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
  .studio-reel .reel-refs-none { padding-top: 7px; color: var(--muted); font-size: 12px; }
  #mainView #videoView .studio-reel .reel-ref { display: inline-flex; align-items: center; gap: 6px; height: 30px; min-height: 0; max-width: 100%; padding: 0 9px 0 3px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface-soft); color: var(--text); font-size: 12px; cursor: pointer; }
  #mainView #videoView .studio-reel .reel-ref:hover { border-color: var(--line-strong); }
  .studio-reel .reel-ref img { flex: none; width: 24px; height: 24px; border-radius: 5px; object-fit: cover; }
  .studio-reel .reel-ref span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  /* 角色 / 场景 / 道具那几项（2026-10-11 起）：外面这层画边框，左边一块点开选版本（.reel-ref-main），右端「×」移除这一项（.reel-ref-x）；
     3D 导演台那一项还是一整颗按钮，吃上面那条 */
  #mainView #videoView .studio-reel .reel-ref:is(.reel-ref-role, .reel-ref-scene, .reel-ref-prop) { gap: 0; padding: 0; cursor: default; }
  #mainView #videoView .studio-reel .reel-ref-main { display: inline-flex; align-items: center; gap: 6px; min-width: 0; height: 100%; min-height: 0; padding: 0 2px 0 3px;
    border: 0; border-radius: 7px 0 0 7px; background: transparent; color: inherit; font-size: 12px; cursor: pointer; }
  .studio-reel .reel-ref.missing .reel-ref-main { cursor: default; }
  #mainView #videoView .studio-reel .reel-ref-x { position: relative; flex: none; width: 22px; height: 22px; min-height: 0; margin: 0 3px 0 1px; padding: 0; border: 0; border-radius: 6px;
    background: transparent; color: var(--muted); cursor: pointer; }
  .studio-reel .reel-ref-x::before { content: ""; position: absolute; inset: 0; width: 10px; height: 10px; margin: auto; background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 2l6 6M8 2 2 8' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 2l6 6M8 2 2 8' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; }
  #mainView #videoView .studio-reel .reel-ref-x:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
  #mainView #videoView .studio-reel .reel-ref-x:disabled { cursor: not-allowed; opacity: .45; }
  .studio-reel .reel-ref em { flex: none; padding: 0 4px; border-radius: 4px; background: var(--accent-soft); color: var(--accent-text); font-size: 11px; font-style: normal; }
  #mainView #videoView .studio-reel .reel-ref.missing { cursor: default; color: var(--muted); }
  #mainView #videoView .studio-reel .reel-ref.missing:hover { border-color: var(--line); }
  .studio-reel .reel-ref.missing i { display: grid; place-items: center; width: 24px; height: 24px; box-sizing: border-box; border: 1px dashed var(--line-strong); border-radius: 5px; font-size: 0; }
  .studio-reel .reel-ref.missing i::after { content: "待"; color: var(--muted); font-size: 10px; font-style: normal; }
  #mainView #videoView .studio-reel .reel-ref-3d { padding-left: 7px; }
  .studio-reel .reel-ref-3d em { background: var(--surface); color: var(--muted); }
  .studio-reel .reel-ref-3d.warn em { color: var(--warn, #d9a441); }
  .studio-reel .reel-ico-3d { flex: none; width: 16px; height: 16px; background: var(--accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'%3E%3Cpath d='M12 2 21 7v10l-9 5-9-5V7z'/%3E%3Cpath d='M12 22V12M21 7l-9 5-9-5'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'%3E%3Cpath d='M12 2 21 7v10l-9 5-9-5V7z'/%3E%3Cpath d='M12 22V12M21 7l-9 5-9-5'/%3E%3C/svg%3E") center / contain no-repeat; }
  #mainView #videoView .studio-reel :is(.reel-ref-remove, .reel-refs-restore) { height: 30px; min-height: 0; padding: 0 8px; border: 0; background: transparent; color: var(--muted); font-size: 12px; cursor: pointer; }
  #mainView #videoView .studio-reel :is(.reel-ref-remove, .reel-refs-restore):hover:not(:disabled) { color: var(--text); }
  #mainView #videoView .studio-reel .reel-refs-restore:disabled { cursor: not-allowed; opacity: .45; }
  /* 台词整段显示；镜头描述占剩下的高度；生成设置和两颗按钮钉在底部 */
  #videoView .studio-reel .studio-inspector-content { display: flex; flex: 1 1 auto; flex-direction: column; gap: 14px; min-height: 0; padding: 14px 20px 0; overflow-x: hidden; overflow-y: auto;
    scrollbar-width: thin; }
  #videoView .studio-reel .studio-inspector-content > * + * { margin-top: 0; }
  #videoView .studio-reel .studio-inspector .studio-copy-section { display: flex; flex-direction: column; min-height: 0; border-radius: 0; background: transparent; }
  #videoView .studio-reel .studio-inspector .studio-copy-section:not(.studio-description-section) { flex: none; max-height: none; }
  #videoView .studio-reel .studio-inspector .studio-description-section { flex: 1 0 180px; min-height: 180px; }
  #videoView .studio-reel .studio-inspector .studio-section-head { min-height: 0; padding: 0 0 8px; }
  #videoView .studio-reel .studio-inspector .studio-section-head h3 { color: var(--muted); font-size: 12px; font-weight: 500; line-height: 18px; }
  #videoView .studio-reel .studio-dialogue-editor-wrap { flex: none; height: auto; min-height: 44px; max-height: 320px; margin: 0; overflow: hidden; }
  #videoView .studio-reel .studio-dialogue-editor { min-height: 44px; max-height: 320px; border-radius: 8px; line-height: 1.85; }
  #videoView .studio-reel .studio-description-editor-wrap { margin: 0; }
  #videoView .studio-reel .studio-description-editor { border-radius: 8px; }
  /* 生成设置和两颗按钮那一块不铺底色，落在右栏这张卡上：小屏那条铺 --bg（那里它在卡片外面），这里照吃的话盖成页面色，
     右栏看着在分隔线那儿断掉、比中间那一列短一截，按钮像悬在外面（用户 2026-10-10：「按钮区域怎么短一截，并且没有背景？」）。
     上面不画分隔线（用户同一天：「不需要那根线」），和镜头描述之间就是一格区块间距 14（原来线上下各 14，撤了线不收的话空出 28） */
  #videoView .studio-reel .studio-inspector .studio-actions { flex: none; margin: 0; padding: 14px 20px 16px; background: transparent; }
  /* 右栏窄（笔记本 380 宽）也放得下：清晰度 / 比例 / 时长按比例分，「更多设置」按内容；两颗按钮左边按内容、右边吃剩下的 */
  #videoView .studio-reel .studio-generate-row-params { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, .9fr) auto; }
  #videoView .studio-reel .studio-generate-row-params > * { min-width: 0; }
  #videoView .studio-reel :is(#studioDuration, #studioMore) { width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #videoView .studio-reel .studio-action-row { grid-template-columns: max-content minmax(0, 1fr); }
  #videoView .studio-reel .studio-action-row > button { min-width: 0; padding-right: 10px; padding-left: 14px; white-space: nowrap; }

  /* ---- 预览：画面 + 播放条 + 成片版本（原来播放条上那颗「版本」藏起来，成片版本挪到下面一排） ---- */
  #videoView .studio-reel .studio-preview-panel { grid-area: mon; display: flex; flex-direction: column; padding: 14px 20px 0; overflow: hidden; border-radius: 0; background: transparent; }
  #videoView .studio-reel .studio-preview-head { display: none; }
  #videoView .studio-reel .studio-preview-stage { position: relative; overflow: hidden; border-radius: 12px; background: var(--reel-mon); }
  #videoView .studio-reel .studio-preview { margin: 0; background: transparent; }
  #videoView .studio-reel .studio-preview-pane,
  #videoView .studio-reel .studio-preview-pane :is(img, video) { border-radius: 0; background: transparent; }
  #videoView .studio-reel .studio-preview-empty strong { color: #ddd; }
  /* 播放条不铺底色，直接落在中间那一列的底板上：原来照小屏那条铺 --surface-alt（深色 #1c1c1c，底板 #212121），圆角画面正下面接一块直角的深色条，
     两个下角看着缺一块（用户 2026-10-10：「预览窗口的底部，有很奇怪的边缘」） */
  #videoView .studio-reel .studio-player-controls { padding: 8px 0 6px; border-top: 0; background: transparent; }
  #videoView .studio-reel #studioTakeHost { display: none !important; }
  /* 没出片时盖在画面上的那一层：画首帧 + 状态 + 下一步；出过片的不盖。预览这一块深浅主题都是深底。 */
  #videoView .studio-reel .reel-stage { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; overflow: hidden; background: var(--reel-mon); color: #eee; }
  .studio-reel .reel-stage-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
  .studio-reel .reel-stage-shade { position: absolute; inset: 0; background: rgba(8, 9, 10, .58); }
  .studio-reel .reel-cta { position: absolute; right: 0; bottom: 22px; left: 0; display: flex; flex-direction: column; align-items: center; gap: 10px; }
  /* 给首帧图上的字垫底的渐变；直接生成没有图，不垫（背景和「首帧未生成」一样，用户 2026-10-09） */
  .studio-reel .reel-cta::before { content: ""; position: absolute; inset: -60px 0 -22px; z-index: -1; background: linear-gradient(transparent, rgba(0, 0, 0, .55)); }
  .studio-reel .reel-tip-mode ~ .reel-cta::before { display: none; }
  .studio-reel .reel-cta-note { color: rgba(255, 255, 255, .78); font-size: 12px; }
  #mainView #videoView .studio-reel :is(.reel-cta, .reel-tip) .primary-command { display: inline-flex; align-items: center; gap: 8px; height: 38px; min-height: 0; padding: 0 8px 0 18px;
    border-radius: 999px; font-size: 14px; }
  #mainView #videoView .studio-reel :is(.reel-cta, .reel-tip) .primary-command:not(:has(.cost-chip)) { padding: 0 18px; }
  /* 提示（首帧未生成 / 首帧已过期 / 直接生成 / 生成失败）：说明一律写一行（最长 28 个字，1200 宽时预览窗只有 400 也放得下） */
  .studio-reel .reel-tip { position: relative; display: grid; justify-items: center; gap: 10px; max-width: 460px; padding: 0 20px; text-align: center; }
  .studio-reel .reel-tip b { color: #fff; font-size: 15px; font-weight: 600; }
  .studio-reel .reel-tip > span { color: rgba(255, 255, 255, .72); font-size: 12px; line-height: 1.6; }
  .studio-reel .reel-tip.bad b { color: #ff8a8a; }
  .studio-reel .reel-tip-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 4px; }
  /* 生成失败那一条不带按钮（生成键是底下那条的「生成视频」；用户 2026-10-10：「不要搞什么重新生成按钮」），按钮那一行空着也占一行按钮的高（和 .reel-next 一样 32）：
     标题、说明和直接生成 / 首帧未生成那几条落在同一高度。底下那条不垫渐变（首帧图上已经盖了暗层，直接生成没有图） */
  .studio-reel .reel-tip-actions:empty { height: 32px; }
  .studio-reel .reel-tip.bad ~ .reel-cta::before { display: none; }
  /* 排队中 / 生成中：环、标题、已等待 / 已用时、（排队中才有的）停止生成，一整列在预览窗里居中。环原来绝对定位在正中、文字靠 margin-top 46 往下让，
     排队中多一颗按钮、整列变高往上挪，环就压在「排队中」上（用户 2026-10-10：「这是什么情况，重叠了」），挪进这一列就不会再叠 */
  .studio-reel .reel-run { position: relative; display: grid; justify-items: center; gap: 6px; color: #fff; text-align: center; }
  .studio-reel .reel-run-spin { box-sizing: border-box; width: 30px; height: 30px; margin-bottom: 4px; border: 3px solid rgba(255, 255, 255, .18); border-top-color: var(--accent);
    border-radius: 50%; animation: studio-spin 1s linear infinite; }
  .studio-reel .reel-run b { font-size: 15px; font-weight: 600; }
  .studio-reel .reel-run > span { color: rgba(255, 255, 255, .7); font-size: 12px; font-variant-numeric: tabular-nums; }
  #mainView #videoView .studio-reel .reel-run .reel-next { margin-top: 6px; }
  /* 成片版本：这一行定高（版本卡 52 + 上 12），没成片、有成片、生成中换着看都不跳（用户 2026-10-09）。下面到时间线那道线是格子间距 12，
     上下一样（原来上 2 下 16 + 12：「离上方太近了，离下方太远了，这个区域的高度可以缩小一点」，2026-10-10 改，整块矮了 6） */
  #videoView .studio-reel .reel-takes { flex: none; display: flex; box-sizing: border-box; align-items: center; gap: 12px; height: 64px; min-height: 0; padding: 12px 0 0; }
  .studio-reel .reel-takes-label { flex: none; color: var(--muted); font-size: 12px; }
  .studio-reel .reel-takes-none { color: var(--muted); font-size: 12px; }
  .studio-reel .reel-takes-list { display: flex; gap: 8px; min-width: 0; overflow-x: auto; scrollbar-width: thin; }
  #mainView #videoView .studio-reel .reel-take { display: flex; flex: none; align-items: center; gap: 8px; height: 52px; min-height: 0; padding: 4px 12px 4px 4px; border: 1px solid var(--line);
    border-radius: 10px; background: var(--surface-soft); color: var(--text); text-align: left; cursor: pointer; }
  #mainView #videoView .studio-reel .reel-take:hover:not(.on) { border-color: var(--line-strong); }
  #mainView #videoView .studio-reel .reel-take.on { border-color: var(--accent); background: var(--accent-soft); }
  #mainView #videoView .studio-reel .reel-take.ghost { border-style: dashed; cursor: default; }
  .studio-reel .reel-take-media { display: grid; flex: none; place-items: center; width: min(76px, calc(42px * var(--arw, 16) / var(--arh, 9))); aspect-ratio: var(--arw, 16) / var(--arh, 9);
    overflow: hidden; border-radius: 6px; background: var(--reel-mon); }
  .studio-reel .reel-take-media video { width: 100%; height: 100%; object-fit: cover; }
  .studio-reel .reel-take-text { display: grid; gap: 2px; }
  .studio-reel .reel-take-text b { font-size: 12px; font-weight: 600; }
  .studio-reel .reel-take-text small { color: var(--muted); font-size: 11px; white-space: nowrap; }
  .studio-reel .reel-take.on .reel-take-text small { color: var(--accent-text); }
  .studio-reel .reel-spin { width: 16px; height: 16px; border: 2px solid rgba(255, 255, 255, .2); border-top-color: var(--accent); border-radius: 50%; animation: studio-spin 1s linear infinite; }

  /* ---- 时间线（每一镜一段、宽度按秒数；已出片的正常亮、其余调暗） ---- */
  #videoView .studio-reel > .reel-timeline { grid-area: line; display: block; padding: 0 20px 16px; border-top: 1px solid var(--line); }
  .studio-reel .reel-tl-top { display: flex; align-items: center; gap: 10px; height: 40px; }
  .studio-reel .reel-tl-top b { color: var(--text); font-size: 13px; font-weight: 600; }
  .studio-reel .reel-tl-top > span { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
  .studio-reel .reel-tl-actions { display: flex; align-items: center; gap: 4px; margin-left: auto; }
  #mainView #videoView .studio-reel .reel-text-btn { height: 28px; min-height: 0; padding: 0 8px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); font-size: 12px;
    cursor: pointer; transition: background-color .15s, color .15s, translate .15s, scale .15s; }
  #mainView #videoView .studio-reel .reel-text-btn:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
  #mainView #videoView .studio-reel .reel-text-btn:active:not(:disabled) { translate: 0 1px; scale: .98; }
  #mainView #videoView .studio-reel .reel-text-btn.on { color: var(--accent-text); }
  #mainView #videoView .studio-reel .reel-text-btn:disabled { cursor: not-allowed; opacity: .5; }
  .studio-reel .reel-tl-scroll { overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; scrollbar-width: thin; }
  .studio-reel .reel-tl-inner { position: relative; }
  .studio-reel .reel-tl-ruler { position: relative; height: 18px; }
  .studio-reel .reel-tick { position: absolute; top: 0; height: 5px; padding-left: 4px; border-left: 1px solid var(--line-strong); color: var(--muted); font-size: 10px; line-height: 10px;
    font-variant-numeric: tabular-nums; white-space: nowrap; }
  .studio-reel .reel-tick.major { height: 10px; }
  .studio-reel .reel-tl-track { display: flex; gap: 4px; height: 66px; }
  .studio-reel .reel-clip-item { position: relative; flex: none; height: 66px; touch-action: pan-x pan-y; }
  /* 拖动排序照分镜台（用户 2026-10-10：「拖动的时候没有悬浮，要和以前一样的那种卡片拖动」）：浮起来的是一份拷贝（.reel-clip-lift，固定定位跟着手、
     放大 1.05、带阴影），原位那一格只留看不见的盒子当缺口，其余几段平移让位（这条过渡就是让位的动画），放下时拷贝落进缺口再撤 */
  .studio-reel .reel-clip-item { transition: transform .22s cubic-bezier(.22, .61, .36, 1); }
  .studio-reel .reel-clip-item.lifted .reel-clip { visibility: hidden; }
  .studio-reel .reel-tl-track.dragging,
  .studio-reel .reel-tl-track.dragging .reel-clip { cursor: grabbing; user-select: none; }
  #mainView #videoView .studio-reel .reel-clip.reel-clip-lift { position: fixed; z-index: 120; margin: 0; box-shadow: 0 18px 42px rgba(0, 0, 0, .48); pointer-events: none;
    transform: scale(1.05); transform-origin: center; will-change: transform; backface-visibility: hidden; }
  #mainView #videoView .studio-reel .reel-clip.reel-clip-lift.settling { transition: transform .2s cubic-bezier(.22, .61, .36, 1); }
  #mainView #videoView .studio-reel .reel-clip { position: relative; display: block; width: 100%; height: 66px; min-height: 0; padding: 0; overflow: hidden; border: 0; border-radius: 8px;
    background: var(--surface-soft); cursor: pointer; }
  .studio-reel .reel-clip-film { position: absolute; inset: 0; background-position: left center; background-repeat: repeat-x; background-size: auto 100%; }
  .studio-reel .reel-clip-film.none { background: repeating-linear-gradient(135deg, var(--surface-soft) 0 7px, var(--surface-hover) 7px 14px); }
  .studio-reel .reel-clip-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  .studio-reel .reel-clip:not(.k-done) :is(.reel-clip-film:not(.none), .reel-clip-video) { opacity: .42; filter: grayscale(.5); }
  .studio-reel .reel-clip-tag { position: absolute; top: 5px; left: 5px; padding: 1px 6px; border-radius: 5px; background: rgba(0, 0, 0, .62); color: #fff; font-size: 11px; line-height: 16px;
    font-variant-numeric: tabular-nums; white-space: nowrap; }
  .studio-reel .reel-clip-tag b { margin-right: 4px; font-weight: 600; }
  .studio-reel :is(.reel-clip-direct, .reel-clip-takes) { position: absolute; bottom: 5px; padding: 0 5px; border-radius: 4px; background: rgba(0, 0, 0, .62); color: #fff; font-size: 10px;
    line-height: 16px; white-space: nowrap; }
  .studio-reel .reel-clip-direct { left: 5px; }
  .studio-reel .reel-clip-takes { right: 5px; }
  .studio-reel .reel-clip:hover { filter: brightness(1.08); }
  .studio-reel .reel-clip.narrow .reel-clip-tag { padding: 1px 4px; font-size: 10px; }
  .studio-reel .reel-clip.narrow .reel-clip-tag b { margin-right: 0; }
  .studio-reel .reel-clip.narrow .reel-clip-tag span,
  .studio-reel .reel-clip.narrow :is(.reel-clip-direct, .reel-clip-takes) { display: none; }
  /* 选中的那一段：强调色描边画在最上面一层（::after 抬到画面之上）。原来是往里收 2 像素的 outline，被铺满的画面 / 视频盖住，
     用户 2026-10-10：「选中后没有选中样式，不知道选中了哪个」；里面再压一圈细暗边，亮画面上也分得开 */
  .studio-reel .reel-clip.on::after { content: ""; position: absolute; inset: 0; z-index: 2; border: 2px solid var(--accent); border-radius: inherit;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .35); pointer-events: none; }
  .studio-reel .reel-clip.on :is(.reel-clip-film, .reel-clip-video) { opacity: 1; filter: none; }
  #mainView #videoView .studio-reel .reel-clip-remove { position: absolute; top: 4px; right: 4px; z-index: 1; display: grid; place-items: center; width: 22px; height: 22px; min-height: 0;
    padding: 0; border: 0; border-radius: 50%; background: rgba(0, 0, 0, .7); color: #fff; cursor: pointer; }
  .studio-reel .reel-clip-remove .close-icon { width: 10px; height: 10px; }
  .studio-reel .reel-tl-playhead { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; background: #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, .45); opacity: 0; pointer-events: none; }
  :root:not([data-theme="dark"]) .studio-reel .reel-tl-playhead { background: #111; box-shadow: 0 0 0 1px rgba(255, 255, 255, .6); }

  /* ---- 没拆分镜的那一集：中间到右边一整块写明，预览、镜头面板、时间线藏起来 ---- */
  #videoView .studio-reel.reel-empty-ep > :is(.studio-inspector, .reel-timeline),
  #videoView .studio-reel.reel-empty-ep .studio-preview-panel { display: none; }
  #videoView .studio-reel.reel-empty-ep > .reel-surface { grid-column: 2 / -1; }
  #videoView .studio-reel > .reel-empty { display: grid; grid-column: 2 / -1; grid-row: 2 / -1; place-items: center; text-align: center; }
  .studio-reel .reel-empty > div { display: grid; justify-items: center; gap: 10px; }
  .studio-reel .reel-empty b { color: var(--text); font-size: 15px; }
  .studio-reel .reel-empty > div > span { color: var(--muted); font-size: 13px; }
}

/* 笔记本（1200–1599）：目录栏、右栏收窄一点 */
@media (min-width: 1200px) and (max-width: 1599px) {
  #videoView .video-studio.studio-reel { --reel-binder: 220px; --reel-side: 380px; }
  #mainView #videoView .studio-reel .reel-head-actions .reel-line { padding: 0 12px; }
}
@media (min-width: 1600px) {
  #videoView .video-studio.studio-reel { --reel-binder: 268px; --reel-side: 480px; }
}

/* 剪辑台「生成剩余 N 镜」点开的那个框（RemainingDialog.tsx，用户 2026-10-09：「生成剩余就按全部未出片的数来显示，如果选择的模型无法生成超出时长的视频，就弹窗提示」）：
   样子跟全站确认框，宽一点放得下三颗按钮（取消 / 仅生成能生成的 / 改用某某模型生成）；正文一类一块，补得上的那一类行尾一颗补的按钮
   （去生成图片是跳转按钮那一身，生成镜头描述扣积分、是生成类那一身）。价签在白底按钮上反色（和生成类按钮上的价签一个规矩）。 */
.studio-remaining-dialog { width: min(640px, calc(100vw - 32px)); }
.studio-remaining-body { display: grid; gap: 10px; padding: 4px 20px 8px; color: var(--text); font-size: 13px; line-height: 1.6; }
.studio-remaining-intro, .studio-remaining-note { margin: 0; }
.studio-remaining-note { color: var(--muted); font-size: 12px; }
.studio-remaining-rows { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.studio-remaining-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 10px; background: var(--surface-soft); }
.studio-remaining-text { display: grid; flex: 1; gap: 2px; min-width: 0; }
.studio-remaining-text p { display: flex; align-items: baseline; gap: 8px; margin: 0; }
.studio-remaining-text b { font-weight: 600; }
.studio-remaining-row.ok .studio-remaining-text b { color: var(--success); }
.studio-remaining-text em { color: var(--muted); font-style: normal; font-variant-numeric: tabular-nums; }
.studio-remaining-codes { overflow-wrap: anywhere; font-size: 12px; font-variant-numeric: tabular-nums; }
.studio-remaining-text small { color: var(--muted); font-size: 12px; }
#studioRemainingDialog .studio-remaining-fix { flex: none; height: 30px; min-height: 0; padding: 0 12px; font-size: 12px; white-space: nowrap; }
#studioRemainingDialog .studio-remaining-fix.gen { display: inline-flex; align-items: center; border: 0; border-radius: 999px; background: var(--command-create-bg); color: var(--command-create-text);
  font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; }
#studioRemainingDialog .studio-remaining-fix.gen .cost-chip,
#studioRemainingDialog .dialog-footer .primary-command .cost-chip { margin-left: 6px; background: var(--command-create-text); color: var(--command-create-bg); }
#studioRemainingDialog .dialog-footer { flex-wrap: wrap; }

/* ==================== 目录栏顶上的剧本简介（2026-10-10） ====================
   剧本、分镜、图片、视频四个目录栏顶上那段（script/BinderSynopsis.tsx）：剧本简介的前三行，「展开」看全文、「收起」收回，点文字也一样；
   展开、收起的高度动画由组件放（Web Animations，.26s，和箭头同一条缓动），这里只管放的时候不出滚动条。
   原来那里是副标题 → 剧名 → 文件夹名（副标题早就没地方填，线上全是剧名、和顶栏项目名重复；用户：「改」）。
   字照正文那一档（13px、正文色），下面只剩统计那行灰字（题材 · 时代 · 地域那行同一天撤了、空出来给简介：两行 → 三行）；展开后最高 40vh、再长就在框里滚。
   「展开」是一行灰字，不是独立的操作按钮（不进按下缩小那一串）。没有简介时灰字那行顶上的 8 像素外边距去掉，四个目录栏各一条（优先级要压过各自那条 span）。
   剧本、分镜、图片三个目录栏各有一条「目录栏里的按钮一律复位」（带 id，字色回正文、字号跟着外面），光写 button.类名 压不住：
   按钮那两条用 :is(四个目录栏顶上那块) 抬到它们上面（宽度也写回按内容：那几条复位把按钮撑成整行宽，空白处都能点）。 */
.binder-synopsis { display: grid; justify-items: start; gap: 2px; }
.binder-synopsis p { margin: 0; max-width: 100%; color: var(--text); font-size: 13px; line-height: 1.6; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.binder-synopsis.open p { display: block; -webkit-line-clamp: unset; max-height: 40vh; overflow-y: auto; }
/* 三行放不下时点文字也能展开、收起；高度动画放着的时候不出滚动条（框比字矮时 overflow-y: auto 会闪一下） */
.binder-synopsis.clickable p { cursor: pointer; }
.binder-synopsis p.animating { overflow: hidden; }
:is(#scriptView .script-binder-cover, #workflowView .breakdown-layout-binder .sb-cover, #mainView #imageView.ig-view .ig-cover, .studio-reel .reel-cover) button.binder-synopsis-toggle {
  display: inline-flex; align-items: center; gap: 2px; justify-self: start; width: auto;
  height: auto; min-height: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  color: var(--muted); font: inherit; font-size: 12px; font-weight: 400; line-height: 1.6; cursor: pointer; }
:is(#scriptView .script-binder-cover, #workflowView .breakdown-layout-binder .sb-cover, #mainView #imageView.ig-view .ig-cover, .studio-reel .reel-cover) button.binder-synopsis-toggle:hover { color: var(--text); }
/* 箭头（剧本页药丸那颗 12×12 的下箭头）：颜色跟着字走、悬浮一起亮；展开了转过去朝上。
   它是个 span：四个目录栏顶上那块的 span 规则（块级、上外边距 8、灰色 12px）会套上来，这里写回去 */
:is(#scriptView .script-binder-cover, #workflowView .breakdown-layout-binder .sb-cover, #mainView #imageView.ig-view .ig-cover, .studio-reel .reel-cover) .binder-synopsis-toggle .spec-chevron {
  display: inline-flex; align-items: center; margin: 0; color: inherit; font-size: inherit; line-height: 1; transition: transform .26s cubic-bezier(.4, 0, .2, 1); }
:is(#scriptView .script-binder-cover, #workflowView .breakdown-layout-binder .sb-cover, #mainView #imageView.ig-view .ig-cover, .studio-reel .reel-cover) .binder-synopsis-toggle[aria-expanded="true"] .spec-chevron { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  :is(#scriptView .script-binder-cover, #workflowView .breakdown-layout-binder .sb-cover, #mainView #imageView.ig-view .ig-cover, .studio-reel .reel-cover) .binder-synopsis-toggle .spec-chevron { transition: none; }
}
#workflowView .breakdown-layout-binder .sb-cover > span:first-child,
#mainView #imageView.ig-view .ig-cover > span:first-child,
#scriptView .script-binder-cover > span:first-child,
.studio-reel .reel-cover > span:first-child { margin-top: 0; }

/* ==================== 跳转按钮（全站一个样，2026-10-09） ====================
   用户：「这种跳转按钮统一为：去生成图片、去生成分镜、去生成XX这种」「按钮的样式，也改成 分镜生成中的 去生图一样」「跳转按钮全站一起统一」。
   挂 .jump-command 的（去生成图片 / 去生成分镜 / 去生成视频 / 去生成正文 / 去生成分镜首帧）一律是分镜页「去生图」那一身：
   描边胶囊、常规字重、文字后面挂跳转图标，悬浮换底色、按下下沉 1 像素再缩到 .98（按下效果在顶上那一串里）；扣积分的生成类照旧是主按钮。
   颜色、描边、圆角、字重、阴影带 !important：各处原来的按钮类（图片页标题行 .ig-primary、剧本页「下一步」.generate-command、
   资产卡 .asset-generate / .asset-view、分镜表 .shot-action-command、分镜页主按钮 .one-click-command）写法各异、优先级高低不一，
   不压住就各是各的样。高度、内边距、字号照旧归各处按场合定（行里 30、空态 40、标题行 38），这里不写尺寸，别为了「一样」去改它们。 */
.jump-command {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid var(--btn-outline-border) !important;
  border-radius: 999px !important;
  background: var(--btn-outline-bg) !important;
  background-image: none !important;
  color: var(--text) !important;
  font-weight: 400 !important;
  box-shadow: none !important;
  filter: none !important;
  transition: background-color .15s, translate .15s, scale .15s;
}
.jump-command:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--btn-outline-hover) !important; }
.jump-command:disabled,
.jump-command[aria-disabled="true"] { cursor: not-allowed; opacity: .45; }
.jump-command .jump-icon { flex: none; width: 12px !important; height: 12px !important; margin: 0 !important; color: inherit; }
/* 资产卡图框正中那颗「去生成图片」灰掉时和上面「生图提示词」同一身：中性底、灰字、不透明（用户 2026-09-12 定的，统一之后照旧） */
.asset-placeholder .asset-generate.jump-command:disabled { border-color: transparent !important; background: var(--command-neutral-bg) !important; color: var(--muted) !important; opacity: 1; }
/* 分集列表里「去生成正文」那颗小签（整行是按钮，它是 span 扮的）：照旧 12 号字、小一圈，图标跟着收小 */
.episode-pill.jump-command .jump-icon { width: 10px !important; height: 10px !important; }
.episode-pill.jump-command { gap: 4px; }
.episode-pill.action.jump-command:active { transform: none; }
