/*
 * Setup surfaces: the Web installer / upgrader (public/install.php), the
 * "database needs upgrade" guide and the required-configuration page.
 *
 * Layout follows the uiarc onboarding stepper: a single centered card, a
 * horizontal connected stepper with labels + hints, a single-column form area,
 * inline field validation, and Back / Continue actions at the bottom.
 *
 * The design is intentionally static: no cursor effects, filters or blend modes.
 * Guide pages reuse the same shell classes without the wizard-specific parts.
 */

/* ========================================================================== */
/* 1. Page shell                                                              */
/* ========================================================================== */
/*
 * 安装页同样只是一个「用共享令牌排版的页面」。
 * 颜色 / 圆角 / 字体全部来自 admin-ui.css 的 --ui-*，与后台、认证页同源；
 * 本文件只写安装向导独有的结构与排版，不再声明任何颜色令牌。
 */

body.setup-page {
    min-width: 320px;
    min-height: 100dvh;
    margin: 0;
    background: var(--bg);
    color: var(--dark);
    font-family: var(--font);
    font-size: 14px;
    line-height: 1.45;
    letter-spacing: -.005em;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    accent-color: var(--primary);
}

/* Dark theme: the dock writes data-admin-theme on <html>, so the installer and
   login screens follow the same switch as the admin panel. Values mirror the
   admin bundle's dark palette so both surfaces look like one product. */
/* ========================================================================== */
/* 2. Shell and card                                                          */
/* ========================================================================== */

.setup-shell {
    display: grid;
    min-height: 100dvh;
    place-items: center;
    padding: clamp(24px, 6vh, 72px) 16px;
}

.setup-panel {
    width: min(1180px, 100%);
    min-width: 0;
    margin-inline: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg);
    color: var(--dark);
    padding: 32px;
    animation: setupCardIn .3s cubic-bezier(0, 0, .2, 1) both;
}

.setup-panel--narrow {
    width: min(560px, 100%);
}

@keyframes setupCardIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ========================================================================== */
/* 3. Card head                                                               */
/* ========================================================================== */

/* One row, edge to edge: brand title on the left, the whole stepper on the
   right, starting at the title's right edge and ending flush with the card. */
.wizard-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: clamp(24px, 4vw, 56px);
    margin-bottom: 26px;
}

.wizard-brand {
    display: flex;
    flex: 0 0 auto;
    min-width: 0;
    align-items: center;
    gap: 10px;
    /* Optical centering against the 28px step discs next to it. */
    padding-top: 1px;
}

/* 左上角品牌标记：现在是内联 <svg>（能跟随主题色），不再是被裁成圆形的位图，
   所以不要 object-fit / border-radius —— 圆边由 SVG 自己画。 */
.wizard-logo {
    display: block;
    width: 26px;
    height: 26px;
    flex: none;
}

.wizard-title {
    margin: 0;
    color: var(--dark);
    font-size: 19px;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -.01em;
}

/* ========================================================================== */
/* 4. Stepper                                                                 */
/* ========================================================================== */

/* Pill row: each step is a fit-width chip of icon + label, aligned to the right
   edge of the card next to the title. No connector rail, no numbers, no hints. */
.wizard-stepper {
    position: relative;
    flex: 0 1 auto;
    min-width: 0;
    margin: 0;
    padding: 0;
}

.wizard-steps {
    position: relative;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.wizard-step {
    position: relative;
    display: flex;
    flex: 0 0 auto;
}

.wizard-step-head {
    display: inline-flex;
    min-width: 0;
    align-items: center;
    gap: 7px;
    margin: 0;
    padding: 6px 13px;
    border: 0;
    border-radius: var(--radius-pill);
    background: var(--border-color);
    color: var(--dark);
    font: inherit;
    text-decoration: none;
    transition: background-color .2s ease, color .2s ease;
}

.wizard-step[data-status="current"] .wizard-step-head {
    background: var(--primary);
    color: #fff;
}

a.wizard-step-head { cursor: pointer; }
a.wizard-step-head:hover { background: var(--border-color); color: var(--dark); }
.wizard-step-head:focus-visible { outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent); outline-offset: 3px; }
.wizard-step-head[aria-disabled="true"] { cursor: default; }

.wizard-step-icon {
    flex: none;
    color: inherit;
    font-size: 13px;
    line-height: 1;
}

.wizard-step-label {
    color: inherit;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
}

.wizard-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ========================================================================== */
/* 5. Step content                                                            */
/* ========================================================================== */

/* Fixed-height body for the four wizard steps: each one keeps the same content
   box, so the card never resizes when switching steps. The height is the tallest
   step (step 1 with the preview card); content that needs more simply grows the
   box instead of clipping. Success screens keep their natural height.
   Inside that box, `.wizard-body` owns the free space and centers its content
   vertically, while the action row stays pinned to the bottom. */
.wizard-panel {
    --wizard-body-h: 449px;

    width: 100%;
    max-width: 760px;
    min-width: 0;
    margin: 0 auto;
}

.wizard-panel[data-step="site_config"],
.wizard-panel[data-step="db_config"],
.wizard-panel[data-step="check"],
.wizard-panel[data-step="admin"],
.wizard-panel[data-step="authorize_upgrade"] {
    display: flex;
    min-height: var(--wizard-body-h);
    flex-direction: column;
}

.wizard-panel[data-step="site_config"] > form,
.wizard-panel[data-step="db_config"] > form,
.wizard-panel[data-step="check"] > form,
.wizard-panel[data-step="admin"] > form,
.wizard-panel[data-step="authorize_upgrade"] > form {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    flex-direction: column;
}

.wizard-body {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    flex-direction: column;
    justify-content: center;
}

/* A single 28px gap above the actions; the base rule's margin-top is dropped
   here so the gap is not counted twice against the fixed height. */
.wizard-panel[data-step] .wizard-actions {
    margin-top: 0;
    padding-top: 28px;
}

.wizard-panel[data-step="site_config"] { max-width: none; }

.wizard-step-title {
    margin: 0 0 18px;
    color: var(--dark);
    font-size: 15px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: -.005em;
}

.wizard-lead {
    max-width: 60ch;
    margin: 0 0 18px;
    color: var(--dark);
    font-size: 13.5px;
    line-height: 1.55;
}

.wizard-divider {
    height: 0;
    margin: 24px 0;
    border: 0;
    border-top: 1px solid var(--bg);
}

/* Fields ------------------------------------------------------------------- */

.wizard-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 16px;
}

.wizard-field {
    display: grid;
    min-width: 0;
    /* Rows stay at the top: a taller field (e.g. password with its meter) must
       not stretch the label/input of the neighbouring one apart. */
    align-content: start;
    gap: 6px;
}

.wizard-field--full { grid-column: 1 / -1; }

/* Label row: the label sits left, its validation message right. Keeping the
   message here (instead of under the input) means a validation error never
   changes the board's vertical rhythm. */
.wizard-field-head {
    display: flex;
    min-width: 0;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.wizard-field-head > .wizard-field-error,
.wizard-field-head > .setup-field-error {
    flex: 0 1 auto;
    max-width: 62%;
    margin: 0;
    text-align: right;
}

.wizard-field-head > .form-label {
    display: flex;
    min-width: 0;
    align-items: baseline;
    gap: 3px;
    margin: 0;
    color: var(--dark);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
}

.wizard-field > .form-label .text-danger {
    color: var(--danger);
    font-weight: 500;
}

.wizard-field .form-text {
    margin: 0;
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    font-size: 12px;
    line-height: 1.45;
}

.setup-page .form-control {
    display: block;
    width: 100%;
    height: 44px;
    padding: 0 16px;
    box-sizing: border-box;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--dark);
    font-family: inherit;
    font-size: 14px;
    line-height: 42px;
    box-shadow: none;
    transition: border-color .16s ease, box-shadow .16s ease;
    -moz-appearance: textfield;
    appearance: none;
}

.setup-page .form-control::placeholder { color: color-mix(in srgb, var(--dark) 60%, var(--bg)); }
.setup-page .form-control:hover { border-color: #bfbfbf; }
.setup-page .form-control:focus {
    border-color: var(--primary);
    outline: 0;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
.setup-page .form-control:disabled,
.setup-page .form-control[readonly] {
    background: var(--bg);
    color: var(--dark);
    border-color: var(--border-color);
    cursor: default;
}
.setup-page .form-control.is-invalid {
    border-color: #dc2626;
    box-shadow: none;
}
.setup-page .form-control.is-invalid:focus {
    border-color: #dc2626;
    box-shadow: 0 0 0 3px rgba(220, 38, 38, .12);
}

/* Email field with typo suggestion ------------------------------------------------
   Domain chips appear while the domain is still empty; a "did you mean" line
   appears for near-miss domains and fixes the value in one click. */
.wizard-em {
    position: relative;
    display: flex;
    align-items: center;
}

.wizard-em-input { padding-right: 44px; }

/* Success tick: only shown once the address parses cleanly and has no suggestion. */
.wizard-em-check {
    position: absolute;
    right: 14px;
    top: 50%;
    width: 20px;
    height: 20px;
    transform: translateY(-50%);
    pointer-events: none;
}

.wizard-em-check circle {
    fill: var(--success);
    transform: scale(0);
    transform-origin: center;
    transform-box: fill-box;
    transition: transform .35s cubic-bezier(.3, 1.7, .5, 1);
}

.wizard-em-check path {
    fill: none;
    stroke: #fff;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 14;
    stroke-dashoffset: 14;
    transition: stroke-dashoffset .3s ease .2s;
}

.wizard-em.is-ok .wizard-em-check circle { transform: none; }
.wizard-em.is-ok .wizard-em-check path { stroke-dashoffset: 0; }

/* Domain menu: an overlay under the field, so opening it never grows the row. */
.wizard-em-menu {
    position: absolute;
    z-index: 20;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    display: grid;
    gap: 2px;
    padding: 6px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    box-shadow: 0 12px 28px -12px rgba(20, 20, 40, .28), 0 2px 6px rgba(20, 20, 40, .06);
}

.wizard-em-menu[hidden] { display: none; }

.wizard-em-option {
    padding: 8px 12px;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--dark);
    font: inherit;
    font-size: 12.5px;
    text-align: left;
    cursor: pointer;
    transition: background-color .14s ease, color .14s ease;
}

.wizard-em-option:hover,
.wizard-em-option.is-active {
    background: color-mix(in srgb, var(--primary) 10%, transparent);
    color: var(--primary);
}

.wizard-em-option:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent);
    outline-offset: 1px;
}

/* "Did you mean" — a quiet warning row, not a full alert. */
.wizard-em-fix {
    display: flex;
    align-items: center;
    gap: 7px;
    width: 100%;
    margin-top: 8px;
    padding: 9px 12px;
    border: 0;
    border-radius: var(--radius-pill);
    background: #fef3c7;
    color: var(--dark);
    font: inherit;
    font-size: 12px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background-color .18s ease;
}

.wizard-em-fix[hidden] { display: none; }
.wizard-em-fix:hover { background: #fde68a; }
.wizard-em-fix:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent);
    outline-offset: 2px;
}

.wizard-em-fix .bi {
    flex: none;
    color: #b45309;
    font-size: 13px;
}

.wizard-em-fix b { font-weight: 600; }

.wizard-em-fix mark {
    background: none;
    color: #b45309;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* Password field with inline tools -------------------------------------------------
   Two icon buttons live inside the pill: a generator and a show/hide toggle.
   The input keeps its own pill styling; only the right padding grows so text
   never runs under the icons. */
.wizard-pw {
    position: relative;
    display: flex;
    align-items: center;
}

/* Right padding must beat `.setup-page .form-control`'s shorthand, so the
   selector carries the same two-class weight; otherwise long passwords render
   underneath the inline tools. */
.setup-page .wizard-pw-input {
    padding-right: 92px;
    font-variant-numeric: tabular-nums;
}

/* Confirm field carries only the eye button. */
.setup-page .wizard-pw--confirm .wizard-pw-input { padding-right: 46px; }

.wizard-pw-tools {
    position: absolute;
    right: 6px;
    top: 50%;
    display: flex;
    align-items: center;
    gap: 2px;
    transform: translateY(-50%);
}

.wizard-pw-btn {
    display: grid;
    width: 32px;
    height: 32px;
    padding: 0;
    place-items: center;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    cursor: pointer;
    transition: background-color .16s ease, color .16s ease;
}

.wizard-pw-btn svg {
    width: 20px;
    height: 20px;
    overflow: visible;
}

/* Generator glyph: "dice" before anything is generated, "refresh" afterwards. */
.wizard-pw-regen-icon { display: none; }
.wizard-pw.is-generated .wizard-pw-gen-icon { display: none; }
.wizard-pw.is-generated .wizard-pw-regen-icon { display: block; }

.wizard-pw-btn:hover {
    background: var(--bg);
    color: var(--dark);
}

.wizard-pw-btn:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent);
    outline-offset: 1px;
}

.wizard-pw-btn:active { color: var(--dark); }

/* Eye toggle: two Bootstrap Icons cross-fading in place. Slashed eye while the
   value is masked, plain eye once it is visible. */
.wizard-pw-eye {
    position: relative;
    display: grid;
    place-items: center;
}

.wizard-pw-eye .bi {
    grid-area: 1 / 1;
    font-size: 16px;
    line-height: 1;
    transition: opacity .18s ease, scale .18s cubic-bezier(.2, .8, .2, 1);
}

.wizard-pw-eye-off { opacity: 1; scale: 1; }
.wizard-pw-eye-on { opacity: 0; scale: .8; }

.wizard-pw.is-revealed .wizard-pw-eye-off { opacity: 0; scale: .8; }
.wizard-pw.is-revealed .wizard-pw-eye-on { opacity: 1; scale: 1; }

.wizard-pw.is-revealed .wizard-pw-eye { color: var(--primary); }

/*
 * Edge 的密码框自带一个「显示密码」眼睛（::-ms-reveal），Safari 带自动填充
 * 钥匙（::-webkit-credentials-auto-fill-button）。它们不是 DOM 元素，
 * 任何 opacity / class 都影响不到，只能靠伪元素关掉；否则会和
 * .wizard-pw-eye 并排出现两个眼睛。
 */
.setup-page input::-ms-reveal,
.setup-page input::-ms-clear { display: none; }

.setup-page input::-webkit-credentials-auto-fill-button,
.setup-page input::-webkit-strong-password-auto-fill-button {
    position: absolute;
    right: 0;
    visibility: hidden;
    pointer-events: none;
}

/* Four-segment strength bar: no labels, purely visual. */
/* Meter + hint share one full-width row: bars flush left, text flush right. */
.wizard-pw-meter-row {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 2px 0 0;
}

.wizard-pw-meter {
    display: grid;
    width: 96px;
    flex: none;
    grid-template-columns: repeat(4, 1fr);
    gap: 4px;
}

.wizard-pw-hint {
    margin: 0;
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    font-size: 11.5px;
    line-height: 1.35;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wizard-pw-hint[data-level="1"] { color: #dc2626; }
.wizard-pw-hint[data-level="2"] { color: #d97706; }
.wizard-pw-hint[data-level="3"] { color: #ca8a04; }
.wizard-pw-hint[data-level="4"] { color: var(--success); }

.wizard-pw-meter i {
    position: relative;
    height: 5px;
    overflow: hidden;
    border-radius: var(--radius-pill);
    background: var(--bg);
}

.wizard-pw-meter i::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--pw-level, var(--border-color));
    transform: scaleX(0);
    transform-origin: 0 50%;
    transition: transform .45s cubic-bezier(.2, .8, .2, 1), background-color .4s ease;
}

.wizard-pw-meter i.on::after { transform: none; }
.wizard-pw-meter i:nth-child(2)::after { transition-delay: .05s; }
.wizard-pw-meter i:nth-child(3)::after { transition-delay: .10s; }
.wizard-pw-meter i:nth-child(4)::after { transition-delay: .15s; }

.setup-page textarea.form-control {
    height: auto;
    min-height: 96px;
    padding: 10px 16px;
    border-radius: var(--radius-pill);
    line-height: 1.5;
    resize: vertical;
}

.setup-field-error {
    margin: 0;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.45;
}
.setup-field-error:empty { display: none; }

/* Inline row / checkbox ---------------------------------------------------- */

.wizard-inline-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 14px;
    margin-top: 20px;
}

.wizard-check {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    margin: 0;
    color: var(--dark);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

/* Custom round checkbox: the native box is hidden and redrawn so the control
   keeps the installer's rounded geometry everywhere. */
/* 字段行右侧：错误信息与行内开关并排靠右，不额外占一行高度。
   与「错误显示在 label 行右侧」的既有规则一致。 */
.wizard-field-aside {
    display: flex;
    min-width: 0;
    flex: 0 1 auto;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
}

/* 行内开关（如密码行的「留空」）：比常规 checkbox 小一号，颜色次要，
   避免和主标签抢注意力。 */
.wizard-check--inline {
    flex: none;
    gap: 5px;
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    font-size: 12px;
    font-weight: 400;
    white-space: nowrap;
}

/* 必须带上 .wizard-check 前缀：`.wizard-check input[type="checkbox"]` 的
   特异性比单独一个修饰类高，只写 .wizard-check--inline 会被压回 18px。 */
.wizard-check.wizard-check--inline input[type="checkbox"] {
    width: 14px;
    height: 14px;
    flex: none;
    border-width: 1.5px;
}

.wizard-field.is-clearing .wizard-check--inline {
    color: var(--primary);
    font-weight: 500;
}

.wizard-check input[type="checkbox"] {
    position: relative;
    width: 18px;
    height: 18px;
    flex: none;
    margin: 0;
    border: 1.5px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    transition: border-color .16s ease, background-color .16s ease;
}

.wizard-check input[type="checkbox"]::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 9px;
    height: 5px;
    border-left: 2px solid #fff;
    border-bottom: 2px solid #fff;
    translate: -50% -65%;
    rotate: -45deg;
    scale: 0;
    transition: scale .16s cubic-bezier(.34, 1.56, .64, 1);
}

.wizard-check input[type="checkbox"]:checked {
    border-color: var(--primary);
    background: var(--primary);
}

.wizard-check input[type="checkbox"]:checked::after { scale: 1; }
.wizard-check input[type="checkbox"]:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent);
    outline-offset: 2px;
}
.wizard-check input[type="checkbox"]:disabled { cursor: default; opacity: .6; }

.wizard-text-button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 2px;
    border: 0;
    background: transparent;
    color: var(--primary);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
}
.wizard-text-button:hover { color: var(--primary); }
.wizard-text-button .bi { font-size: 13px; }

/* Option cards ------------------------------------------------------------- */

.wizard-options {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin: 0;
}

.wizard-option {
    position: relative;
    display: grid;
    min-width: 0;
    align-content: start;
    gap: 3px;
    padding: 14px 16px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg);
    color: var(--dark);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color .16s ease, background-color .16s ease;
}

.wizard-option:not([data-disabled="1"]):hover { border-color: #bfbfbf; }
.wizard-option:focus-within { outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent); outline-offset: 1px; }
.wizard-option[data-selected="1"] { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); }
.wizard-option[data-selected="1"] .wizard-radio { border-color: var(--primary); }
.wizard-option[data-selected="1"] .wizard-radio::after { scale: 1; }

/* Disabled option: readable but obviously unavailable, and it never shows a
   selected state. Used when the detected schema makes an action impossible. */
.wizard-option[data-disabled="1"] {
    border-color: var(--border-color);
    background: var(--bg);
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    cursor: not-allowed;
}
.wizard-option[data-disabled="1"] .wizard-option-label,
.wizard-option[data-disabled="1"] .wizard-option-detail { color: color-mix(in srgb, var(--dark) 60%, var(--bg)); }
.wizard-option[data-disabled="1"] .wizard-radio { border-color: var(--border-color); }
.wizard-option[data-disabled="1"] .wizard-radio::after { scale: 0; }

.wizard-option-input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.wizard-option-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.wizard-option-label {
    color: var(--dark);
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.4;
}

.wizard-option-detail {
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    font-size: 12px;
    line-height: 1.45;
}

.wizard-option[data-danger="1"] .wizard-option-detail { color: var(--danger); }

.wizard-radio {
    position: relative;
    display: grid;
    box-sizing: border-box;
    width: 16px;
    height: 16px;
    flex: none;
    place-items: center;
    border: 1.5px solid var(--border-color);
    border-radius: 50%;
    transition: border-color .16s ease;
}

/* Centering uses translate (not a fractional inset) so the dot stays perfectly
   centred even though the ring's border is 1.5px. Scaling is its own property so
   it never fights the centering transform. */
.wizard-radio::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--primary);
    translate: -50% -50%;
    scale: 0;
    transition: scale .18s ease;
}

/* Facts (success screens) -------------------------------------------------- */

/* ========================================================================== */
/* 5b. Step-one layout                                                       */
/* ========================================================================== */

.setup-main {
    /* Single centered column: every step uses the same content column, so the
       first step no longer reads as a two-pane layout. */
    width: 100%;
    max-width: 720px;
    margin-inline: auto;
}

.setup-form { min-width: 0; }
.site-section { min-width: 0; }
.site-section + .site-section { margin-top: 28px; }

/* The sync checkbox closes the brand block instead of heading it. */
.site-sync-toggle {
    margin-top: 16px;
    padding-left: 2px;
    font-size: 12.5px;
    font-weight: 500;
}

/* Actions: one centered cluster; Back sits to the left of the primary action
   instead of being pinned to the far edge. */
.wizard-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 28px;
}

.wizard-actions-start,
.wizard-actions-end {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-left: 0;
}

/* Buttons are static: no hover lift, no color/shadow change, no icon slide.
   Only the busy state (spinner + label) differs from rest. */
.wizard-btn {
    display: inline-flex;
    min-height: 46px;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 0 22px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font: inherit;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
}

.wizard-btn .bi {
    font-size: 14px;
    line-height: 1;
}

.wizard-btn-primary {
    border-color: var(--primary);
    background: var(--primary);
    color: #ffffff;
}
.wizard-btn-primary:disabled { cursor: not-allowed; opacity: .45; }
.wizard-btn-primary:focus-visible { outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent); outline-offset: 3px; }

.wizard-btn-secondary {
    border-color: var(--border-color);
    background: var(--bg);
    color: var(--dark);
}
.wizard-btn-secondary:focus-visible { outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent); outline-offset: 3px; }

.wizard-btn-danger {
    border-color: var(--danger);
    background: var(--danger);
    color: #fff;
}

/* Shared loading indicator: the installer does not load admin.css, so the
   submit buttons carry their own spinner. */
/* Busy buttons stay fully opaque so the spinner and label stay readable;
   only the pointer changes. */
.wizard-btn.is-busy,
.wizard-btn.is-busy:disabled { cursor: progress; opacity: 1; }

.wizard-link {
    color: var(--primary);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
}
.wizard-link:hover { color: var(--primary); }

/* ========================================================================== */
/* 7. Guide-page compatibility                                               */
/* ========================================================================== */

/* 安装向导不再有页面内横幅：所有「出问题了」的提示都走全局 Toast（见
   install_queue_problem()）。这里曾有一组 .wizard-alert 样式，留着会被后来人
   误用成横幅，所以直接移除。 */

.setup-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding-bottom: 20px;
}

.setup-icon {
    display: grid;
    flex: none;
    width: 36px;
    height: 36px;
    place-items: center;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--dark);
    font-size: 16px;
}

.setup-icon--warning { border-color: #fde68a; background: #fffbeb; color: #b45309; }

.setup-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0;
    color: color-mix(in srgb, var(--dark) 60%, var(--bg));
    font-size: 12px;
    line-height: 1.4;
}

.setup-breadcrumb [aria-current] { color: var(--dark); font-weight: 500; }

.setup-title {
    margin: 0;
    color: var(--dark);
    font-size: 15px;
    font-weight: 500;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.setup-subtitle {
    max-width: 60ch;
    margin-top: 3px;
    color: var(--dark);
    font-size: 12.5px;
    line-height: 1.5;
}

.setup-body {
    min-width: 0;
    color: var(--dark);
    font-size: 13.5px;
    line-height: 1.55;
}

.setup-body > :first-child { margin-top: 0; }
.setup-body > :last-child { margin-bottom: 0; }
.setup-body p { margin: 0 0 12px; }
.setup-body ul { margin: 6px 0 12px; padding-left: 18px; }
.setup-body code {
    padding: 1px 7px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--dark);
    font-family: "SFMono-Regular", Consolas, monospace;
    font-size: 11.5px;
    overflow-wrap: anywhere;
}
.setup-body pre { max-width: 100%; overflow: auto; font-size: 12px; }
.setup-body a { color: var(--dark); text-decoration: none; }
.setup-body a:hover { color: var(--dark); }

.setup-page .alert {
    margin: 0 0 16px;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--dark);
    font-size: 13px;
    line-height: 1.5;
}
.setup-page .alert-danger { border-color: #fecaca; background: #fef2f2; color: #991b1b; }
.setup-page .alert-success { border-color: #a7f3d0; background: #ecfdf5; color: #065f46; }
.setup-page .alert-warning { border-color: #fde68a; background: #fffbeb; color: #92400e; }
.setup-page .alert-info { border-color: var(--border-color); background: var(--bg); color: var(--dark); }

.config-note,
.issue-box {
    margin-bottom: 16px;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    background: var(--bg);
    color: var(--dark);
    font-size: 13px;
    line-height: 1.5;
}
.issue-box { border-color: #fde68a; background: #fffbeb; color: #92400e; }
.issue-box ul { margin: 6px 0 0; padding-left: 18px; }

.form-section-title {
    margin: 22px 0 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-color);
    color: var(--dark);
    font-size: 13px;
    font-weight: 500;
}
.form-section-title:first-child { margin-top: 0; }

.setup-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--bg);
}
.setup-file { min-width: 0; color: color-mix(in srgb, var(--dark) 60%, var(--bg)); font-size: 12px; overflow-wrap: anywhere; }

/* ========================================================================== */
/* 7b. Form guidance, validation and progressive disclosure                  */
/* ========================================================================== */

.wizard-form-error {
    margin: 0 0 18px;
    padding: 10px 12px;
    border: 1px solid #fecaca;
    border-radius: var(--radius-pill);
    background: #fef2f2;
    color: #991b1b;
    font-size: 12.5px;
    line-height: 1.45;
}

.wizard-form-error[hidden] { display: none; }

.wizard-field-error {
    margin: 0;
    color: var(--danger);
    font-size: 12px;
    line-height: 1.45;
}

.wizard-field-error:empty,
.wizard-field-error[hidden] { display: none; }

.wizard-field.has-error .form-control {
    border-color: #dc2626;
    box-shadow: none;
}

.wizard-field.has-error .form-control:focus {
    border-color: #dc2626;
    box-shadow: 0 0 0 3px rgba(220, 38, 38, .12);
}

.wizard-required {
    color: var(--danger);
    font-weight: 500;
}

.wizard-fieldset {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.wizard-fieldset[hidden] { display: none; }

.wizard-legend {
    margin: 6px 0 14px;
    padding: 0;
    color: var(--dark);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
}

.wizard-fieldset--plain { margin-top: 4px; }

.wizard-advanced {
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--bg);
}

.wizard-advanced > summary {
    display: inline-flex;
    min-height: 32px;
    align-items: center;
    gap: 8px;
    width: fit-content;
    color: var(--dark);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    list-style: none;
}

.wizard-advanced > summary::-webkit-details-marker { display: none; }
.wizard-advanced > summary .bi { font-size: 12px; transition: transform .18s ease; }
.wizard-advanced[open] > summary .bi { transform: rotate(180deg); }
.wizard-advanced > summary:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--primary) 18%, transparent);
    outline-offset: 3px;
    border-radius: var(--radius-pill);
}
.wizard-advanced[open] > summary { margin-bottom: 16px; }

[hidden] { display: none !important; }

/* ========================================================================== */
/* 8. Responsive                                                             */
/* ========================================================================== */

@media (max-width: 760px) {
    /* Space runs out for a single row, so the title goes above the pills. */
    .wizard-head {
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        margin-bottom: 0;
    }
    .wizard-brand { padding-top: 0; }
    .wizard-stepper { margin: 14px 0 24px; }
    .wizard-steps { justify-content: flex-start; }
    .wizard-step-label { font-size: 12px; }
}

@media (max-width: 560px) {
    .setup-panel { padding: 20px 16px; }
    .wizard-grid,
    .wizard-options { grid-template-columns: minmax(0, 1fr); }
    .wizard-field--full { grid-column: auto; }
    /* Phones stack the form above the preview, which makes step 1 far taller.
       All steps still share that height so the footer never jumps. */
    .wizard-panel { --wizard-body-h: 708px; }
    /* Stacked actions keep the primary button last and full width. */
    .wizard-actions {
        flex-direction: column-reverse;
        align-items: stretch;
        gap: 10px;
    }
    .wizard-actions-start,
    .wizard-actions-end { width: 100%; }
    .wizard-actions .wizard-btn { width: 100%; }
    /* Narrow header: the step list is not important info, so show only the
       current pill instead of wrapping or shrinking the whole set. */
    .wizard-steps { display: flex; flex-wrap: nowrap; }
    .wizard-step { display: none; }
    .wizard-step[data-status="current"] { display: flex; }
    .wizard-stepper[data-finished="1"] .wizard-step:last-child { display: flex; }
    .wizard-stepper[data-finished="1"] .wizard-step:last-child .wizard-step-head {
        background: var(--primary);
        color: #fff;
    }
    .wizard-step-head { padding: 5px 11px; gap: 6px; }
    .wizard-step-label { font-size: 11.5px; }
}

/* ========================================================================== */
/* 9. Motion preferences                                                     */
/* ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .setup-panel { animation: none; }
    .wizard-step-head,
    .wizard-step-label { transition: none; animation: none; }
    .setup-page .form-control,
    .wizard-option,
    .wizard-radio,
    .wizard-radio::after,
    .wizard-check input[type="checkbox"],
    .wizard-check input[type="checkbox"]::after,
    .wizard-pw-eye .bi { transition: none; }
    .ui-spinner { animation-duration: 2s; }
}
