.rp-sync-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    color: #111827;
}

.rp-sync-modal.is-open {
    display: block;
}

.rp-sync-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(17, 24, 39, 0.32);
    backdrop-filter: blur(5px);
}

.rp-sync-modal__panel {
    position: absolute;
    left: 50%;
    top: 50%;
    /* 没有这一行，窄屏那条 width:100% 是"内容宽 100%"，再叠上左右各 16px 内边距
       就变成 422px > 390px 视口：右侧被裁、"返回"按钮被顶到屏幕外。
       全站只有记忆编辑框写过 box-sizing，所以这个面板一直是 content-box。 */
    box-sizing: border-box;
    width: min(92vw, 420px);
    max-height: min(86vh, 640px);
    max-height: min(86dvh, 640px);   /* 手机地址栏会吃掉 vh 的一截，可见高度用 dvh；旧浏览器回退上一行 */
    overflow-y: auto;
    transform: translate(-50%, -50%);
    border: 1px solid rgba(229, 231, 235, 0.95);
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.98);
    box-shadow: 0 24px 70px -34px rgba(15, 23, 42, 0.55);
    padding: 18px;
}

/* /sync-restore 是独立页：worker 给的 HTML 里除了这个面板什么都没有。
   沿用窄屏的"底部抽屉"时，上面整片全是白的，用户看到的就是"白屏"；
   这里让它铺一层站点自己的底色、面板居中，看起来是一页而不是一条悬空的白条。
   选择器比窄屏那条多一级类，所以不用 !important 也能盖住 left/right/bottom 那套。 */
.rp-sync-modal--restore {
    background: linear-gradient(168deg, #faf8f3 0%, #eef0fb 46%, #e4eef1 100%);
}
.rp-sync-modal--restore .rp-sync-modal__backdrop {
    display: none;
}
.rp-sync-modal--restore .rp-sync-modal__panel {
    left: 50%;
    right: auto;
    top: 50%;
    bottom: auto;
    transform: translate(-50%, -50%);
    width: min(92vw, 460px);
    border: 1px solid rgba(229, 231, 235, 0.95);
    border-radius: 18px;
    box-shadow: 0 24px 70px -34px rgba(15, 23, 42, 0.55);
}
:root[data-app-theme="dark"] .rp-sync-modal--restore { background: var(--night-canvas); }
/* 关闭按钮基础样式是 32×32 的圆形 ×，恢复页把它换成"返回"两个字后放不下，
   实测直接竖排成两行（截图里是「返/回」）。这里放开宽度并收成正常胶囊按钮。 */
.rp-sync-modal--restore .rp-sync-modal__close {
    width: auto;
    height: auto;
    min-width: 58px;
    padding: 7px 14px;
    border-radius: 999px;
    font-size: 13px;
    line-height: 1.2;
    white-space: nowrap;
}

.rp-sync-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.rp-sync-modal__eyebrow {
    color: #2563eb;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.rp-sync-modal__title {
    margin: 0;
    color: #111827;
    font-size: 20px;
    line-height: 1.2;
    font-weight: 800;
}

.rp-sync-modal__title-main {
    display: block;
    color: #111827;
    font-size: 18px;
    line-height: 1.1;
    font-weight: 800;
    letter-spacing: 0;
}

.rp-sync-modal__title-sub {
    display: block;
    margin-bottom: 3px;
    color: #2563eb;
    font-size: 11px;
    line-height: 1.2;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.rp-sync-modal__close {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border: 1px solid rgba(229, 231, 235, 0.95);
    border-radius: 999px;
    background: #fff;
    color: #6b7280;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

@media (hover:hover) and (pointer:fine){.rp-sync-modal__close:hover {
    color: #111827;
    border-color: rgba(156, 163, 175, 0.9);
}}

.rp-sync-main-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

.rp-sync-action-button {
    min-width: 0;
    height: 42px;
    border: 1px solid rgba(229, 231, 235, 0.95);
    border-radius: 12px;
    background: #fff;
    color: #374151;
    font-size: 15px;
    font-weight: 800;
    cursor: pointer;
    transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
}

@media (hover:hover) and (pointer:fine){.rp-sync-action-button:hover {
    border-color: rgba(59, 130, 246, 0.35);
    background: #f8fafc;
    color: #1d4ed8;
}}

.rp-sync-action-button.is-primary {
    border-color: rgba(37, 99, 235, 0.25);
    background: #eff6ff;
    color: #1d4ed8;
}

.rp-sync-action-button:disabled,
.rp-sync-modal__button:disabled {
    opacity: 0.62;
    cursor: wait;
}

.rp-sync-modal__status {
    min-height: 22px;
    margin: 14px 0 10px;
    color: #4b5563;
    font-size: 13px;
    line-height: 1.55;
}

.rp-sync-progress {
    width: 100%;
    height: 8px;
    overflow: hidden;
    border-radius: 999px;
    background: #e5e7eb;
}

.rp-sync-progress__bar {
    width: 0;
    height: 100%;
    border-radius: inherit;
    background: #2563eb;
    transition: width 0.28s ease;
}

.rp-sync-progress__value {
    margin-top: 6px;
    text-align: right;
    color: #2563eb;
    font-size: 12px;
    font-weight: 800;
}

.rp-sync-modal__intro {
    margin: 0 0 10px;
    color: #64748b;
    font-size: 13px;
    line-height: 1.55;
}

.rp-sync-password-field {
    display: grid;
    gap: 7px;
    margin-top: 14px;
    color: #475569;
    font-size: 13px;
    font-weight: 800;
}

.rp-sync-modal__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
}

.rp-sync-modal__button {
    min-width: 78px;
    border: 1px solid rgba(209, 213, 219, 0.95);
    border-radius: 999px;
    background: #fff;
    color: #374151;
    font-size: 13px;
    font-weight: 800;
    padding: 8px 12px;
    cursor: pointer;
}

@media (hover:hover) and (pointer:fine){.rp-sync-modal__button:hover {
    border-color: rgba(59, 130, 246, 0.35);
    color: #1d4ed8;
}}

.rp-sync-modal__button.is-primary {
    border-color: rgba(37, 99, 235, 0.25);
    background: #eff6ff;
    color: #1d4ed8;
}

.rp-sync-password-field input {
    width: 100%;
    border: 1px solid rgba(209, 213, 219, 0.95);
    border-radius: 12px;
    background: #fff;
    color: #111827;
    font-size: 15px;
    line-height: 1.4;
    padding: 11px 12px;
    outline: none;
    transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.rp-sync-password-field input:focus {
    border-color: rgba(37, 99, 235, 0.52);
    box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11);
}

.rp-sync-password-status {
    min-height: 20px;
    margin: 10px 0 0;
    color: #475569;
    font-size: 13px;
    line-height: 1.5;
}

@media (max-width: 640px) {
    .rp-sync-modal__backdrop {
        background: rgba(17, 24, 39, 0.36);
    }
    .rp-sync-modal__panel {
        left: 0;
        right: 0;
        bottom: 0;
        top: auto;
        width: 100%;
        max-height: min(82vh, 620px);
        max-height: min(82dvh, 620px);   /* 同上：手机可见高度用 dvh */
        transform: none;
        border-right: 0;
        border-bottom: 0;
        border-left: 0;
        border-radius: 20px 20px 0 0;
        padding: 16px 16px max(16px, env(safe-area-inset-bottom));
    }

    .rp-sync-modal__header {
        margin-bottom: 12px;
    }

    .rp-sync-modal__title {
        font-size: 18px;
    }

    .rp-sync-modal__title-main {
        font-size: 17px;
    }

    .rp-sync-modal__title-sub {
        font-size: 10px;
    }

    .rp-sync-main-actions {
        gap: 8px;
    }

    .rp-sync-action-button {
        height: 42px;
        font-size: 14px;
        border-radius: 12px;
    }

}

/* 夜间模式：云同步弹窗是魔改自带 UI，作者的主题规则不覆盖它，这里按作者变量补齐 */
/* 自动同步：主按钮之下一档，但要读得出"开着没、多久一次、下次几点"。
   卡片分三层：抬头（名字 + 状态徽标）／两行整行可点的设置项／一行说明。 */
.rp-sync-auto {
    box-sizing: border-box;
    margin: 12px 0 2px;
    padding: 10px 12px 11px;
    border: 1px solid rgba(15, 23, 42, 0.1);
    border-radius: 12px;
    background: linear-gradient(180deg, rgba(79, 70, 229, 0.045), rgba(15, 23, 42, 0.02));
}
.rp-sync-auto__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}
.rp-sync-auto__title {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.02em;
}
.rp-sync-auto__state {
    box-sizing: border-box;
    flex: 0 0 auto;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid rgba(15, 23, 42, 0.14);
    background: rgba(15, 23, 42, 0.05);
    font-size: 11px;
    line-height: 1.6;
    opacity: 0.85;
}
.rp-sync-auto__state.is-on {
    border-color: rgba(79, 70, 229, 0.34);
    background: rgba(79, 70, 229, 0.12);
    color: #4338ca;
    opacity: 1;
}
.rp-sync-auto__state.is-paused {
    border-color: rgba(180, 83, 9, 0.34);
    background: rgba(245, 158, 11, 0.14);
    color: #92400e;
}
.rp-sync-auto__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    min-height: 34px;
    padding: 2px 0;
    cursor: pointer;
}
.rp-sync-auto__row + .rp-sync-auto__row { border-top: 1px dashed rgba(15, 23, 42, 0.08); }
.rp-sync-auto__label {
    flex: 1 1 auto;
    font-size: 13px;
    font-weight: 500;
}
/* 开关：仍是原生 checkbox（键盘与读屏照常），只是把方框画成轨道 + 滑块 */
.rp-sync-auto__switch {
    appearance: none;
    -webkit-appearance: none;
    box-sizing: border-box;
    flex: 0 0 auto;
    position: relative;
    width: 42px;
    height: 24px;
    margin: 0;
    border: 1px solid rgba(15, 23, 42, 0.16);
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.14);
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease;
}
.rp-sync-auto__switch::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.28);
    transition: transform 0.18s ease;
}
.rp-sync-auto__switch:checked {
    border-color: rgba(67, 56, 202, 0.6);
    background: #4f46e5;
}
.rp-sync-auto__switch:checked::after { transform: translateX(18px); }
.rp-sync-auto__switch:focus-visible { outline: 2px solid #4f46e5; outline-offset: 2px; }
.rp-sync-auto__interval {
    box-sizing: border-box;
    flex: 0 0 auto;
    max-width: 46%;
    font: inherit;
    font-size: 12px;
    padding: 4px 8px;
    border: 1px solid rgba(15, 23, 42, 0.16);
    border-radius: 8px;
    background: #fff;
    color: inherit;
}
.rp-sync-auto__interval:disabled { opacity: 0.5; }
.rp-sync-auto-line {
    margin: 7px 0 0;
    font-size: 12px;
    line-height: 1.55;
    opacity: 0.72;
}
.rp-sync-auto-line.is-warn {
    color: #b42318;
    opacity: 1;
}

:root[data-app-theme="dark"] .rp-sync-modal { color: var(--night-text); }
:root[data-app-theme="dark"] .rp-sync-modal__backdrop { background: rgba(0, 0, 0, 0.62); }
:root[data-app-theme="dark"] .rp-sync-modal__panel {
    border-color: var(--night-border);
    background: var(--night-raised);
    box-shadow: 0 24px 70px -34px rgba(0, 0, 0, 0.85);
}
:root[data-app-theme="dark"] .rp-sync-modal__title,
:root[data-app-theme="dark"] .rp-sync-modal__title-main { color: var(--night-text); }
:root[data-app-theme="dark"] .rp-sync-modal__eyebrow,
:root[data-app-theme="dark"] .rp-sync-modal__title-sub,
:root[data-app-theme="dark"] .rp-sync-progress__value { color: #93c5fd; }
:root[data-app-theme="dark"] .rp-sync-modal__close,
:root[data-app-theme="dark"] .rp-sync-modal__button,
:root[data-app-theme="dark"] .rp-sync-action-button {
    border-color: var(--night-border);
    background: var(--night-field);
    color: var(--night-text);
}
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rp-sync-modal__close:hover,
:root[data-app-theme="dark"] .rp-sync-modal__button:hover,
:root[data-app-theme="dark"] .rp-sync-action-button:hover {
    border-color: rgba(134, 173, 244, 0.5);
    background: rgba(30, 58, 138, 0.28);
    color: #dbeafe;
}}
:root[data-app-theme="dark"] .rp-sync-modal__button.is-primary,
:root[data-app-theme="dark"] .rp-sync-action-button.is-primary {
    border-color: rgba(99, 102, 241, 0.55);
    background: rgba(30, 58, 138, 0.42);
    color: #bfdbfe;
}
:root[data-app-theme="dark"] .rp-sync-modal__status,
:root[data-app-theme="dark"] .rp-sync-modal__intro,
:root[data-app-theme="dark"] .rp-sync-password-field,
:root[data-app-theme="dark"] .rp-sync-password-status { color: #a1a1aa; }
:root[data-app-theme="dark"] .rp-sync-progress { background: #3f3f46; }
:root[data-app-theme="dark"] .rp-sync-progress__bar { background: #60a5fa; }
:root[data-app-theme="dark"] .rp-sync-password-field input {
    border-color: var(--night-border);
    background: var(--night-field);
    color: var(--night-text);
}
:root[data-app-theme="dark"] .rp-sync-password-field input:focus {
    border-color: #86adf4;
    box-shadow: 0 0 0 3px rgba(120, 166, 244, 0.16);
}

/* ===== 更新角色卡弹窗（补丁：build_card_update.mjs） =====
   面板/头部/表格底色走作者的 bg-white + border-gray-* 工具类，夜间由作者主题接管；
   这里只补自建类：状态色、表格强调、下拉、按钮与撤销浮条，并给这些自造色配夜间版本。 */
.rph-update-version {
    flex: none;
    font-size: 11px;
    font-weight: 600;
    color: #1d4ed8;
    background: #eff6ff;
    border: 1px solid #bfdbfe;
    border-radius: 999px;
    padding: 2px 9px;
}

/* 新版作者注释：整段列出来，一行都不许被裁掉。
   以前只有"看着像版本号的那一行"贴在标题旁，多行注释其余部分在界面上一个字都看不见。 */
.rph-update-note-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 30vh;
    overflow-y: auto;
    padding: 9px 11px;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    background: #f8fafc;
    font-size: 12.5px;
    line-height: 1.6;
    color: #334155;
    overflow-wrap: anywhere;
}

.rph-update-note-body p {
    margin: 0;
    white-space: pre-wrap;
}

.rph-update-close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 10px;
    color: #9ca3af;
    border: 1px solid transparent;
    cursor: pointer;
    transition: all .15s ease;
    -webkit-tap-highlight-color: transparent;
    outline: none;
}

.rph-update-close:focus-visible { box-shadow: 0 0 0 3px rgba(99, 102, 241, .28); }
.rph-update-close:active { transform: scale(.94); color: #1f2937; background: #e5e7eb; }

@media (hover:hover) and (pointer:fine){.rph-update-close:hover {
    color: #374151;
    background: #f3f4f6;
    border-color: #e5e7eb;
}}

.rph-update-keep {
    border: 1px solid rgba(16, 185, 129, .26);
    background: rgba(16, 185, 129, .06);
    border-radius: 14px;
    padding: 12px;
}

.rph-update-keep-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: 700;
    color: #047857;
}

.rph-update-keep-head svg { width: 15px; height: 15px; }

.rph-update-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 8px 4px;
    border-radius: 11px;
    background: rgba(255, 255, 255, .72);
    border: 1px solid rgba(16, 185, 129, .16);
}

.rph-update-stat-main {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 4px;
    white-space: nowrap;
}

.rph-update-stat b {
    font-size: 22px;
    line-height: 1.1;
    font-weight: 800;
    color: #065f46;
    font-variant-numeric: tabular-nums;
}

.rph-update-stat span { font-size: 12px; color: #059669; }

.rph-update-stat em {
    font-style: normal;
    font-size: 10px;
    line-height: 1.3;
    margin-top: 2px;
    color: rgba(5, 150, 105, .8);
    text-align: center;
}

.rph-update-label {
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
    color: #6b7280;
}

.rph-update-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12.5px;
}

.rph-update-table th {
    text-align: right;
    font-size: 11px;
    font-weight: 600;
    color: #9ca3af;
    padding: 0 8px 6px;
}

.rph-update-table th:first-child { text-align: left; }

.rph-update-table td {
    padding: 7px 8px;
    border-top: 1px solid #f3f4f6;
    color: #6b7280;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.rph-update-table td:first-child {
    text-align: left;
    color: #374151;
    font-weight: 500;
}

.rph-update-table tr.is-changed td { background: rgba(59, 130, 246, .055); }
.rph-update-table tr.is-changed td:first-child { color: #1e40af; }
.rph-update-table tr.is-changed td:last-child { color: #1d4ed8; font-weight: 700; }

.rph-update-tpl {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 5px 0;
}

.rph-update-tpl-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    font-weight: 600;
    color: #374151;
}

.rph-update-select {
    flex: 0 1 auto;
    max-width: 62%;
    font-size: 12px;
    color: #374151;
    background: #fff;
    border: 1px solid #e6edf7;
    border-radius: 10px;
    padding: 7px 9px;
    outline: none;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.rph-update-select:focus-visible {
    border-color: #5281ec;
    box-shadow: 0 0 0 3px rgba(120, 166, 244, .18);
}

.rph-update-warn {
    margin-top: 8px;
    padding: 9px 11px;
    border-radius: 12px;
    font-size: 12px;
    line-height: 1.65;
    color: #92400e;
    background: rgba(245, 158, 11, .09);
    border: 1px solid rgba(245, 158, 11, .26);
}

/* —— 封面三格选择器 —— */
.rph-cover-sub { margin: 0 0 12px; font-size: 12px; line-height: 1.5; color: #9ca3af; }
.rph-cover-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; align-items: stretch; }
.rph-cover-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    min-height: 128px;
    padding: 13px 8px 11px;
    font: inherit;
    text-align: center;
    background: #fbfbfd;
    border: 1.5px solid #e5e7eb;
    border-radius: 14px;
    cursor: pointer;
    transition: border-color .16s, background .16s, box-shadow .16s;
    -webkit-tap-highlight-color: transparent;
}
@media (hover:hover) and (pointer:fine){.rph-cover-tile:hover { border-color: #c7d2fe; background: #f8faff; }}
.rph-cover-tile:active { transform: scale(.985); border-color: #a5b4fc; }
.rph-cover-tile.is-on { border-color: #2563eb; background: #eff6ff; box-shadow: 0 0 0 3px rgba(37, 99, 235, .13); }
.rph-cover-tile:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
.rph-cover-th { flex: none; width: 72px; height: 72px; border-radius: 11px; object-fit: cover; background: #e5e7eb; border: 1px solid rgba(0, 0, 0, .06); }
.rph-cover-lb { display: flex; align-items: center; min-height: 34px; font-size: 12px; font-weight: 600; line-height: 1.35; color: #4b5563; }
.rph-cover-tile.is-on .rph-cover-lb { color: #1d4ed8; }
.rph-cover-tick { position: absolute; top: -8px; right: 10px; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; color: #fff; background: #2563eb; box-shadow: 0 2px 6px rgba(37, 99, 235, .45); }
.rph-cover-why { font-size: 11px; line-height: 1.35; color: #9ca3af; }
.rph-cover-tile.is-off { background: #fafafa; border-style: dashed; cursor: not-allowed; }
.rph-cover-tile.is-off .rph-cover-th { filter: grayscale(1); opacity: .42; }
.rph-cover-tile.is-off .rph-cover-lb { font-weight: 500; color: #b6bcc6; }
.rph-cover-size { margin-top: 12px; padding: 4px 12px 10px; font-size: 11.5px; line-height: 1.55; color: #92400e; background: #fffbeb; border: 1px solid #fde68a; border-radius: 12px; }
.rph-cover-size-line { margin: 8px 0 0; font-weight: 700; }
.rph-cover-size-opt { display: flex; align-items: center; gap: 9px; min-height: 44px; margin-top: 2px; font-weight: 600; color: #7c2d12; cursor: pointer; user-select: none; }
.rph-cover-size-opt input { flex: none; width: 16px; height: 16px; accent-color: #d97706; cursor: pointer; }
.rph-cover-fine { margin: 0; font-size: 10.5px; color: #b45309; opacity: .85; }

.rph-update-btn {
    min-width: 88px;
    padding: 9px 16px;
    font-size: 13px;
    font-weight: 600;
    color: #4b5563;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    cursor: pointer;
    transition: all .15s ease;
}

@media (hover:hover) and (pointer:fine){.rph-update-btn:hover { color: #111827; border-color: #d1d5db; background: #f9fafb; }}
.rph-update-btn:active { transform: scale(.97); }

.rph-update-btn--primary {
    color: #fff;
    background: #2563eb;
    border-color: #2563eb;
    box-shadow: 0 8px 18px -8px rgba(37, 99, 235, .7);
}

@media (hover:hover) and (pointer:fine){.rph-update-btn--primary:hover { color: #fff; background: #1d4ed8; border-color: #1d4ed8; }}

/* 更新完成提示：顶部居中的悬浮小卡（不通栏、不做药丸），10 秒自动收起，绝不压住输入区 */
.rph-update-undo-chip {
    position: fixed;
    top: calc(10px + env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 79;
    width: min(560px, calc(100vw - 20px));
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 9px 9px 12px;
    border-radius: 14px;
    font-size: 13px;
    line-height: 1.45;
    color: #14532d;
    background: rgba(255, 255, 255, .97);
    border: 1px solid #d1fae5;
    backdrop-filter: blur(12px);
    box-shadow: 0 12px 30px -14px rgba(15, 23, 42, .38), 0 0 0 4px rgba(16, 185, 129, .07);
    animation: rphUpdateChipIn .24s cubic-bezier(.2, .9, .3, 1);
    transition: opacity .28s ease, transform .28s ease;
}

.rph-update-chip-ico { flex: none; width: 20px; height: 20px; color: #059669; }

.rph-update-chip-text {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rph-update-undo-chip.is-out { opacity: 0; transform: translate(-50%, -16px); }

@keyframes rphUpdateChipIn {
    from { opacity: 0; transform: translate(-50%, -16px); }
    to { opacity: 1; transform: translate(-50%, 0); }
}

.rph-update-undo-btn {
    flex: none;
    padding: 5px 9px;
    font-size: 12.5px;
    font-weight: 700;
    color: #047857;
    background: transparent;
    border: none;
    border-radius: 9px;
    cursor: pointer;
    transition: .15s;
}

@media (hover:hover) and (pointer:fine){.rph-update-undo-btn:hover { background: rgba(16, 185, 129, .13); }}

.rph-update-undo-close {
    flex: none;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    color: #9ca3af;
    background: transparent;
    border: none;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}

@media (hover:hover) and (pointer:fine){.rph-update-undo-close:hover { color: #374151; background: #f3f4f6; }}

/* —— 夜间：工具类由作者主题接管，这几处自造色要自己跟上 —— */
:root[data-app-theme="dark"] .rph-update-version { color: #bfdbfe; background: rgba(37, 99, 235, .22); border-color: rgba(96, 165, 250, .3); }
:root[data-app-theme="dark"] .rph-update-note-body { color: #d4d4d8; background: rgba(32, 32, 32, .7); border-color: var(--night-border); }
:root[data-app-theme="dark"] .rph-update-close { color: #8f8f96; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-close:hover { color: #e4e4e7; background: var(--night-raised); border-color: var(--night-border); }}
:root[data-app-theme="dark"] .rph-update-close:active { color: #f4f4f5; background: #3f3f46; }
:root[data-app-theme="dark"] .rph-update-keep { border-color: rgba(16, 185, 129, .34); background: rgba(6, 95, 70, .18); }
:root[data-app-theme="dark"] .rph-update-keep-head { color: #6ee7b7; }
:root[data-app-theme="dark"] .rph-update-stat { background: rgba(32, 32, 32, .7); border-color: rgba(16, 185, 129, .22); }
:root[data-app-theme="dark"] .rph-update-stat b { color: #6ee7b7; }
:root[data-app-theme="dark"] .rph-update-stat span { color: #34d399; }
:root[data-app-theme="dark"] .rph-update-stat em { color: rgba(52, 211, 153, .75); }
:root[data-app-theme="dark"] .rph-update-label { color: #a1a1aa; }
:root[data-app-theme="dark"] .rph-update-table th { color: #8f8f96; }
:root[data-app-theme="dark"] .rph-update-table td { border-top-color: var(--night-border); color: #a1a1aa; }
:root[data-app-theme="dark"] .rph-update-table td:first-child { color: #d4d4d8; }
:root[data-app-theme="dark"] .rph-update-table tr.is-changed td { background: rgba(59, 130, 246, .12); }
:root[data-app-theme="dark"] .rph-update-table tr.is-changed td:first-child { color: #bfdbfe; }
:root[data-app-theme="dark"] .rph-update-table tr.is-changed td:last-child { color: #dbeafe; }
:root[data-app-theme="dark"] .rph-update-tpl-name { color: #e4e4e7; }
:root[data-app-theme="dark"] .rph-update-select { background: var(--night-field); border-color: var(--night-border); color: var(--night-text); }
:root[data-app-theme="dark"] .rph-update-warn { color: #fcd34d; background: rgba(245, 158, 11, .12); border-color: rgba(245, 158, 11, .28); }
:root[data-app-theme="dark"] .rph-cover-sub { color: #8f8f96; }
:root[data-app-theme="dark"] .rph-cover-tile { background: #26262b; border-color: #3f3f46; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-cover-tile:hover { border-color: #4f46e5; background: #2a2a31; }}
:root[data-app-theme="dark"] .rph-cover-tile:active { border-color: #6366f1; }
:root[data-app-theme="dark"] .rph-cover-tile.is-on { border-color: #3b82f6; background: rgba(37, 99, 235, .15); box-shadow: 0 0 0 3px rgba(59, 130, 246, .16); }
:root[data-app-theme="dark"] .rph-cover-lb { color: #d4d4d8; }
:root[data-app-theme="dark"] .rph-cover-tile.is-on .rph-cover-lb { color: #bfdbfe; }
:root[data-app-theme="dark"] .rph-cover-tile.is-off { background: #232327; border-color: #3a3a41; }
:root[data-app-theme="dark"] .rph-cover-tile.is-off .rph-cover-lb { color: #71717a; }
:root[data-app-theme="dark"] .rph-cover-th { background: #3f3f46; border-color: rgba(255, 255, 255, .08); }
:root[data-app-theme="dark"] .rph-cover-size { color: #fcd34d; background: rgba(120, 53, 15, .22); border-color: rgba(217, 119, 6, .36); }
:root[data-app-theme="dark"] .rph-cover-size label { color: #fde68a; }
:root[data-app-theme="dark"] .rph-cover-size input { accent-color: #fbbf24; color-scheme: dark; }
:root[data-app-theme="dark"] .rph-cover-fine { color: #fbbf24; opacity: .8; }
:root[data-app-theme="dark"] .rph-update-btn { background: var(--night-field); border-color: var(--night-border); color: #d4d4d8; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-btn:hover { background: var(--night-raised); border-color: #52525b; color: #f4f4f5; }}
:root[data-app-theme="dark"] .rph-update-btn--primary { background: #2563eb; border-color: #3b82f6; color: #fff; box-shadow: 0 8px 18px -10px rgba(59, 130, 246, .8); }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-btn--primary:hover { background: #1d4ed8; border-color: #60a5fa; }}
/* 窄屏（≤430pt）撤销条改两行：文字一行、按钮一行，绝不截字 */
@media (max-width: 430px) {
    .rph-update-undo-chip { flex-wrap: wrap; gap: 6px 8px; padding: 10px 12px; border-radius: 16px; }
    .rph-update-chip-text { flex: 1 1 calc(100% - 34px); }
    .rph-update-undo-btn { flex: 1 1 auto; text-align: center; }
}

:root[data-app-theme="dark"] .rph-update-undo-chip { background: rgba(24, 34, 31, .97); border-color: rgba(16, 185, 129, .32); color: #d1fae5; box-shadow: 0 12px 30px -16px rgba(0, 0, 0, .7), 0 0 0 4px rgba(16, 185, 129, .09); }
:root[data-app-theme="dark"] .rph-update-chip-ico { color: #34d399; }
:root[data-app-theme="dark"] .rph-update-undo-btn { color: #6ee7b7; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-undo-btn:hover { background: rgba(16, 185, 129, .18); }}
:root[data-app-theme="dark"] .rph-update-undo-close { color: #7c8a84; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-undo-close:hover { color: #e4e4e7; background: var(--night-raised); }}
:root[data-app-theme="dark"] .rph-update-undo-chip { background: rgba(32, 32, 32, .94); border-color: var(--night-border); color: #d4d4d8; box-shadow: 0 18px 30px -16px rgba(0, 0, 0, .7); }
:root[data-app-theme="dark"] .rph-update-undo-btn { background: rgba(37, 99, 235, .22); border-color: rgba(96, 165, 250, .34); color: #bfdbfe; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-undo-btn:hover { background: rgba(37, 99, 235, .34); }}
:root[data-app-theme="dark"] .rph-update-undo-close { color: #8f8f96; }
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-update-undo-close:hover { color: #e4e4e7; background: var(--night-raised); }}
/* —— 手机端（<640px）：弹窗改底部抽屉 ——
   不用垂直居中：内容超高时 flex 居中会把顶部裁掉且滚不回来；
   高度用 dvh（随地址栏伸缩），vh 由模板上的类做旧浏览器兜底。 */
@media (max-width: 639.98px) {
    .rph-update-panel {
        border-left: 0;
        border-right: 0;
        border-bottom: 0;
        border-radius: 18px 18px 0 0;
        box-shadow: 0 -18px 40px -20px rgba(15, 23, 42, .45);
    }

    .rph-update-panel::before {
        content: "";
        position: absolute;
        top: 7px;
        left: 50%;
        transform: translateX(-50%);
        width: 38px;
        height: 4px;
        border-radius: 999px;
        background: rgba(148, 163, 184, .5);
    }

    .rph-update-panel > header { padding: 16px 14px 10px; }
    .rph-update-panel > header > div > div:last-child { font-size: 15px; gap: 6px; }
    .rph-update-panel > div { padding: 12px 14px; }
    .rph-update-panel > footer { padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px)); }
    .rph-update-panel footer .rph-update-btn { flex: 1 1 0; min-width: 0; min-height: 46px; padding: 12px 16px; }
    .rph-update-stat b { font-size: 18px; }
    .rph-update-table { font-size: 12px; }
    .rph-update-table td { padding: 6px 6px; }

    /* 面板配对在窄屏改成上下排，下拉框整行可点，避免长名字被挤掉 */
    .rph-update-tpl { flex-direction: column; align-items: stretch; gap: 5px; padding: 7px 0; }
    .rph-update-select { max-width: none; width: 100%; }
    .rph-cover-tiles { gap: 8px; }
    .rph-cover-tile { min-height: 118px; padding: 11px 6px 9px; gap: 6px; border-radius: 12px; }
    .rph-cover-th { width: 60px; height: 60px; border-radius: 10px; }
    .rph-cover-lb { font-size: 11.5px; min-height: 30px; }
    .rph-cover-why { font-size: 10.5px; }
    .rph-cover-tick { top: -7px; right: 7px; width: 18px; height: 18px; }
    .rph-cover-size { padding: 9px 10px; gap: 6px; }
}

:root[data-app-theme="dark"] .rph-update-panel::before { background: rgba(148, 163, 184, .35); }

/* ===== 记忆补录的重试状态行（build_memory_backfill）=====
   作者这套弹窗只有浅色态（index.html 里没有一条 dark: 类），所以这里不写夜间覆写，
   免得多出一块"深色卡在白面板里"的假适配。 */
.rph-retry {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 11px 12px;
    border: 1px solid rgba(217, 119, 6, .28);
    border-radius: 14px;
    background: linear-gradient(180deg, rgba(255, 251, 235, .92), rgba(255, 255, 255, .96));
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05), inset 0 1px 0 rgba(255, 255, 255, .7);
    animation: rph-retry-in .26s cubic-bezier(.2, .7, .3, 1) both;
}

@keyframes rph-retry-in {
    from { opacity: 0; transform: translateY(-6px); }
    to { opacity: 1; transform: none; }
}

.rph-retry-dot {
    flex: none;
    width: 9px;
    height: 9px;
    margin-top: 5px;
    border-radius: 50%;
    background: #d97706;
    animation: rph-retry-pulse 1.6s ease-out infinite;
}

@keyframes rph-retry-pulse {
    0% { box-shadow: 0 0 0 0 rgba(217, 119, 6, .45); }
    70% { box-shadow: 0 0 0 7px rgba(217, 119, 6, 0); }
    100% { box-shadow: 0 0 0 0 rgba(217, 119, 6, 0); }
}

.rph-retry-body { flex: 1 1 auto; min-width: 0; }
.rph-retry-main { margin: 0; font-size: 13px; line-height: 1.55; color: #3f3f46; }
.rph-retry-main b { color: #1f2937; font-weight: 700; }
.rph-retry-main em {
    font-style: normal;
    font-weight: 700;
    color: #b45309;
    font-variant-numeric: tabular-nums;
}
.rph-retry-when { white-space: nowrap; }
.rph-retry-why {
    margin: 4px 0 0;
    font-size: 12px;
    line-height: 1.6;
    color: #78716c;
    overflow-wrap: anywhere;
}
.rph-retry-fine { margin: 5px 0 0; font-size: 11px; line-height: 1.5; color: #a8a29e; }
.rph-retry-bar {
    margin-top: 8px;
    height: 3px;
    border-radius: 999px;
    background: rgba(217, 119, 6, .14);
    overflow: hidden;
}
.rph-retry-bar > i {
    display: block;
    position: relative;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, #f59e0b, #2563eb);
    transition: width .2s linear;
    overflow: hidden;
}
.rph-retry-bar > i::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .6), transparent);
    transform: translateX(-100%);
    animation: rph-retry-sweep 1.4s linear infinite;
}

@keyframes rph-retry-sweep { to { transform: translateX(100%); } }

/* 发送中（没有倒计时可读）走一段来回的滑块，避免进度条钉死像卡住 */
.rph-retry-bar > u.rph-retry-slide {
    display: block;
    position: relative;
    height: 100%;
    width: 34%;
    border-radius: 999px;
    background: linear-gradient(90deg, rgba(245, 158, 11, .55), #2563eb);
    animation: rph-retry-slide 1.15s cubic-bezier(.55, .05, .45, .95) infinite alternate;
}

@keyframes rph-retry-slide {
    from { transform: translateX(0); }
    to { transform: translateX(194%); }
}

.rph-retry-dot.is-stopped { background: #78716c; animation: none; box-shadow: none; }

/* 夜间态：作者 theme.css 有 `:root[data-app-theme="dark"] :where(*,*::before,*::after){border-color:var(--night-border)}`，
   权重比单个类高，浅色态的边框色会被它压掉，所以这里必须用同等权重的前缀覆写。 */
:root[data-app-theme="dark"] .rph-retry {
    border-color: rgba(217, 119, 6, .46);
    background: linear-gradient(180deg, rgba(56, 42, 22, .9), var(--night-raised));
    box-shadow: 0 1px 2px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255, 255, 255, .07);
}
:root[data-app-theme="dark"] .rph-retry-main { color: var(--night-text); }
:root[data-app-theme="dark"] .rph-retry-main b { color: #f2efe9; }
:root[data-app-theme="dark"] .rph-retry-main em { color: #fbbf24; }
:root[data-app-theme="dark"] .rph-retry-why { color: var(--night-secondary); }
:root[data-app-theme="dark"] .rph-retry-fine { color: var(--night-muted); }
:root[data-app-theme="dark"] .rph-retry-bar { background: rgba(251, 191, 36, .2); }
:root[data-app-theme="dark"] .rph-retry-bar > i::after {
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .28), transparent);
}
:root[data-app-theme="dark"] .rph-retry-stop {
    color: var(--night-secondary);
    background: var(--night-field);
    border-color: rgba(217, 119, 6, .34);
}
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-retry-stop:hover { color: #f4f4f5; background: #2f2f2f; border-color: rgba(217, 119, 6, .5); }}
:root[data-app-theme="dark"] .rph-retry-dot.is-stopped { background: #a1a1aa; }

/* ===== 记忆正文就地编辑（build_memory_edit）=====
   跟作者那条「重试」按钮同一尺寸，避免同一排出现两种高度的控件 */
.rph-mem-edit-open {
    display: inline-flex;
    align-items: center;
    height: 28px;
    padding: 0 9px;
    font-size: 12px;
    font-weight: 500;
    color: #4b5563;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease;
    -webkit-tap-highlight-color: transparent;
}
@media (hover:hover) and (pointer:fine){.rph-mem-edit-open:hover:not(:disabled) { color: #2563eb; border-color: #bfdbfe; background: #f8fbff; }}
.rph-mem-edit-open:active:not(:disabled) { color: #1d4ed8; border-color: #93c5fd; background: #eff6ff; }
.rph-mem-edit-open:disabled { cursor: not-allowed; opacity: .5; }

.rph-mem-edited-tag {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 7px;
    font-size: 11px;
    color: #92400e;
    background: rgba(245, 158, 11, .1);
    border: 1px solid rgba(245, 158, 11, .26);
    border-radius: 999px;
}

.rph-mem-secondary-tag {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 7px;
    font-size: 11px;
    color: #4b5563;
    background: rgba(107, 114, 128, .1);
    border: 1px solid rgba(107, 114, 128, .24);
    border-radius: 999px;
}

.rph-mem-edit { display: flex; flex-direction: column; gap: 8px; }
.rph-mem-edit-area {
    width: 100%;
    box-sizing: border-box;
    min-height: 8.5em;
    max-height: 60vh;
    padding: 10px 12px;
    font-size: 13px;
    line-height: 1.75;
    color: #374151;
    background: #fcfcfd;
    border: 1px solid #d8dce3;
    border-radius: 10px;
    resize: vertical;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.rph-mem-edit-area:focus {
    outline: none;
    background: #fff;
    border-color: #93b4f5;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
}
.rph-mem-edit-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.rph-mem-edit-count { font-size: 11.5px; color: #6b7280; font-variant-numeric: tabular-nums; }
.rph-mem-edit-note { font-size: 11.5px; color: #9ca3af; }
.rph-mem-edit-spacer { flex: 1 1 auto; }
.rph-mem-edit-btn {
    min-width: 62px;
    height: 30px;
    padding: 0 12px;
    font-size: 12.5px;
    font-weight: 600;
    color: #4b5563;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 9px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease, transform .12s ease;
}
@media (hover:hover) and (pointer:fine){.rph-mem-edit-btn:hover:not(:disabled) { color: #111827; border-color: #d1d5db; background: #f9fafb; }}
.rph-mem-edit-btn:active:not(:disabled) { transform: scale(.97); }
.rph-mem-edit-btn:disabled { cursor: not-allowed; opacity: .55; }
.rph-mem-edit-btn.is-primary { color: #fff; background: #2563eb; border-color: #2563eb; }
@media (hover:hover) and (pointer:fine){.rph-mem-edit-btn.is-primary:hover:not(:disabled) { color: #fff; background: #1d4ed8; border-color: #1d4ed8; }}
.rph-mem-edit-area:focus-visible,
.rph-mem-edit-btn:focus-visible,
.rph-mem-edit-open:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }

/* 夜间：作者 theme.css 的 :where(*) 会压掉边框色，这里同权重覆写 */
:root[data-app-theme="dark"] .rph-mem-edit-open,
:root[data-app-theme="dark"] .rph-mem-edit-btn {
    color: var(--night-secondary);
    background: var(--night-field);
    border-color: var(--night-border);
}
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-mem-edit-open:hover:not(:disabled),
:root[data-app-theme="dark"] .rph-mem-edit-btn:hover:not(:disabled) { color: #f4f4f5; border-color: #585858; background: #2f2f2f; }}
:@media (hover:hover) and (pointer:fine){root[data-app-theme="dark"] .rph-mem-edit-btn.is-primary,
:root[data-app-theme="dark"] .rph-mem-edit-btn.is-primary:hover:not(:disabled) {
    color: #fff;
    background: var(--night-primary);
    border-color: var(--night-primary);
}}
:root[data-app-theme="dark"] .rph-mem-edit-area {
    color: var(--night-text);
    background: var(--night-field);
    border-color: var(--night-border);
}
:root[data-app-theme="dark"] .rph-mem-edit-area:focus { background: #1b1b1b; border-color: #4d6f9f; box-shadow: 0 0 0 3px rgba(66, 107, 216, .22); }
:root[data-app-theme="dark"] .rph-mem-edit-count { color: var(--night-muted); }
:root[data-app-theme="dark"] .rph-mem-edit-note { color: #8a8a8a; }
:root[data-app-theme="dark"] .rph-mem-edited-tag { color: #fbbf24; background: rgba(245, 158, 11, .14); border-color: rgba(245, 158, 11, .34); }
:root[data-app-theme="dark"] .rph-mem-secondary-tag { color: var(--night-secondary); background: rgba(156, 163, 175, .12); border-color: rgba(156, 163, 175, .28); }

@media (max-width: 419px) {
    .rph-mem-edit-foot { gap: 6px; }
    .rph-mem-edit-note { flex: 1 1 100%; order: -1; }
    .rph-mem-edit-spacer { display: none; }
    .rph-mem-edit-btn { flex: 1 1 0; min-width: 0; height: 34px; }
    .rph-mem-edit-area { min-height: 10em; font-size: 14px; }
}

.rph-retry-stop {
    flex: none;
    min-height: 32px;
    padding: 6px 11px;
    font-size: 12px;
    font-weight: 600;
    color: #57534e;
    background: rgba(255, 255, 255, .8);
    border: 1px solid #e7e5e4;
    border-radius: 10px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease, transform .12s ease;
}
@media (hover:hover) and (pointer:fine){.rph-retry-stop:hover { color: #1f2937; border-color: #d6d3d1; background: #fafaf9; }}
.rph-retry-stop:active { transform: scale(.97); }
.rph-retry-stop:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }

@media (max-width: 419px) {
    /* 小屏让"停止重试"独占第二行并右对齐；卡片要占满面板宽度，不能被按钮挤成窄条 */
    .rph-retry { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 10px; padding: 10px 10px 11px; }
    .rph-retry-dot { grid-column: 1; grid-row: 1; }
    .rph-retry-body { flex: none; grid-column: 2; grid-row: 1; }
    .rph-retry-stop { grid-column: 2; grid-row: 2; justify-self: end; margin-top: 2px; min-height: 36px; }
}

@media (prefers-reduced-motion: reduce) {
    .rph-retry { animation: none; }
    .rph-retry-dot { animation: none; box-shadow: 0 0 0 3px rgba(217, 119, 6, .18); }
    .rph-retry-bar > i { transition: none; }
    .rph-retry-bar > i::after { animation: none; }
    .rph-retry-bar > u.rph-retry-slide { animation: none; width: 100%; }
    .rph-retry-stop { transition: none; }
}

/* 夜间态：作者 theme.css 的全局规则会压过我们单类名的写法，所以这几条都带 :root[data-app-theme] 前缀 */
:root[data-app-theme="dark"] .rp-sync-auto {
    border-color: rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.04);
}
:root[data-app-theme="dark"] .rp-sync-auto__row + .rp-sync-auto__row { border-top-color: rgba(255, 255, 255, 0.1); }
:root[data-app-theme="dark"] .rp-sync-auto__state {
    border-color: rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.08);
    color: var(--night-secondary);
}
:root[data-app-theme="dark"] .rp-sync-auto__state.is-on {
    border-color: rgba(129, 140, 248, 0.48);
    background: rgba(79, 70, 229, 0.3);
    color: #c7d2fe;
}
:root[data-app-theme="dark"] .rp-sync-auto__state.is-paused {
    border-color: rgba(217, 119, 6, 0.5);
    background: rgba(217, 119, 6, 0.22);
    color: #fbbf24;
}
:root[data-app-theme="dark"] .rp-sync-auto__switch {
    border-color: rgba(255, 255, 255, 0.22);
    background: rgba(255, 255, 255, 0.16);
}
:root[data-app-theme="dark"] .rp-sync-auto__switch::after { background: #e5e7eb; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); }
:root[data-app-theme="dark"] .rp-sync-auto__switch:checked {
    border-color: rgba(165, 180, 252, 0.55);
    background: #6366f1;
}
:root[data-app-theme="dark"] .rp-sync-auto__switch:focus-visible { outline-color: #818cf8; }
:root[data-app-theme="dark"] .rp-sync-auto__interval {
    background: #2a2a2a;
    border-color: rgba(255, 255, 255, 0.18);
    color: var(--night-text);
}
:root[data-app-theme="dark"] .rp-sync-auto-line.is-warn { color: #f97066; }

/* ===== 补录记忆弹窗在手机上的可视高度（本轮修用户报的"有重试行时超出上下高度"）=====
   作者的弹窗是 modal-shell 面板 + 内部 flex-1 overflow-y-auto，结构本身没毛病；
   出问题的是高度上限写死 max-h-[94vh]：手机浏览器的 vh 量的是"地址栏收起后"的大视口，
   比屏幕上真正看得见的那块高出一截，于是内容一多（多一条重试状态行就是几十像素），
   底部那排按钮被顶到屏幕外，而内部滚动区看着还能滚 —— 用户看到的就是"框超出上下高度"。
   dvh 跟着当前可见高度走；不支持 dvh 的老内核退回 92vh，仍然比 94vh 保守一点。
   （第十六批 R19 之后：窄屏那一档已由文末的共享规则接管，同一口径只在那一处写；
   这里只留桌面与老内核的兜底，别再往这里加设备数值。） */
section[aria-labelledby="memory-backfill-title"] {
    max-height: 92vh;
    max-height: 92dvh;
}

/* ===== 我们自己的"最新用量"三项（输入 / 缓存 / 输出）=====
   作者那条把"缓存"塞在输入格里、且只在 >0 时才出现，看着像数字丢了；我们改成固定三格，缺值明写 —。
   2026-10-10 按用户"参考 2.0.0 重做"改版：学它的**方形语义灯座 + 等宽三格**，
   但**不抄它的 CSS 变量**（--c-text-4 / --r-md 那套令牌是 2.0.0 才有的，随包基座 1.9.8 里没有，
   抄过来会静默失效）；岛底与发丝线一律由运行层采样到的墨色经 color-mix 派生，
   所以卡片自带主题、夜间模式都不用各写一份覆写。每条都留一行不含 color-mix 的兜底值。 */
.rph-usage-bar {
    display: grid;
    /* 四格等宽（输入/缓存/输出/推理）。窄屏不折行：每格约 90px，值最长是「未上报」三个字 */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: stretch;
    margin: .125rem .5rem .5rem;
    overflow: hidden;
    border: 1px solid rgba(120, 120, 120, .18);
    border: 1px solid color-mix(in srgb, var(--rph-usage-ink, #374151) 17%, transparent);
    border-radius: 12px;
    background: rgba(127, 127, 127, .07);
    background: color-mix(in srgb, var(--rph-usage-ink, #374151) 6%, transparent);
}
.rph-usage-cell {
    display: flex;
    min-width: 0;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    padding: .3125rem .25rem;
    white-space: nowrap;
}
/* 格与格之间用发丝线分开，比三个独立胶囊更安静；最后一格在窄屏也不会挤出去（等分 + min-w-0） */
.rph-usage-cell + .rph-usage-cell {
    border-left: 1px solid rgba(120, 120, 120, .14);
    border-left: 1px solid color-mix(in srgb, var(--rph-usage-ink, #374151) 12%, transparent);
}
/* 灯座：作者 2.0.0 的 .usage-swatch 同形（0.5rem 见方、2px 圆角），比 1.9.8 那颗 1.5 圆点更稳 */
.rph-usage-swatch {
    flex: none;
    width: .5rem;
    height: .5rem;
    border-radius: 2px;
}
/* 灯座颜色：一律用我们自己的类钉死，不挂 bg-primary-500 那类主题色板类。
   作者 theme.css 有一条 `:root[data-app-theme="dark"] :is(.bg-primary-500,…){background:var(--night-primary)!important}`，
   挂上去就等于让主题改我们的灯色；卡自带主题也能按同一个类名改写。色值就是默认主题下实测到的原色。
   注意选择器形状：模板把 swatch 和 tone 写在同一个 <i> 上（`class="rph-usage-swatch rph-usage-tone--input"`），
   写成后代（.tone .swatch）就永远匹配不到 —— 上一轮四盏灯就是这么集体消失的（数字还在，灯没了）。 */
.rph-usage-swatch.rph-usage-tone--input { background: #3b82f6; }
/* 缓存 = 输入的同色系浅一档：不赌 1.9.8 里有没有 primary-300 这个类，用透明度达到同样效果 */
.rph-usage-swatch.rph-usage-tone--cache { background: #3b82f6; }
.rph-usage-tone--soft { opacity: .45; }
.rph-usage-swatch.rph-usage-tone--output { background: #facc15; }
.rph-usage-swatch.rph-usage-tone--reasoning { background: #c084fc; }
/* 取到新数据时这一格重建并轻轻弹一下（模板给每格挂了 :key，值一变 Vue 就换节点，动画自然重放） */
@keyframes rph-usage-pop {
    from { opacity: .25; transform: translateY(2px) scale(.94); }
    to { opacity: 1; transform: none; }
}
.rph-usage-cell { animation: rph-usage-pop .28s ease-out; }
@media (prefers-reduced-motion: reduce) { .rph-usage-cell { animation: none; } }
/* 数字的墨色不写死：角色卡能自带主题（卡内容注 <style>，宿主的 DOMPurify 放行 style），
   写死 text-gray-700 碰上深色卡就是深字压深底。运行层现场采样输入栏字色并按对比度兜底，
   算出来的值挂在这个变量上（见 magic-extension.js 的 定墨色）。 */
.rph-usage-bar .rph-usage-ink {
    color: var(--rph-usage-ink, #374151);
}
/* 空值格学作者 2.0.0 的 dd.is-empty：变淡变细，但格子留着占位，整排不会跳位 */
.rph-usage-cell.is-empty .rph-usage-ink { opacity: .5; font-weight: 400; }
.rph-usage-cell.is-empty .rph-usage-swatch { opacity: .28; }

/* ===== 实验室卡（第七批 ⑤⑥）=====
   整卡走作者自己的 settings-fields 网格：每一行（含运行层注入的两颗开关）都是同一个 settings-field，
   框线、内边距、夜间态全部由作者主题接管 —— 用户问"凭啥空回那行跟固定生图视觉差这么远"，
   根因就是那两张行一张包在 settings-field 里、一张没有。
   颜色一律从 currentColor 派生（color-mix），卡自带主题时不会变成一块白斑。 */
.rph-lab-body.settings-fields { border-top: 0; }
/* 只有这张新卡里槽位才"消失"，让注入的两行直接落进作者的网格行距里 */
.rph-lab-body .rph-lab-slot { display: contents; }
.rph-lab-body > .settings-field { padding: .9rem 0; }
/* 注入行：作者 .settings-toggle-row 自带 12px/16px 内边距，与 settings-field 的会叠成两层；
   这里只留 settings-field 那一层，行高交给开关自己撑。 */
.rph-lab-body .rph-lab-row { padding: .9rem 0; min-height: 0; border-bottom: 1px solid #f3f4f6; }
/* 槽里最后一行不是整卡的最后一格 —— 作者的 .settings-field:last-child 会把它那条分隔线抹掉，
   于是"图片只存本机"下面突然没线，看着就像它不属于这张卡。 */
.rph-lab-slot > .settings-field:last-child { border-bottom: 1px solid #f3f4f6; padding-bottom: .9rem; }

/* 空回/审核自动拦截：整行可点，点开才出拦截词面板（用户 2026-10-11）。
   行里那颗开关单独挡冒泡，否则"想展开"会变成"顺手把功能关了"。 */
.rph-guard-row {
    display: flex;
    width: 100%;
    align-items: center;
    gap: .6rem;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    outline: none;
}
@media (hover:hover) and (pointer:fine){.rph-guard-row:hover { background: color-mix(in srgb, currentColor 4%, transparent); }}
.rph-guard-row:active { background: color-mix(in srgb, currentColor 9%, transparent); }
.rph-guard-row:focus-visible { outline: 2px solid color-mix(in srgb, currentColor 45%, transparent); outline-offset: 2px; }
.rph-guard-name { flex: 1 1 auto; min-width: 0; font-size: 13.5px; font-weight: 500; }
.rph-guard-switch { display: inline-flex; flex: none; align-items: center; cursor: pointer; }
/* 作者原生那颗开关的轨道里层是 `relative inline-flex`：滑块由 .settings-toggle::after 用 absolute 画，
   没有这圈定位上下文滑块就飘到轨道外，整颗开关看着就是一个纯色药丸（= 用户说的"开关丢失"）。 */
.rph-guard-knob { position: relative; display: inline-flex; flex: none; align-items: center; }
.rph-guard-row .rph-caret { margin: 0 0 0 .1rem; }
.rph-guard-row.is-open .rph-caret { transform: rotate(180deg); }
.rph-guard-body { margin-top: .55rem; display: grid; gap: .5rem; }
/* 每一组自己一个框：内置 = 标题行 + 词表；自定义 = 那一行文案 + 20 条表格。
   现场读数是两块都直接躺在 guard-body 上（边框 0px、内边距 0px），所以看着像散着的一堆。
   边框一律 color-mix(currentColor)，夜间态跟着作者主题自己走（正文色不许写死）。 */
.rph-guard-box {
    border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    border-radius: 12px;
    padding: .2rem .5rem .5rem;
    background: color-mix(in srgb, currentColor 3%, transparent);
}
.rph-guard-cap { margin: .3rem 0 .45rem; font-size: 12.5px; font-weight: 600; line-height: 1.5; opacity: 1; }
.rph-guard-tip { margin: .4rem 0 0; font-size: 11px; opacity: .5; line-height: 1.5; }

/* 玩家词表：序号单独一列并右对齐 + 一条竖分隔线。
   以前序号是行内 flex 子项、跟输入框各自的 padding 不同源，一眼看过去就是错位（用户：全部重新设计）。 */
.rph-word-grid {
    display: grid;
    border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    border-radius: 12px;
    overflow: hidden;
    background: color-mix(in srgb, currentColor 3%, transparent);
}
.rph-word-row {
    display: grid;
    grid-template-columns: 2.1rem minmax(0, 1fr) 2rem;
    align-items: center;
}
.rph-word-row + .rph-word-row { border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
.rph-word-no {
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-right: .45rem;
    border-right: 1px solid color-mix(in srgb, currentColor 12%, transparent);
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    opacity: .85;
}
.rph-word-input {
    min-width: 0;
    padding: .5rem .6rem;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    font-size: 12.5px;
    outline: none;
}
.rph-word-input:focus { background: color-mix(in srgb, currentColor 6%, transparent); }
.rph-word-act {
    width: 2rem;
    height: 100%;
    border: 0;
    background: none;
    color: inherit;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
@media (hover:hover) and (pointer:fine){.rph-word-act:hover { opacity: 1; background: color-mix(in srgb, currentColor 8%, transparent); }}
.rph-word-act:active { opacity: 1; background: color-mix(in srgb, currentColor 14%, transparent); }
.rph-word-act:disabled, .rph-word-act.is-void { opacity: 0; cursor: default; pointer-events: none; }
.rph-word-row.is-empty { opacity: .92; }
/* 内置那侧的"N 条"计数也要和自定义的"已填 X/20"同权重 */
.rph-guard-box .rph-fold-sum { opacity: .85; font-weight: 600; }

/* 内置词库二次展开 + 词条标签：以前是一圈没有边框的圆点，散在面板里像装饰不像内容 */
.rph-fold-row {
    display: flex;
    width: 100%;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    margin-top: .55rem;
    border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    border-radius: 12px;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
@media (hover:hover) and (pointer:fine) { .rph-fold-row:hover { background: color-mix(in srgb, currentColor 5%, transparent); } }
.rph-fold-row:active { background: color-mix(in srgb, currentColor 13%, transparent); }
/* 第十二批 M6（发散扫把 M3 那一类扫出来了）：备用密钥这类"点一下就地展开"的标题行以前没关文字选中，
   双击会把整行涂蓝 —— 快照行第十一批已经关了，同族的三颗（折叠行 / 实验室的开关行 / 弹层选项）漏了。
   只圈纯点击目标：带输入框的行不在这条里，否则会把框里的选中一起废掉。 */
.rph-fold-row, .rph-guard-row, .rph-pop-item {
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
}
/* A9（用户 2026-10-17 晚点名）：「高级设置」那行以前是 名称 flex:1 1 auto ＋ 摘要 flex:none ——
   摘要一寸不让，窄屏上所有挤压全落在名称上，实测 360 那档被压成「高级／设置」两行（末行 2 字＝孤字）。
   改成：名称是自己的自然宽（宁可省略号也不拆行），摘要让位（可缩、可整块掉到第二行）。
   行允许 wrap 之后，掉下去的是整块摘要而不是一个字 —— 这跟作者那套「标题 + 下一行说明」的卡头是同一种形状。 */
.rph-fold-row { flex-wrap: wrap; }
.rph-fold-name { flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 12.5px; font-weight: 600; }
.rph-fold-sum { flex: 1 1 auto; min-width: 0; text-align: right; font-size: 11px; opacity: .6; font-variant-numeric: tabular-nums; }
.rph-fold-row .rph-caret { margin-left: 2px; }
.rph-fold-row.is-open .rph-caret { transform: rotate(180deg); }
/* R9（用户 2026-10-15）：补录并发数／速率上限／保留最近楼层 收进一个默认收起的「高级设置」。
   用原生 <details>/<summary> 而不是再塞一个 Vue 布尔态：收起＝不占位也不可见，浏览器管焦点与展开，
   而且不像 v-if 那样把这三项从 DOM 里摘走 —— 补丁锚点与量具的"在场"判据都要它们一直在。 */
details.rph-adv { margin: 0; }
details.rph-adv > summary.rph-fold-row { list-style: none; }
details.rph-adv > summary.rph-fold-row::-webkit-details-marker { display: none; }
details.rph-adv[open] > summary.rph-fold-row .rph-caret { transform: rotate(180deg); }
.rph-chip-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: .5rem;
    padding: 10px;
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    border-radius: 12px;
    background: color-mix(in srgb, currentColor 3%, transparent);
}
.rph-chip {
    display: inline-flex;
    max-width: 100%;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border: 1px solid color-mix(in srgb, currentColor 16%, transparent);
    border-radius: 8px;
    background: color-mix(in srgb, currentColor 7%, transparent);
    font-size: 11px;
    line-height: 1.5;
    word-break: break-all;
}
.rph-chip::before {
    content: "";
    flex: none;
    width: 4px;
    height: 4px;
    border-radius: 1px;
    background: currentColor;
    opacity: .4;
}

/* ===== 生图供应商那一格（第七批 ⑨）：备用密钥表 + 小提示 + 小按钮 ===== */
.rph-mini-btn {
    display: inline-flex;
    align-items: center;
    margin-top: .5rem;
    padding: 6px 12px;
    border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
    border-radius: 9px;
    background: none;
    color: inherit;
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
@media (hover:hover) and (pointer:fine){.rph-mini-btn:hover { background: color-mix(in srgb, currentColor 6%, transparent); }}
.rph-mini-btn:active { background: color-mix(in srgb, currentColor 13%, transparent); transform: scale(.98); }
/* 第九批 J2：内置画师串的"名字 + 字数 、复制 + 倒三角"那一横行整块删掉（复制全站只留下拉面板那一颗），
   这一格只剩副行文案与摊开的串本身。下面只留还在用的两条。 */
/* 倒三角挂在自己身上时也要能翻转（原来只有 .rph-fold-row.is-open .rph-caret 这一条，父级不带类就不转） */
.rph-caret.is-open { transform: rotate(180deg); }
.rph-artist-sub { margin-top: 2px; font-size: 11.5px; opacity: .6; }
.rph-artist-view {
    max-height: 12rem;
    margin-top: .5rem;
    padding: 9px 11px;
    overflow: auto;
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    border-radius: 12px;
    background: color-mix(in srgb, currentColor 4%, transparent);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
    line-height: 1.6;
    white-space: pre-wrap;
    word-break: break-word;
    user-select: text;
}
/* 密钥表比拦截词表多两列：尾号芯片（认出是哪把）+ 删除，空行也留着位置不跳 */
.rph-key-grid .rph-key-row {
    display: grid;
    grid-template-columns: 2.1rem minmax(0, 1fr) auto 2rem;
    align-items: center;
}
.rph-key-row + .rph-key-row { border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
/* 第十二批 M1（用户原话「小绿灯周围有长条」）：灯就是灯。
   上一批那层胶囊（底色 + 左右 7px 内距 + 999px 圆角 + opacity .7）是留着"…尾号"那段文字时给的，
   第十一批 L12 把尾号取消后只剩一颗灯，那条长条就变成纯粹多余的外观，还把红绿两色一起压暗 30%。
   现在外层只当容器用：无底色、无内距、无圆角、不透明。 */
.rph-key-tail {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    white-space: nowrap;
}
/* M1 的指向：这一轮真挑中过的那把，行尾在灯的左边点一个 →。只有箭头、没有任何数字（L12 那条还在）。 */
.rph-key-use {
    flex: none;
    font-size: 11px;
    line-height: 1;
    font-weight: 700;
    opacity: .74;
}

/* 密钥轮询状态栏（用户 2026-10-12：尽量简单标记，绿/红指示灯）。
   颜色一律不靠主题继承：自己带描边，夜间态换一档亮度，正文色再怎么变都看得见。 */
.rph-key-lamp {
    width: 8px; height: 8px; flex: none;
    border-radius: 50%;
    background: color-mix(in srgb, currentColor 26%, transparent);
    border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.rph-key-lamp.is-ok { background: #16a34a; border-color: rgba(22, 163, 74, .5); }
.rph-key-lamp.is-bad { background: #dc2626; border-color: rgba(220, 38, 38, .55); }
:root[data-app-theme="dark"] .rph-key-lamp.is-ok { background: #4ade80; border-color: rgba(74, 222, 128, .55); }
:root[data-app-theme="dark"] .rph-key-lamp.is-bad { background: #f87171; border-color: rgba(248, 113, 113, .6); }

/* 供应商标题行右边那句状态汇总「成 N · 败 M」：只在真落定过之后出现（v-if），所以不许留占位高度。
   整句染红太重（用户点名排版），颜色只落在两个数字上；标签那几个字用 currentColor 混出来，两档主题都不写死。 */
.rph-key-last { font-size: 11px; line-height: 1.4; max-width: 55%; font-variant-numeric: tabular-nums;
    color: color-mix(in srgb, currentColor 62%, transparent); }
.rph-key-last b { font-weight: 700; }
.rph-key-last .rph-key-good { color: #15803d; }
.rph-key-last .rph-key-bad { color: #b91c1c; }
:root[data-app-theme="dark"] .rph-key-last { color: color-mix(in srgb, currentColor 70%, transparent); }
:root[data-app-theme="dark"] .rph-key-last .rph-key-good { color: #4ade80; }
:root[data-app-theme="dark"] .rph-key-last .rph-key-bad { color: #fca5a5; }
/* 主密钥那颗灯（连它左边的箭头）要**贴到右边缘**：与上面那格输入框、下面"备用密钥"那一行同一条右缘。
   以前这里让出 2rem 给"删"那一列，但"删"其实长在各行**内部**（备用行的 ✕ 在灯右边），外层再让位
   只会把灯和模式框齐齐往里推 32px（2026-10-13 用户实拍指出"手机端还不是靠右"）。 */
.rph-key-tail-line { display: flex; justify-content: flex-end; align-items: center; margin-top: 2px; }
/* 密钥轮询那一行：左边一句短标签，右边那颗弹层钮 */
/* 同上一条：这里以前跟着让 2rem（L11 时代还要多让那颗胶囊的 7px），现在灯与钮都贴右缘，
   让位一律取消 —— 两者仍与同摞盒子右缘一致，m0 段量的就是"灯/钮右缘 == 输入框右缘 ≤2px"。 */
.rph-key-mode { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.rph-key-mode-name { font-size: 12px; font-weight: 500; opacity: .72; }
.rph-key-mode-select {
    flex: none;
    min-width: 7.5rem;
    padding: .3rem 1.6rem .3rem .55rem;
    border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
    border-radius: .55rem;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 12px;
}
.rph-key-mode-select:focus-visible { outline: 2px solid color-mix(in srgb, currentColor 45%, transparent); outline-offset: 1px; }
/* ── 第八批 I9：原生 select 的浏览器箭头换成全卡同形的 .rph-caret（聊天渲染楼层 + 密钥轮询两处一起）。
   颜色一律 color-mix(currentColor)，夜间态自己跟；appearance 必须带 -webkit- 前缀，iOS Safari 才不吃。 ── */
.rph-lab-line { display: flex; align-items: center; justify-content: space-between; gap: .6rem; min-width: 0; }
.rph-select-wrap { position: relative; display: inline-flex; align-items: center; flex: none; }
/* 第九批 J6：密钥轮询那格从原生 select 换成手写弹层。
   第十一批 L4/L11（用户：非展开状态整颗还是偏大；框和状态要视觉对齐）：
   宽度改成跟着内容走、只留一个下限，不再靠整格撑大；右端与灯那一列退同样的 2rem，两条右边缘对在同一条线上。 */
.rph-key-mode-box { position: relative; display: inline-flex; align-items: center; flex: none; min-width: 6.5rem; }
.rph-key-mode-btn { min-width: 6.5rem; width: auto; padding: .3rem .62rem; gap: .4rem; }
/* ── 第十批 K5/K6：两处下拉（密钥轮询、聊天渲染楼层）共用这一套弹层形状，要求像素级一致 ──
   上一版这两处的毛病都量出来了：面板只有钮那么宽（138px），说明文字被挤成一竖条，
   两行选项撑到 194px 高；对号那一列只有一行有东西，看着像空了一列。
   这里改成：面板按内容长（上限 20rem、不超视口），行是"文字 | 对号"两列栅格，
   对号那一列**每行都占位**（没选中就空着但等宽），并且加一层透明遮罩实现"点空白处收起"。 */
.rph-pop-box { position: relative; display: inline-flex; align-items: center; flex: none; min-width: 6.5rem; }
.rph-pop-btn { min-width: 6.5rem; width: auto; padding: .3rem .62rem; gap: .4rem; }
/* 第十二批 M2（用户：「"密钥轮询"右侧那个调模式的框缩小尺寸，靠右」）。
   这里必须带 `.settings-view` 前缀才打得住：作者那两条是
   `.settings-view .settings-control { width:100%; min-height:2.875rem; padding:.625rem .875rem }`(0,2,0) 与
   `.settings-view button.settings-control { height:2.875rem }`(0,2,1)，
   上一版写在裸 `.rph-pop-btn`(0,1,0) 上的 width/padding 从来没生效过 —— 量到的 110×46 就是作者那一套。
   带前缀 + 源码在后面，同特异性下按顺序取胜；两颗同族钮（密钥轮询 / 聊天渲染楼层）一起吃这一刀，
   第十批 K6 那条"同款"才不会被拆成两样。 */
.settings-view .rph-pop-box, .settings-view .rph-key-mode-box { min-width: 0; }
.settings-view .rph-pop-btn { width: auto; min-width: 0; min-height: 0; height: auto; padding: .3rem .45rem; gap: .25rem; border-radius: .5rem; }
.settings-view button.rph-pop-btn { height: auto; min-height: 0; }
/* 实测缩了一圈还是 99px 宽：模板里那颗文字 span 自带作者的 `text-sm`（14px，0,1,0 直接命中 span），
   写在按钮上的 font-size 只是继承值、被它压住；倒三角那颗也占 16+2。三样一起吃才到得了 ≤84：
   字号跟着同一行左侧的 .rph-key-mode-name（12px）对齐，三角收成 14、贴掉那 2px 间隙。 */
.settings-view .rph-pop-btn > span { font-size: 12px; }
.settings-view .rph-pop-btn .rph-caret { width: 14px; margin-left: 0; }
.rph-pop-backdrop { position: fixed; inset: 0; z-index: 39; }
.rph-pop {
    /* 右对齐锚：这两颗钮都在各自那一行的右端，锚左边在手机上会把整块面板顶出视口（390 实拍就是这么红的） */
    position: absolute; left: auto; right: 0; top: calc(100% + .25rem); z-index: 40;
    width: max-content; min-width: 11rem; max-width: min(20rem, calc(100vw - 1.5rem));
    padding: .25rem; border-radius: .75rem;
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    background: #fff; color: #111827;
    box-shadow: 0 12px 28px -12px rgba(15, 23, 42, .30);
}
:root[data-app-theme="dark"] .rph-pop { background: #1f2937; color: #e5e7eb;
    border-color: color-mix(in srgb, #fff 16%, transparent);
    box-shadow: 0 14px 30px -12px rgba(0, 0, 0, .62); }
.rph-pop-item {
    display: grid; grid-template-columns: minmax(0, 1fr) 18px; align-items: start; gap: .5rem;
    width: 100%; padding: .42rem .55rem; border: 0; border-radius: .55rem;
    background: none; color: inherit; text-align: left; cursor: pointer;
}
@media (hover:hover) and (pointer:fine){.rph-pop-item:hover { background: color-mix(in srgb, currentColor 7%, transparent); }}
.rph-pop-item:active { background: color-mix(in srgb, currentColor 14%, transparent); transform: translateY(1px); }
.rph-pop-item.is-on { background: color-mix(in srgb, #2563eb 12%, transparent); color: #1d4ed8; }
.rph-pop-item.is-on:active { background: color-mix(in srgb, #2563eb 20%, transparent); }
:root[data-app-theme="dark"] .rph-pop-item.is-on { background: color-mix(in srgb, #60a5fa 20%, transparent); color: #bfdbfe; }
.rph-pop-name { display: block; font-size: 13px; font-weight: 700; line-height: 1.35; }
.rph-pop-desc { display: block; margin-top: 1px; font-size: 11.5px; line-height: 1.35; opacity: .66; }
/* 对号那一列每行都占位：没选中也留 18px，两行文字右边缘才对得齐（用户点名的"那一竖列没有字"） */
.rph-pop-tick { width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; flex: none; }
.rph-pop-tick svg { width: 16px; height: 16px; }
.rph-native-select {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    min-width: 8.6rem; padding: .42rem 1.9rem .42rem .7rem;
    border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
    border-radius: 10px;
    background: color-mix(in srgb, currentColor 5%, transparent);
    color: inherit; font: inherit; font-size: 12.5px; outline: none;
}
.rph-native-select:focus-visible { outline: 2px solid color-mix(in srgb, currentColor 45%, transparent); outline-offset: 1px; }
/* 箭头盒子从 8×5 的实心楔形换成 16×16 的细描 chevron 之后，居中偏移要按新盒高算（-2px 是老楔形的） */
.rph-select-wrap .rph-caret { position: absolute; right: .6rem; top: 50%; margin-top: -8px; pointer-events: none; opacity: .62; }

.rph-fold-row:focus-visible { outline: 2px solid color-mix(in srgb, currentColor 45%, transparent); outline-offset: -2px; }

/* 补图片信息的气泡：作者那颗堆在时挂进堆里（沿用原生观感），不在时（图片管理内联页、或堆还没建出来）
   用自家这套类画同一颗顶部气泡。只在自家堆里生效，不跟作者的 toast-item 抢样式。 */
.rph-bubble-stack { position: fixed; top: 18px; left: 50%; transform: translateX(-50%);
    display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 100;
    pointer-events: none; width: min(92vw, 520px); }
.rph-bubble-stack .rph-bubble { pointer-events: auto; min-width: 0; max-width: 100%;
    padding: 9px 20px; border-radius: 999px; border: 1px solid; font-size: 12.5px; line-height: 1.5;
    text-align: center; box-shadow: 0 6px 18px rgba(15, 23, 42, .16); backdrop-filter: blur(4px); }
.rph-bubble-stack .rph-bubble--info { border-color: #7dd3fc; color: #075985; background: rgba(240, 249, 255, .95); }
.rph-bubble-stack .rph-bubble--success { border-color: #86efac; color: #166534; background: rgba(240, 253, 244, .95); }
.rph-bubble-stack .rph-bubble--error { border-color: #fca5a5; color: #991b1b; background: rgba(254, 242, 242, .95); }
:root[data-app-theme="dark"] .rph-bubble-stack .rph-bubble--info { border-color: #0e7490; color: #a5f3fc; background: rgba(8, 47, 73, .94); }
:root[data-app-theme="dark"] .rph-bubble-stack .rph-bubble--success { border-color: #15803d; color: #bbf7d0; background: rgba(20, 83, 45, .94); }
:root[data-app-theme="dark"] .rph-bubble-stack .rph-bubble--error { border-color: #b91c1c; color: #fecaca; background: rgba(127, 29, 29, .94); }

/* 只有结果、还没在"用过"表里留下尾号时：胶囊只当灯的底座，别把空尾号撑得太宽 */
.rph-key-tail.is-lamp-only { padding-right: 5px; }

/* ===== 窄屏下二级界面的面板跟着「看得见的那一块」走（第十六批 R19）=====
   用户原话（2026-10-15）：「剧情分支、真实上下文请求点开的二级界面、预设/世界书/正则/工具点击编辑后的
   二级界面需要适配手机，现在高度溢出屏幕。」
   真因不是内容太长（内层 `flex-1 overflow-y-auto` 本来就滚），而是面板的高度上限按 `vh` 写死、
   居中又是拿 CSS 视口当参照：手机上 `vh` 量的是地址栏收起后的**大视口**，比眼睛真正看得见的那块高出一截，
   于是面板上下都超出可见区，底部那排主按钮（保存／取消／进入／编辑／删除）玩家够不着
   —— 实测 390×844、可见区压到 675 时：剧情分支越界 135px、预设 98px、世界书 144px、正则 133px。
   两条一起才够：① `max-height` 跟着可见高走（面板本身不再比可见区长）；
   ② `margin-top: 可见高 − 100vh`（负值，flex 居中会把它折半）＝ 把"在 CSS 视口里居中"改成
   "在看得见的那块里居中"，上下各留 16px。只做①的话面板照样朝 844 那一档居中，底下还是漏出可见区。
   可见高由运行层现场读 `visualViewport.height` 挂成 `--rph-可视高`（见 magic-extension.js 的 定可见高）：
   拿 JS 而不是只写 `100dvh`，是为了键盘顶起来那截也跟着收，并且老内核没有 dvh 也能退回 `100vh`。
   不用 `transform` 位移：那会把面板变成 fixed 后代的包含块（里面的下拉浮层会跟着错位），
   还要和作者入场动画 `.modal-enter-active > .app-modal-panel` 的 transform 打架。
   窄屏才生效：1280 那一档的几何是已定档实拍过的，这一条不去动它。
   为什么要多写一层 `body`：作者的 Tailwind 不是随 assets/css/styles.css 静态排好的，而是运行时注入成
   内联 `<style>`（CSSOM 里排在 /DB/styles.css 之后，实测序 1329 vs 1188），同特异度 `.max-h-\[94vh\]`
   就把我们那条压掉了 —— 症状是 margin-top 生效、max-height 不生效。抬一层 `body`（0,1,1 > 0,1,0）
   就不靠加载顺序了；这也解释了为什么暗色那批规则同样要带 `body`。 */
@media (max-width: 767px) {
    /* ① 把"看得见的那一块"直接交给覆盖层，别去推面板（第十七批 A12）。
       形状无关：桌面 Chromium 上 `fixed inset-0` 等于 100vh，这一条把它收到可视高；手机 Edge 上它本来
       就只有可视高，同一条不改任何东西 —— 两种内核得到同一个结果。
       旧写法 `margin-top: calc(可视高 - 100vh)` 是一笔按"覆盖层＝100vh"的盲补偿，真机上把面板顶出上缘
       44~86px（390×844 几何对拍，见 施工方案-第十七批.md §十三·一：剧情分支的标题行与 ✕ 半截在屏幕外）。
       老内核没有 :has() 时这一条整段不生效，退到"只收高不挪位"：最坏是底部漏出约 (100vh−可视高)/2，
       比切顶轻，也不再靠负 margin 去赌浏览器是哪一种形状。 */
    body .fixed.inset-0:has(> .app-modal-panel) {
        top: var(--rph-可视顶, 0px);
        height: var(--rph-可视高, 100dvh);
    }
    /* ② 面板只收高。上缘留给 ①，不再自己挪位。 */
    body .app-modal-panel,
    body section[aria-labelledby="memory-backfill-title"] {
        max-height: calc(100vh - 32px);   /* 没有 dvh、JS 又没跑起来的内核：至少不贴边 */
        max-height: calc(var(--rph-可视高, 100dvh) - 32px);
    }
}

/* #60（第十七批）：体检跑完那一下，「体检 N/M」这行要真贴顶，不是停在屏幕中间。
   根因是几何：这一屏的滚动容器在版本卡贴到内容底之后就再也滚不动了（实测顶边 387／可视高 844、
   判据读出"已滚到底=是"），`scrollIntoView({block:'start'})` 再怎么等 smooth 停稳也只能推到屏幕中间。
   第一版这里写的是 `scroll-padding-block-end` —— **那是错的工具**：scroll-padding 只改变"对齐时让开多少"，
   不会凭空多出一屏可滚内容，所以部署完复跑读数一模一样 387。要解开这个封顶，只能在容器末尾**真的加一段可滚高度**。
   用 :has 只在体检结果在场时生效：玩家滚到这一段时看到的是一屏空白，但体检盒子一收就恢复原样，
   平时这一屏下面不多东西。 */
.management-view:has(.rph-check:not([hidden])) {
    padding-bottom: 52vh;
}
