/* ==================== 登录框 ====================
   用户 2026-09-21：「不需要独立的登录页了……登录弹窗就用现在的登录页的那个样式，但是不要半透明和渐变的，
   用符合当前主题色的弹窗，并且弹窗左半边显示的图片，要可以在控制台配置，没有配置，就显示默认的 MinimoTV」。

   所以版式照搬登录页（左品牌 / 右表单的双栏），配色整套换成主题变量：面板是 --surface、条目是 --surface-soft、
   描边 --line、主按钮 --gradient-primary。**这里一个半透明、一个毛玻璃都没有**——登录页那套 backdrop-filter
   压在流光背景上才成立，压在应用界面上只会把底下的表格和卡片糊成一团。

   auth.js 认的是 .auth-* 那几个类名和 id，所以类名照搬登录页；样式全部收在 .login-dialog 里面，
   页面别处的 .auth-* 一个都碰不到（/forgot、/reset 两页仍旧读 auth.css，两套互不影响）。 */

.login-dialog {
  width: min(880px, calc(100vw - 32px));
  max-width: none;
  max-height: calc(100vh - 48px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  background: var(--surface);
  color: var(--text);
  overflow: hidden;
}
.login-dialog::backdrop { background: rgba(0, 0, 0, .55); }

/* **高度写死**（用户 2026-09-21：「登录弹窗尺寸不能改变……干脆直接变高后的为固定尺寸」）。原来是 min-height: 520，
   各屏按内容撑：扫码屏 522、登录屏 528，从扫码屏点「其他方式登录」整个框会高 6px。526 是登录屏（带底部那行
   「微信登录」）的内容高度，加上下 1px 的边就是 528。报错提示出来也不变：表单是上下居中的，多出来的那一两行
   往上下的留白里长（最多各 16px，留白有 34px），框不动。
   唯一的例外是找回密码那一屏：五个输入框要 624，塞不进来，那一屏照旧按内容撑。 */
.login-dialog-body { display: grid; grid-template-columns: 1fr 1fr; height: 526px; }
.login-dialog-body:has(#forgotPane:not(.hidden)) { height: auto; min-height: 526px; }

/* ---- 左半边：控制台配的图，没配就是品牌块 ---- */
.login-dialog-visual {
  position: relative;
  display: grid;
  place-items: center;
  padding: 24px;
  /* 图没铺满时露出来的底：比面板深一档，两半分得开。 */
  background: var(--surface-soft);
  overflow: hidden;
}
.login-dialog-visual img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.login-dialog-brand {
  display: flex;
  z-index: 1;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
/* 和侧边栏那枚一样：取标的形状、颜色交给主题定，透明底的标在浅色主题上才不会没影。 */
/* 标和字一行，排法和侧栏左上角一样：按基线对齐（标是空盒子，基线 = 自己的底边），大写字母高 = 标高（22px）。
   `font-size-adjust` 把「大写字母高 = 字号 × 0.7」钉住，换一套字体也不走样；字号 = 22 ÷ 0.7。改标高，两处的 22 一起改。 */
.login-dialog-lockup { display: flex; align-items: baseline; gap: 12px; }
.login-dialog-brand .login-dialog-logo {
  /* 两个三角形的那枚标，宽高比 738 : 458（2026-09-21 换的，原来是 72 见方的猫头鹰）。 */
  flex: none;
  width: calc(22px * 738 / 458);
  height: 22px;
  background-color: var(--text);
  -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;
}
.login-dialog-brand strong { font-size: calc(22px / .7); font-size-adjust: cap-height .7; font-weight: 800; letter-spacing: .02em; line-height: 1.2; white-space: nowrap; }
.login-dialog-brand span { color: var(--muted); font-size: 13px; }

/* ---- 右半边：表单 ---- */
.login-dialog-form { display: flex; flex-direction: column; justify-content: center; padding: 34px 38px; }
.login-dialog-form h2 { margin: 0; font-size: 22px; font-weight: 750; }
.login-dialog-form .auth-subtitle { margin: 6px 0 20px; color: var(--muted); font-size: 13px; }

/* 手机号 / 邮箱：全站那条滑轨的底色和滑块，这里按登录页的结构摆（滑块位置由 auth.js 算）。 */
.login-dialog .auth-method-switch {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 18px;
  padding: 4px;
  border-radius: 999px;
  background: var(--tab-rail-strong);
}
.login-dialog .auth-method-switch.single { grid-template-columns: 1fr; }
.login-dialog .auth-method-switch button {
  position: relative;
  z-index: 1;
  min-height: 34px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
}
.login-dialog .auth-method-switch button.active { color: var(--on-primary); }
.login-dialog .auth-method-switch button.hidden { display: none; }
.login-dialog .auth-method-thumb {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  border-radius: 999px;
  background: var(--primary-bg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
  opacity: 0;
}
.login-dialog .auth-method-thumb.ready { opacity: 1; }
.login-dialog .auth-method-switch.thumb-animate .auth-method-thumb {
  transition: transform .28s cubic-bezier(.22, .61, .36, 1), width .28s cubic-bezier(.22, .61, .36, 1);
}

.login-dialog .auth-field { position: relative; margin-bottom: 14px; }
.login-dialog .auth-field.hidden { display: none; }
.login-dialog .auth-field input {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--input-bg);
  color: var(--text);
  font-size: 14px;
}
.login-dialog .auth-field input:focus { outline: none; }
.login-dialog .auth-field input.invalid { border-color: var(--red); }
.login-dialog .auth-inline { display: flex; gap: 10px; }
.login-dialog .auth-code-button {
  flex: none;
  width: 116px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-soft);
  color: var(--text);
  font-size: 13px;
}
.login-dialog .auth-password input { padding-right: 44px; }
.login-dialog .auth-eye {
  position: absolute;
  top: 0;
  right: 0;
  width: 44px;
  min-height: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
}
/* 眼睛用边框画：睁着是一条弧加一个圆，闭上多一道斜杠。不引图片，跟着主题变色。 */
.login-dialog .auth-eye::before {
  content: "";
  display: block;
  width: 17px;
  height: 17px;
  margin: 0 auto;
  border: 1.6px solid currentColor;
  border-radius: 50% 0;
  transform: rotate(45deg);
}
.login-dialog .auth-eye.revealed::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 12px;
  width: 20px;
  border-top: 1.6px solid currentColor;
  transform: rotate(-45deg);
}
.login-dialog .auth-error { margin: 6px 2px 0; color: var(--red); font-size: 12px; }
.login-dialog .auth-agree-field { margin: 4px 0 16px; }
.login-dialog .auth-agree-note { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.7; }
/* 两个协议名只比旁边的字亮一档，不用强调色、不加书名号（用户 2026-09-21：「不要书名号，把协议的字体加亮一点就行了」）。 */
.login-dialog .auth-agree-note a { color: var(--text); text-decoration: none; }
.login-dialog .auth-submit {
  width: 100%;
  min-height: 46px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--gradient-primary);
  color: #fff;
  font-size: 15px;
  font-weight: 650;
}
.login-dialog .auth-status { min-height: 18px; margin: 10px 0 0; color: var(--red); font-size: 12.5px; text-align: center; }
.login-dialog .auth-links { display: flex; justify-content: space-between; margin-top: 12px; }
.login-dialog .auth-links a, .login-dialog .auth-mode-toggle {
  border: 0;
  padding: 0;
  min-height: 0;
  background: none;
  color: var(--accent-text);
  font-size: 13px;
  text-decoration: none;
}
.login-dialog .auth-mode-toggle.hidden { visibility: hidden; }

.login-dialog-close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 30px;
  min-width: 0;
  height: 30px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--surface-soft);
  color: var(--text);
}

/* ---- 图形验证码 / 封禁：两个小框，同一套配色 ---- */
#captchaDialog, #bannedDialog {
  width: min(380px, calc(100vw - 32px));
  padding: 22px 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
}
#captchaDialog::backdrop, #bannedDialog::backdrop { background: rgba(0, 0, 0, .55); }
#captchaDialog h3, #bannedDialog h3 { margin: 0 0 14px; font-size: 17px; font-weight: 700; }
#captchaDialog .auth-field { position: relative; margin-bottom: 14px; }
#captchaDialog input {
  width: 100%;
  height: 42px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--input-bg);
  color: var(--text);
}
#captchaDialog .auth-error { margin: 6px 2px 0; color: var(--red); font-size: 12px; }
#captchaImage, .auth-captcha-image { flex: none; width: 116px; height: 42px; border-radius: var(--radius-sm); cursor: pointer; object-fit: cover; }
.auth-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; }
.auth-dialog-primary { border: 0; background: var(--gradient-primary); color: #fff; font-weight: 650; }
.auth-dialog-hint { margin: 0 0 16px; color: var(--muted); font-size: 13px; line-height: 1.7; }

/* ---- 微信扫码登录（2026-09-21）----
   底部中间那一行字：扫码屏、绑手机屏是「其他方式登录」，手机号 / 邮箱屏是「微信登录」，同一个位置上的一对，点一下互相换。
   位置用户同一天改过三回，最后定的是：「微信登录 也要居中啊，其他方式登录 的高度，改成 微信登录的高度，保持居中」——
   所以三屏都钉在同一个高度、左右居中。绝对定位，不占内容的高度：框的高度是写死的，多一行少一行它都在原地；
   扫码屏上「标题 + 二维码 + 协议」要作为一个整体上下居中，也靠它不在内容里。
   bottom 取 14 是量出来的：登录屏两条报错同时出来时，上面那行「忘记密码 / 验证码登录」会往下长到离底边 34px 的地方，
   再低就压上了。窄屏单栏时高度按内容走，那行字回到内容里。 */
.login-dialog-form { position: relative; }
/* 用 flex 居中，不用 left: 50% + translateX：全站按钮按下是 transform: scale，两个 transform 会打架、一按就跳。 */
.login-dialog-foot { position: absolute; right: 0; bottom: 14px; left: 0; display: flex; justify-content: center; }
.login-dialog-foot.hidden { display: none; }

/* 二维码那一格。登录框、设置页的「绑定微信」、控制台的「扫码自测」共用，所以选择器不挂在登录框上。
   **底色写死白**，不跟主题走：识别靠的是明暗对比，深色主题下给它一块深底，手机相机就认不出来了
   （和付款码同一个道理）。 */
.wechat-qr-box {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--wechat-qr-size, 196px);
  height: var(--wechat-qr-size, 196px);
  margin: 2px auto 10px;
  /* 内边距是 0（用户 2026-09-21：「把里面的码放大一点，现在内边距有点宽了」）。微信那张图是 430×430、
     自带 30px 白边（3 个模块，量过）；原来再叠 8px 内边距，总留白 20px、码只有 153px。收到 0 之后留白就是
     微信自己给的那一圈（13.5px），码 167px。**别再往下裁**：那一圈是码的静区，微信出图时认为够扫的下限，
     深色主题下框外面是黑的，静区只能靠框里这圈白。 */
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md, 10px);
  background: #fff;
  overflow: hidden;
}
.wechat-qr-image { width: 100%; height: 100%; object-fit: contain; }
/* 还没取到图的那一下别露出浏览器的碎图标。 */
.wechat-qr-image:not([src]) { visibility: hidden; }
.wechat-qr-image.stale { opacity: .1; filter: blur(2px); }
.wechat-qr-refresh {
  position: absolute;
  inset: 0;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: rgba(255, 255, 255, .86);
  color: #202124;
  font-size: 13px;
  font-weight: 650;
  line-height: 1.8;
  white-space: normal;
}
.wechat-qr-refresh.hidden { display: none; }
.wechat-qr-hint { margin: 0; color: var(--muted); font-size: 12.5px; text-align: center; }
.wechat-qr-hint.hidden { display: none; }
/* 扫码那一屏：标题居中、没有副标题（用户 2026-09-21），标题和二维码之间的空由它自己留。 */
/* 标题的长度正好等于二维码框的宽度（用户同天：「标题字体可以大一点，确保标题长度正好和下方的二维码框宽度一致」）。
   标题六个汉字（2026-09-26 起是「微信一键登录」，原来「微信扫码登录」也是六个），汉字的字宽就是 1em，所以字号 = 框宽 ÷ 6（196 ÷ 6 ≈ 32.67px），不靠字距去凑。
   **改标题的字数、或者改框的大小，这个 6 和 --wechat-qr-size 要跟着改**（渲染量过：标题 196.0px、框 196px）。 */
.login-dialog #wechatPane { --wechat-qr-size: 196px; }
.login-dialog #wechatPane h2 {
  margin-bottom: 20px;
  font-size: calc(var(--wechat-qr-size) / 6);
  line-height: 1.25;
  letter-spacing: 0;
  text-align: center;
  white-space: nowrap;
}
/* 「其他方式登录」是灰的，不是强调色（用户同天：和原来那句「打开微信扫一扫」一个颜色）：这一屏的主角是二维码。
   字体和上面那句「登录即代表您已阅读…」一样（用户 2026-09-26）：12px、常规字重、行高 1.7——数值跟着 .auth-agree-note 走。
   底部那几颗切换链接（扫码屏 / 绑手机号屏的「其他方式登录」、密码屏的「微信登录」）共用这一条，一起变。 */
.login-dialog .auth-mode-toggle.wechat-other-login { color: var(--muted); font-size: 12px; font-weight: 400; line-height: 1.7; }
/* 协议那句贴近二维码（用户：「上移一点」）：原来中间夹着一条常年空着的状态行，码和这句话隔了 48px，现在 16px。 */
.login-dialog #wechatPane .auth-agree-field { margin: 6px 0 0; text-align: center; }
/* 扫码屏的状态行（码失效、账号不可用）浮在「其他方式登录」上面，不占内容的位置：它平时是空的，
   留在内容里就是一块看不见的 28px，把「标题 + 二维码 + 协议」这个整体顶得不居中。 */
.login-dialog #wechatPane .wechat-float-status {
  position: absolute;
  right: 38px;
  bottom: 40px;
  left: 38px;
  min-height: 0;
  margin: 0;
}

.wechat-bind-dialog { width: min(360px, calc(100vw - 32px)); }
.wechat-bind-body { text-align: center; }
.wechat-bind-body .captcha-status { min-height: 18px; }

/* 控制台：服务器地址那一行。只读，给人复制到公众号后台用。 */
.console-wechat-callback {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 12px 0 4px;
  font-size: 13px;
}
.console-wechat-callback span { flex: none; color: var(--muted); }
.console-wechat-callback code {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--surface-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: all;
}

/* ---- 窄屏：左半边那张图收起，只留表单 ---- */
@media (max-width: 760px) {
  .login-dialog-body { grid-template-columns: 1fr; height: auto; min-height: 0; }
  .login-dialog-body:has(#forgotPane:not(.hidden)) { min-height: 0; }
  .login-dialog-visual { display: none; }
  .login-dialog-form { padding: 30px 22px; }
  /* 单栏时高度按内容走，没有那块固定的留白可钉：角上那行字回到内容里，靠右。 */
  .login-dialog-foot { position: static; margin-top: 14px; }
  .login-dialog #wechatPane .wechat-float-status { position: static; margin-top: 8px; }
}

/* ---- 登录框里的按钮照 New API 的样子（2026-09-28）----
   用户选的是「登录框和付款弹窗保持版式，只换里面的按钮」：版式、尺寸一概不动，按钮换成全站那套（变量 --btn-* 在 styles.css 末尾「按钮与弹窗」）。
   主按钮（登录、重设密码、绑定并登录、确定、验证并发送）照全站主按钮：黑白纯色、悬浮不变——原来是青蓝渐变；
   获取验证码、安全验证框的取消是描边款；显示密码的眼睛、右上角 × 是幽灵款；下面那几行文字链接悬浮出下划线。
   手机号 / 邮箱那对切换是滑轨页签，不动。 */
.login-dialog .auth-submit,
.auth-dialog-primary,
#bannedDialog .auth-submit {
  border: 1px solid transparent;
  border-radius: var(--btn-radius);
  background: var(--command-create-bg);
  color: var(--command-create-text);
  font-weight: 500;
}
.login-dialog .auth-code-button,
.auth-dialog-ghost {
  border: 1px solid var(--btn-outline-border);
  border-radius: var(--btn-radius);
  background: var(--btn-outline-bg);
  color: var(--text);
  font-weight: 500;
}
.login-dialog .auth-code-button:hover:not(:disabled),
.auth-dialog-ghost:hover:not(:disabled) { background: var(--btn-outline-hover); }
/* 眼睛原来是输入框右端一整块 44×44 的透明区：悬浮有了底色就顶满输入框右端，收成居中的 32×32，图标位置不变。 */
.login-dialog .auth-eye { top: 6px; right: 6px; width: 32px; height: 32px; min-height: 0; border-radius: var(--btn-radius-sm); }
.login-dialog .auth-eye.revealed::after { left: 6px; }
.login-dialog .auth-eye:hover:not(:disabled) { background: var(--btn-ghost-hover); color: var(--text); }
.login-dialog-close { width: 28px; height: 28px; border-radius: var(--btn-radius-sm); background: transparent; color: var(--muted); }
.login-dialog-close:hover:not(:disabled) { background: var(--btn-ghost-hover); color: var(--text); }
.login-dialog .auth-links a,
.login-dialog .auth-mode-toggle { text-underline-offset: 4px; }
.login-dialog .auth-links a:hover,
.login-dialog .auth-mode-toggle:hover:not(:disabled) { text-decoration: underline; }
.login-dialog .auth-submit, .auth-dialog-primary, #bannedDialog .auth-submit, .login-dialog .auth-code-button,
.auth-dialog-ghost, .login-dialog .auth-eye, .login-dialog-close {
  transition:
    background-color .15s var(--btn-ease), border-color .15s var(--btn-ease), color .15s var(--btn-ease),
    translate .15s var(--btn-ease), scale .15s var(--btn-ease);
}
.login-dialog .auth-submit:focus-visible, .auth-dialog-primary:focus-visible, #bannedDialog .auth-submit:focus-visible,
.login-dialog .auth-code-button:focus-visible, .auth-dialog-ghost:focus-visible, .login-dialog .auth-eye:focus-visible,
.login-dialog-close:focus-visible {
  outline: 3px solid var(--btn-ring);
  outline-offset: 0;
  border-color: var(--accent);
}

/* ==================== Minimo Design 送来登录的这一页 ====================
   用户 2026-10-28：「点开插件，要默认跳转到Design，而不是Tv」。Design（UE 插件的面板、它的网页）没登录时转到
   /?login=1&next=/design/…，入口给 <html> 挂 data-sign-in="design"（auth/login.ts designSignInTarget）：这一页只露登录框，
   TV 首页、侧栏、横幅一样都不露；框背后是 Design 的首页（/design/backdrop，auth/DesignSignIn.tsx），模糊加一层轻的压暗——
   同一天：「design的登录弹窗，也是对背景做模糊处理，和TV一样，不要被背景搞黑了」。模糊只做在那张首页自己身上（filter），
   不给登录框的蒙版加毛玻璃：平时的登录框压在应用界面上，那层不许糊（login.test.js 钉着）。四边多铺出 24px，糊开的边缘不露底色。
   **藏用 visibility 不用 display**：登录框长在 body 底下，display:none 会连顶层里的框一起藏掉；visibility 能在框上再打开。
   框右上角的 × 和 Esc 照常能关，关了回 Design（auth/login.ts leaveDesignSignIn；用户 2026-10-29：「在design中登录面板要关闭按钮」，
   原来两样都拦着）。选择器只写类名（login.test.js 钉着不许出现框的 id）。 */
html[data-sign-in="design"] { background: var(--bg); }
html[data-sign-in="design"] body { visibility: hidden; background: transparent; }
html[data-sign-in="design"] :is(.login-dialog, .auth-dialog, .legal-dialog) { visibility: visible; }
html[data-sign-in="design"] .login-dialog::backdrop { background: rgba(0, 0, 0, .35); }
.design-sign-in-backdrop {
  position: fixed;
  inset: -24px;
  width: calc(100% + 48px);
  height: calc(100% + 48px);
  border: 0;
  filter: blur(8px);
  visibility: visible;
  pointer-events: none;
}
.login-dialog-brand .login-dialog-design-mark { flex: none; align-self: center; width: 34px; height: 34px; }
