/* 统一 UI 层（前台 + 后台共用）：友好确认框 + 轻提示
   配套脚本：/static/common/ui-dialog.js
   只使用两端 CSS 都有的变量（--panel/--ink/--line/--radius/--shadow），
   品牌色用 var(--brand, var(--green)) 兜底。 */

/* ---------- 确认框 ---------- */
.ui-mask {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(12, 22, 30, .46);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    animation: ui-fade .16s ease;
}

.ui-mask[hidden] { display: none !important; }

.ui-card {
    width: 100%;
    max-width: 420px;
    background: var(--panel);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    overflow: hidden;
    animation: ui-pop .2s cubic-bezier(.22, 1, .36, 1);
}

.ui-head {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 15px 18px 12px;
    border-bottom: 1px solid var(--line);
}

/* 标题前的图标徽标（颜色随 tone 变） */
.ui-ico {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 11px;
    background: rgba(220, 38, 38, .1);
    color: var(--danger, #dc2626);
}

.ui-ico svg { width: 19px; height: 19px; }

.ui-card[data-tone="warn"] .ui-ico {
    background: rgba(217, 119, 6, .12);
    color: var(--warn, #d97706);
}

.ui-card[data-tone="primary"] .ui-ico {
    background: rgba(15, 157, 112, .12);
    color: var(--brand, var(--green, #0f9d70));
}

.ui-card[data-tone="info"] .ui-ico {
    background: rgba(14, 165, 233, .12);
    color: #0ea5e9;
}

.ui-title { flex: 1 1 auto; margin: 0; font-size: 15.5px; font-weight: 700; color: var(--ink); }

.ui-head .close {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    color: var(--ink-3);
    font-size: 19px;
    line-height: 1;
    cursor: pointer;
}

.ui-head .close:hover { background: rgba(15, 23, 42, .06); color: var(--ink); }

.ui-body { padding: 16px 18px; }

.ui-msg {
    margin: 0;
    color: var(--ink-2);
    font-size: 14px;
    line-height: 1.7;
    white-space: pre-line;
    word-break: break-word;
}

.ui-foot {
    display: flex;
    gap: 10px;
    padding: 0 18px 18px;
}

.ui-foot .btn { flex: 1 1 0; }

.ui-foot .btn.danger {
    border-color: transparent;
    background: linear-gradient(135deg, #ef5350, #c62828);
    color: #fff;
    box-shadow: 0 12px 26px -14px rgba(198, 40, 40, .9);
}

@keyframes ui-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ui-pop {
    from { opacity: 0; transform: translateY(10px) scale(.97); }
    to { opacity: 1; transform: none; }
}

/* ---------- 轻提示（右上角堆叠） ----------
 * 约定：站内**所有**轻量反馈统一走 window.uiToast(message, type, duration)，
 * 不要自己再写浮层。type = success | fail | error | warn | info。
 *
 * 配色思路：**浅色调底 + 明亮色图标 + 深色文字**。
 *   之前试过「高饱和实底 + 白字」，大色块看久了发浊（尤其橙 / 琥珀），
 *   而且浅色系状态（琥珀、绿）配白字对比度不够。浅底深字既清爽，
 *   又把强调色集中在图标和进度条上，层次更清楚。
 *
 * 每种状态只覆盖 4 个变量：
 *   --ui-tone 强调色（图标底、进度条）  --ui-bg 卡片底
 *   --ui-line 描边                      --ui-ink 文字
 *
 * 底部那条进度条就是「还剩多久消失」：时长由 JS 通过 --ui-toast-life 传进来，
 * 和它的 setTimeout 用同一个值，视觉与真实关闭时间永远对得上。
 * --ui-toast-delay 用来把同时冒出来的多条依次错开（JS 按排队序号给）。 */
.ui-toasts {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 210;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    pointer-events: none;
}

.ui-toast {
    --ui-tone: #3b82f6;
    --ui-bg: #eff6ff;
    --ui-line: #bfdbfe;
    --ui-ink: #1e40af;
    --ui-icon: none;
    --ui-toast-delay: 0ms;

    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 11px;
    width: min(88vw, 360px);
    padding: 13px 16px 15px;
    overflow: hidden;              /* 进度条的圆角跟着卡片走 */
    font-size: 14.5px;             /* 比原来大一档 */
    font-weight: 600;              /* 加粗：在浅色底上更挺、层次更清楚 */
    line-height: 1.55;
    color: var(--ui-ink);
    background: var(--ui-bg);
    border: 1px solid var(--ui-line);
    border-radius: 14px;
    box-shadow:
        0 18px 40px -22px rgba(13, 42, 53, .34),
        0 1px 3px rgba(13, 42, 53, .04);
    /* 用 backwards 而不是 both：both 会在动画结束后一直占着 transform / opacity，
     * 导致出场时这两个属性的 transition 完全不生效（只有高度在动，看着就是「啪」一下没了）。
     * backwards 只负责「延迟期间先摆成初始样子」，动画一结束就把控制权交还给普通样式。 */
    animation: ui-toast-in .46s cubic-bezier(.16, 1, .3, 1) var(--ui-toast-delay) backwards;

    /* 起始高度由 JS 在出场前量好后写成 inline 的 max-height（见 ui-dialog.js）；
     * 不给具体值的话就只能猜一个大数，塌陷会「前 3/4 时间不动、最后一下收紧」。 */
    max-height: 240px;

    /* 出场：淡出 + 右移 + 高度/内边距一起塌陷，让下面几条平稳补位 */
    transition:
        opacity .3s cubic-bezier(.4, 0, .2, 1),
        transform .34s cubic-bezier(.4, 0, .2, 1),
        max-height .34s cubic-bezier(.4, 0, .2, 1),
        padding-top .34s cubic-bezier(.4, 0, .2, 1),
        padding-bottom .34s cubic-bezier(.4, 0, .2, 1);
}

/* 左侧图标：明亮色圆底 + 白图形；进场时「转着弹出来」 */
.ui-toast::before {
    content: '';
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    margin-top: 1px;
    border-radius: 50%;
    background-color: var(--ui-tone);
    background-image: var(--ui-icon);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 13px 13px;
    animation: ui-toast-icon .58s cubic-bezier(.34, 1.4, .64, 1) calc(var(--ui-toast-delay) + 90ms) both;
}

/* 底部倒计时进度条：scaleX 1 → 0（右端向左缩） */
.ui-toast::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: var(--ui-tone);
    opacity: .62;
    transform-origin: left;
    animation: ui-toast-life var(--ui-toast-life, 2800ms) linear forwards;
}

/* 卡片：从右侧滑入 + 淡入 + 轻微放大（ease-out-expo，收尾很轻） */
@keyframes ui-toast-in {
    from { opacity: 0; transform: translateX(34px) scale(.94); }
    to   { opacity: 1; transform: none; }
}

/* 图标：缩到很小、带着旋转出场 → 过冲一点点 → 回正 */
@keyframes ui-toast-icon {
    0%   { opacity: 0; transform: scale(.2) rotate(-30deg); }
    62%  { opacity: 1; transform: scale(1.14) rotate(4deg); }
    100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

@keyframes ui-toast-life {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}

/* 五种状态：一种底色 + 一个图标。版面完全一致，只换颜色与图形。
 *   success 成功 / fail 失败（操作没做成）/ error 错误（系统·网络异常）
 *   warn 警告 / info 提示                                             */
.ui-toast.success {
    --ui-tone: #16a34a;
    --ui-bg: #f0fdf4;
    --ui-line: #bbf7d0;
    --ui-ink: #166534;
    --ui-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 6.4l2.4 2.4L9.4 3.6' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.ui-toast.fail {
    --ui-tone: #f97316;
    --ui-bg: #fff7ed;
    --ui-line: #fed7aa;
    --ui-ink: #9a3412;
    --ui-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.1 6h5.8' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
}

.ui-toast.error {
    --ui-tone: #ef4444;
    --ui-bg: #fef2f2;
    --ui-line: #fecaca;
    --ui-ink: #991b1b;
    --ui-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.6 3.6l4.8 4.8M8.4 3.6l-4.8 4.8' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
}

.ui-toast.warn {
    --ui-tone: #f59e0b;
    --ui-bg: #fffbeb;
    --ui-line: #fde68a;
    --ui-ink: #92400e;
    --ui-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 3.2v3.9' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3Ccircle cx='6' cy='9' r='.95' fill='%23fff'/%3E%3C/svg%3E");
}

.ui-toast.info {
    --ui-tone: #3b82f6;
    --ui-bg: #eff6ff;
    --ui-line: #bfdbfe;
    --ui-ink: #1e40af;
    --ui-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 5.4v3.3' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3Ccircle cx='6' cy='3.4' r='.95' fill='%23fff'/%3E%3C/svg%3E");
}

/* 出场：淡出 + 右移 + 高度/内边距同步塌陷
 * 四样一起走，所以是「一边滑走一边淡、下面几条一边平稳补位」，
 * 而不是先消失再让剩下的跳上来。
 *
 * ⚠️ max-height 必须 !important：起始值是 JS 写的 inline style（卡片真实高度），
 *    inline 优先级高于类选择器，不加 !important 就压不住它、高度塌不下去。 */
.ui-toast.out {
    opacity: 0;
    transform: translateX(22px) scale(.97);
    max-height: 0 !important;
    padding-top: 0;
    padding-bottom: 0;
    border-top-width: 0;
    border-bottom-width: 0;
}

@media (prefers-reduced-motion: reduce) {
    .ui-toast, .ui-toast::before { animation: none; }
    .ui-toast::after { animation: ui-toast-life var(--ui-toast-life, 2800ms) linear forwards; }
}
@media (max-width: 700px) {
    .ui-mask { align-items: flex-end; padding: 0; }
    .ui-card {
        max-width: 100%;
        border-radius: 18px 18px 0 0;
        animation: ui-up .24s cubic-bezier(.22, 1, .36, 1);
    }

    @keyframes ui-up {
        from { opacity: 0; transform: translateY(24px); }
        to { opacity: 1; transform: none; }
    }
}

/* ---------- 提交中：转圈 + 禁止点击 ----------
 * 规范：任何「点了要等接口」的按钮都要过一遍 window.SubmitGuard.start(btn) / .stop(btn)，
 * 它会贴上 .is-loading（禁止点击 + 转圈）。
 *
 * ⚠️ 按钮上的转圈用**绝对定位居中**、不参与排版：
 *    原先它是 inline-block + margin-left，会把按钮撑宽 —— 加/去 loading 时
 *    按钮一跳、旁边的一排跟着位移，很难看。现在按钮宽高纹丝不动。
 *    文字用 -webkit-text-fill-color 隐藏（只作用于文字，不碰 border / background /
 *    background-image），所以 currentColor 画的转圈依然是有色的。 */
.is-loading {
    pointer-events: none;
    cursor: progress;
}

/* 非按钮元素（卡片等）：转圈跟着文字流走 */
.is-loading::after {
    content: "";
    display: inline-block;
    flex: 0 0 auto;
    width: .9em;
    height: .9em;
    margin-left: .42em;
    vertical-align: -.1em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    opacity: .85;
    animation: btn-spin .62s linear infinite;
}

/* 按钮：转圈脱离文档流，居中盖在文字上 */
.btn.is-loading,
button.is-loading,
input.is-loading[type="submit"],
input.is-loading[type="button"] {
    position: relative;
    -webkit-text-fill-color: transparent;
}

.btn.is-loading::after,
button.is-loading::after,
input.is-loading[type="submit"]::after,
input.is-loading[type="button"]::after {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -0.45em 0 0 -0.45em;
    vertical-align: baseline;
}

@keyframes btn-spin {
    to { transform: rotate(360deg); }
}

/* ---------- 移动端（≤1024px）----------
 * ① 禁止双击缩放 / 双击变大：`touch-action: manipulation` 会与子元素的 touch-action 取交集，
 *    写在 html / body 上就是全局生效（并且保留双指缩放，不影响无障碍）。
 *    同时关掉 iOS 的「文字自动放大」（text-size-adjust），横竖屏切换时字号不再跳。
 * ② 输入框禁止聚焦放大：iOS Safari 对字号 < 16px 的表单控件会在聚焦时自动放大页面，
 *    唯一可靠的规避方式就是控件字号 ≥ 16px（!important 用来压住各页面里更“紧凑”的输入框样式）。
 * ③ 轻提示（.ui-toasts）保持右上角，但**左右各留 10px 边距**（手机上不贴边、不整宽）。 */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html, body { touch-action: manipulation; }

@media (max-width: 1024px) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="button"]):not([type="submit"]):not([type="reset"]),
    select,
    textarea {
        font-size: 16px !important;
    }

    .ui-toasts {
        top: 10px;
        right: 10px;
        left: 10px;
        align-items: stretch;
        gap: 8px;
    }

    .ui-toast {
        width: 100%;
        max-width: none;
        padding: 13px 15px 15px;
        font-size: 15px;
        border-radius: 14px;
    }

    .ui-toast.out { transform: translateX(18px); }
}

/* ---------- 手机号 / 邮箱 / 地址不要被浏览器自动识别成可点链接 ----------
 * 配合每个页面 <head> 里的 `<meta name="format-detection" content="telephone=no, email=no, address=no">`：
 * iOS Safari 会把纯文本里的手机号（比如账号字段是手机号时）做成「点一下打电话」的链接，
 * 那个链接不进 DOM、CSS 改不到，那个 meta 是唯一可靠的关法；下面这条再兜一层：
 * 万一某个内核还是生成了 <a href="tel:...">，让它点不动、也不变色 / 不下划线。 */
a[href^="tel:"],
a[href^="mailto:"] {
    color: inherit;
    text-decoration: none;
    pointer-events: none;
    cursor: text;
}
