` on rows
+ whose control is a single inline element (Input, Switch, Segmented,
+ Select). Leave the default vertical stack for full-width controls
+ (Textarea, multi-line forms). The 1px hairline divider between rows
+ pulls the look closer to reference's flat divider list and away from
+ the previous "each row in its own implicit card" feel. */
+.settingsField[data-orient="horizontal"] {
+ grid-template-columns: minmax(0, 1fr) auto;
+ grid-template-areas:
+ "label control"
+ "hint control";
+ column-gap: 16px;
+ row-gap: 2px;
+ align-items: center;
+ padding: 12px 0;
+ border-bottom: 1px solid var(--border);
+}
+.settingsField[data-orient="horizontal"] > span {
+ grid-area: label;
+ align-self: end;
+}
+.settingsField[data-orient="horizontal"] > small {
+ grid-area: hint;
+ margin-top: 0;
+ align-self: start;
+}
+.settingsField[data-orient="horizontal"] > :not(span):not(small) {
+ grid-area: control;
+ justify-self: end;
+ align-self: center;
+ min-width: 0;
+}
+/* Allow inputs to take a comfortable cap width on horizontal rows so
+ the description column has space to breathe.
+ PR-SETTINGS-INPUT-POLISH-0: cap from 280 → 320 since rows are now
+ roomier (24px L/R padding). */
+.settingsField[data-orient="horizontal"] > input,
+.settingsField[data-orient="horizontal"] > .settingsSelectTrigger {
+ max-width: 320px;
+}
+/* WAWQAQ msg `0951e3b1` 2026-06-25 + @kenji audit #5 (msg `26a221be`):
+ moved to `.settingsRow[data-control-width="select"] >
+ .settingsBaseSelectTrigger` so the contract lives on the row, not on
+ a global class selector. See the data-control-width block above. */
+/* The horizontal row above the first one shouldn't carry a top border;
+ below the last one the section footer takes over. The default
+ border-bottom is fine for the inter-row hairlines. */
+.settingsField[data-orient="horizontal"] + .settingsField[data-orient="horizontal"] {
+ border-top: 0;
+}
+
+/* PR-SETTINGS-INPUT-POLISH-0 (round 6/15, WAWQAQ msg `f7e9d166`):
+ inputs inside settings rows were at 12px / 26px min-height with
+ tight 0/8 padding — visibly thin against the now-bigger row
+ typography (15px label / 13px hint / 26×46 switch). Bump to a
+ 14px input with 34px min-height and proper padding. */
+.settingsField input,
+.settingsField textarea,
+.settingsFormGrid input,
+.settingsFormGrid select,
+.settingsUsageFilters input,
+.settingsUsageFilters select {
+ min-width: 0;
+ min-height: 34px;
+ border: 1px solid oklch(from var(--foreground) l c h / 0.12);
+ border-radius: 7px;
+ background: var(--background);
+ color: var(--foreground);
+ padding: 6px 10px;
+ font-size: 14px;
+ line-height: 1.4;
+ outline: none;
+ transition: border-color var(--duration-base) var(--ease-out-strong), box-shadow var(--duration-base) var(--ease-out-strong);
+}
+
+.settingsField textarea {
+ border-radius: 7px;
+ padding: 8px 10px;
+ line-height: 1.5;
+ font-family: inherit;
+ font-size: 14px;
+}
+
+.settingsField input:focus,
+.settingsField textarea:focus,
+.settingsFormGrid input:focus,
+.settingsFormGrid select:focus,
+.settingsUsageFilters input:focus,
+.settingsUsageFilters select:focus {
+ /* PR-UI-LAYOUT-21: was border-only at 0.3 accent — keyboard
+ * focus was hard to see. Added a 3px accent halo (matches the
+ * connection form / nav-primary / button family) and bumped
+ * border opacity 0.3 → 0.42 for parity. */
+ border-color: oklch(from var(--accent) l c h / 0.42);
+ box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.12);
+}
+
+.settingsNotice {
+ border-radius: 12px;
+ background: oklch(from var(--info) l c h / 0.10);
+ color: var(--info-text);
+ padding: 11px 12px;
+ font-size: 12px;
+ line-height: 1.45;
+}
+
+.settingsNotice[data-tone="passive"] {
+ background: var(--foreground-5);
+ color: var(--foreground-55);
+}
+
+.settingsActionRow {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+/* PR-SETTINGS-SHELL-OWNS-LAYOUT-0: shell now owns max-width + page
+ padding via `.settingsPageContentInner`. About page just manages
+ its own internal block stack. */
+.settingsAboutPage {
+ display: grid;
+ align-content: start;
+ gap: 16px;
+}
+
+.settingsAboutHero {
+ display: grid;
+ grid-template-columns: 48px minmax(0, 1fr);
+ align-items: center;
+ gap: 16px;
+ padding: 0;
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+}
+
+.settingsAboutLogo {
+ width: 48px;
+ height: 48px;
+ display: grid;
+ place-items: center;
+ border-radius: 12px;
+ background: oklch(from var(--accent) l c h / 0.10);
+ color: var(--accent);
+}
+
+.settingsAboutHeading {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin: 0 0 4px;
+}
+
+.settingsAboutHeading h2 {
+ margin: 0;
+ color: var(--foreground);
+ font-size: 22px;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+}
+
+.settingsAboutVersion {
+ font-family: var(--font-mono);
+ font-size: 13px;
+ color: var(--foreground-70);
+ padding: 2px 8px;
+ border-radius: 999px;
+ background: oklch(from var(--foreground) l c h / 0.05);
+}
+
+.settingsAboutChannel {
+ font-size: 11px;
+ color: var(--accent);
+ padding: 2px 8px;
+ border-radius: 999px;
+ background: oklch(from var(--accent) l c h / 0.10);
+ font-weight: 600;
+}
+
+.settingsAboutTagline {
+ margin: 0;
+ color: var(--foreground-70);
+ font-size: 13px;
+ line-height: 1.55;
+}
+
+.settingsAboutPrivacy {
+ display: grid;
+ gap: 8px;
+ padding: 14px 16px;
+ border: 1px solid oklch(from var(--success) l c h / 0.24);
+ border-radius: 12px;
+ background: oklch(from var(--success) l c h / 0.04);
+}
+
+.settingsAboutPrivacy h3 {
+ margin: 0;
+ color: var(--success);
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+.settingsAboutPrivacy ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ display: grid;
+ gap: 6px;
+ color: var(--foreground-70);
+ font-size: 13px;
+ line-height: 1.55;
+}
+
+.settingsAboutPrivacy li::marker {
+ color: var(--success);
+}
+
+.settingsThemeOptions {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.settingsThemeOption {
+ display: grid;
+ grid-template-columns: 40px minmax(0, 1fr);
+ align-items: center;
+ gap: 8px;
+ width: 100%;
+ height: auto;
+ min-height: 48px;
+ justify-content: stretch;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--background-elevated);
+ color: var(--foreground);
+ overflow: hidden;
+ padding: 8px;
+ text-align: left;
+ white-space: normal;
+}
+
+.settingsThemeOptionPreview {
+ grid-template-columns: 1fr;
+ grid-template-rows: auto auto;
+ align-items: stretch;
+ gap: 8px;
+ min-height: 116px;
+}
+
+/* Mini chat-surface mock rendered inside each theme tile. Hardcodes colors
+ * per variant so the preview doesn't track the active theme — comparing
+ * tiles is the whole point. */
+.settingsThemePreview {
+ width: 100%;
+ max-height: 70px;
+ aspect-ratio: 16 / 8;
+ border-radius: 10px;
+ overflow: hidden;
+ border: 1px solid var(--border);
+ display: flex;
+ background: var(--foreground-5);
+}
+
+.settingsThemePreviewSplit > .settingsThemePreviewPane:first-child {
+ border-right: 1px solid var(--border);
+}
+
+.settingsThemePreviewPane {
+ flex: 1 1 0;
+ display: grid;
+ grid-template-columns: 18px minmax(0, 1fr);
+ min-width: 0;
+}
+
+.settingsThemePreviewPane[data-mode="light"] {
+ /* PR-GRAY-CARD-LIFT-0: mirror the app shell after WAWQAQ's gray
+ plate / white content surface direction. */
+ background: oklch(1.000 0 0);
+ color: oklch(0.18 0 0);
+}
+
+.settingsThemePreviewPane[data-mode="dark"] {
+ background: oklch(0.21 0 0);
+ color: oklch(0.92 0 0);
+}
+
+.settingsThemePreviewSidebar {
+ height: 100%;
+}
+
+.settingsThemePreviewPane[data-mode="light"] .settingsThemePreviewSidebar {
+ background: oklch(0.955 0 0);
+ border-right: 1px solid oklch(0.88 0 0);
+}
+
+.settingsThemePreviewPane[data-mode="dark"] .settingsThemePreviewSidebar {
+ background: oklch(0.13 0 0);
+ border-right: 1px solid oklch(0.26 0 0);
+}
+
+.settingsThemePreviewChat {
+ padding: 6px 8px;
+ display: grid;
+ gap: 4px;
+ align-content: end;
+ min-width: 0;
+}
+
+.settingsThemePreviewLine {
+ height: 5px;
+ border-radius: 999px;
+ width: 90%;
+}
+
+.settingsThemePreviewLine-short {
+ width: 56%;
+}
+
+.settingsThemePreviewPane[data-mode="light"] .settingsThemePreviewLine-assistant {
+ background: oklch(0.78 0 0);
+}
+.settingsThemePreviewPane[data-mode="dark"] .settingsThemePreviewLine-assistant {
+ background: oklch(0.36 0 0);
+}
+
+.settingsThemePreviewBubble {
+ align-self: end;
+ justify-self: end;
+ width: 60%;
+ height: 12px;
+ border-radius: 7px 2px 7px 7px;
+ margin-top: 2px;
+}
+
+.settingsThemePreviewPane[data-mode="light"] .settingsThemePreviewBubble {
+ background: oklch(0.935 0 0);
+}
+.settingsThemePreviewPane[data-mode="dark"] .settingsThemePreviewBubble {
+ background: oklch(0.30 0 0);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .settingsThemePreview { transition: none; }
+}
+
+.settingsThemeOption:hover {
+ border-color: oklch(from var(--accent) l c h / 0.22);
+ background: var(--foreground-3);
+}
+
+.settingsThemeOption[data-active="true"] {
+ border-color: oklch(from var(--accent) l c h / 0.45);
+ background: oklch(from var(--accent) l c h / 0.06);
+}
+
+/* PR-THEME-PRODUCT-PALETTES-0: palette picker. Each option shows a
+ round swatch whose color is the palette's accent token. The swatch
+ colors are hard-coded here (not pulled from --accent) so a non-
+ active palette still previews its real color. */
+.settingsPaletteSwatch {
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ border: 2px solid var(--border);
+ box-shadow: inset 0 0 0 2px oklch(1 0 0 / 0.25);
+}
+/* PR-PALETTE-PICKER-GROUPS-0: subgroup container that gathers a small
+ heading (编辑器主题 / 产品色调) above its own grid. Spacing matches
+ the existing settingsSubheading rhythm so the page reads as one
+ continuous picker, just structured. */
+.settingsPaletteGroup {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+.settingsPaletteGroup + .settingsPaletteGroup {
+ margin-top: 8px;
+}
+.settingsPaletteGroupHeading {
+ margin: 0;
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--foreground-55);
+ letter-spacing: 0;
+}
+
+/* PR-BOT-SETTINGS-PASSWORD-EYE-0 (WAWQAQ msg `51c7b4ff` screenshots):
+
+ trailing Eye / EyeOff toggle, matching the
+ reference design's "Bot Token …………… 👁" affordance. The toggle is
+ a real button so keyboard users can flip it. */
+.settingsPasswordField {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+.settingsPasswordField input {
+ width: 100%;
+ padding-right: 56px;
+}
+.settingsPasswordActions {
+ position: absolute;
+ right: 4px;
+ top: 50%;
+ transform: translateY(-50%);
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+.settingsPasswordToggle {
+ display: grid;
+ place-items: center;
+ width: 22px;
+ height: 22px;
+ border: 0;
+ border-radius: 4px;
+ background: transparent;
+ color: var(--foreground-50);
+ transition: background var(--duration-base) var(--ease-out-strong), color var(--duration-base) var(--ease-out-strong);
+}
+.settingsPasswordToggle:hover {
+ background: var(--hover);
+ color: var(--foreground);
+}
+.settingsPasswordToggle:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 2px oklch(from var(--accent) l c h / 0.28);
+}
+
+/* PR-BOT-WECHAT-SCAN-LOGIN-0 (WAWQAQ msg `2fa6ada6` screenshots):
+ action buttons in bot settings follow the reference design — outlined
+ blue rounded pills, stacked vertically with a small gap. */
+.settingsBotActionStack {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ margin-top: 8px;
+}
+.settingsBotAction {
+ appearance: none;
+ min-width: 110px;
+ min-height: 36px;
+ padding: 0 22px;
+ border: 1px solid oklch(from var(--accent) l c h / 0.45);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--accent);
+ font-size: 13px;
+ font-weight: 500;
+ transition: background var(--duration-quick) var(--ease-out-strong), border-color var(--duration-quick) var(--ease-out-strong), color var(--duration-quick) var(--ease-out-strong);
+}
+.settingsBotAction:hover {
+ background: oklch(from var(--accent) l c h / 0.08);
+}
+.settingsBotAction:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.22);
+}
+.settingsBotAction[disabled] {
+ opacity: 0.45;
+ cursor: not-allowed;
+}
+
+/* Inline hint after a field label — mirrors "(国内网络必填)" /
+ "(仅用于 Bot 鉴权)" in the reference. */
+.settingsFieldHint {
+ font-style: normal;
+ font-weight: 400;
+ color: var(--foreground-50);
+ font-size: 11.5px;
+ margin-left: 6px;
+}
+
+/* Amber info notice with a leading ⓘ glyph — mirrors the reference
+ design's TUN-mode hints in Telegram + Discord. */
+.settingsBotInfoNotice {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 10px 12px;
+ border-radius: 10px;
+ background: oklch(from var(--info) l c h / 0.10);
+ border: 1px solid oklch(from var(--info) l c h / 0.18);
+ color: var(--info-text);
+ font-size: 12.5px;
+ line-height: 1.5;
+}
+.settingsBotInfoNoticeIcon {
+ font-style: normal;
+ font-weight: 500;
+ color: var(--info);
+ line-height: 1.4;
+}
+
+/* PR-BOT-WECHAT-QR-MODAL-0 (WAWQAQ msg `10ec1fbe`): scan-login modal —
+ compact dialog centered above the backdrop with the QR + status. */
+.settingsBotScanLoginModal {
+ background: var(--background);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 14px 16px;
+ width: min(320px, 92vw);
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ box-shadow: var(--shadow-minimal);
+}
+.settingsBotScanLoginHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+.settingsBotScanLoginHeader h3 {
+ margin: 0;
+ font-size: 13px;
+ font-weight: 600;
+}
+.settingsBotScanLoginBody {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ text-align: center;
+}
+.settingsBotScanLoginQr {
+ width: 180px;
+ height: 180px;
+ border-radius: 8px;
+ background: white;
+ padding: 8px;
+ border: 1px solid var(--border);
+ object-fit: contain;
+}
+.settingsBotScanLoginQrPlaceholder {
+ width: 180px;
+ height: 180px;
+ display: grid;
+ place-items: center;
+ border-radius: 8px;
+ background: var(--foreground-5);
+ border: 1px dashed var(--border);
+ color: var(--foreground-40);
+ font-size: 24px;
+}
+.settingsBotScanLoginStatus {
+ margin: 0;
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--foreground-70);
+}
+.settingsBotScanLoginStatus[data-status="confirmed"] { color: var(--success-text); }
+.settingsBotScanLoginStatus[data-status="error"] { color: var(--destructive-text); }
+.settingsBotScanLoginStatus[data-status="expired"] { color: var(--info-text); }
+.settingsBotScanLoginActions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+/* WeChat 高级设置 collapsible toggle. */
+.settingsBotAdvanced {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.settingsBotAdvancedToggle {
+ align-self: flex-start;
+ appearance: none;
+ border: 0;
+ background: transparent;
+ color: var(--foreground-55);
+ padding: 4px 0;
+ font-size: 12px;
+ text-decoration: underline;
+ text-decoration-style: dotted;
+ text-underline-offset: 3px;
+}
+.settingsBotAdvancedToggle:hover {
+ color: var(--foreground);
+}
+.settingsBotAdvancedBody {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding-left: 12px;
+ border-left: 2px solid var(--border);
+}
+
+.settingsWechatQrBackdrop {
+ position: fixed;
+ inset: 0;
+ z-index: calc(var(--z-modal) + 1);
+ display: grid;
+ place-items: center;
+ background: oklch(0.34 0.01 80 / 0.34);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+}
+
+.settingsWechatQrModal {
+ width: min(360px, calc(100vw - 40px));
+ display: grid;
+ gap: 18px;
+ border: 1px solid color-mix(in srgb, #07C160 18%, var(--border));
+ border-radius: 12px;
+ background: var(--background);
+ box-shadow:
+ 0 24px 58px oklch(0 0 0 / 0.18),
+ 0 8px 18px oklch(0 0 0 / 0.10);
+ padding: 18px;
+}
+
+.settingsWechatQrHeader {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 12px;
+}
+
+.settingsWechatQrHeader h3 {
+ margin: 0 0 4px;
+ font-size: 16px;
+ font-weight: 650;
+}
+
+.settingsWechatQrHeader p,
+.settingsWechatQrCaption {
+ margin: 0;
+ color: var(--foreground-55);
+ font-size: 12.5px;
+ line-height: 1.5;
+}
+
+.settingsWechatQrClose {
+ width: 30px;
+ height: 30px;
+ display: grid;
+ place-items: center;
+ border: 0;
+ border-radius: 8px;
+ background: transparent;
+ color: var(--foreground-55);
+}
+
+.settingsWechatQrClose:hover {
+ background: var(--hover);
+ color: var(--foreground);
+}
+
+.settingsWechatQrClose:focus-visible,
+.settingsWechatQrSecondary:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.24);
+}
+
+.settingsWechatQrBody {
+ display: grid;
+ place-items: center;
+ gap: 12px;
+ text-align: center;
+}
+
+.settingsWechatQrFrame {
+ width: 224px;
+ height: 224px;
+ display: grid;
+ place-items: center;
+ border: 1px solid color-mix(in srgb, #07C160 20%, var(--border));
+ border-radius: 12px;
+ background: white;
+ padding: 12px;
+}
+
+.settingsWechatQrFrame img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ image-rendering: pixelated;
+}
+
+.settingsWechatQrState {
+ width: 100%;
+ min-height: 180px;
+ display: grid;
+ place-items: center;
+ align-content: center;
+ gap: 10px;
+ border: 1px dashed var(--border);
+ border-radius: 12px;
+ background: var(--foreground-3);
+ color: var(--foreground-60);
+ padding: 18px;
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+.settingsWechatQrState strong {
+ color: inherit;
+ font-weight: 600;
+}
+
+.settingsWechatQrState[data-tone="success"] {
+ border-color: oklch(from var(--success) l c h / 0.24);
+ background: oklch(from var(--success) l c h / 0.08);
+ color: var(--success-text);
+}
+
+.settingsWechatQrState[data-tone="warning"] {
+ border-color: oklch(from var(--info) l c h / 0.26);
+ background: oklch(from var(--info) l c h / 0.09);
+ color: var(--info-text);
+}
+
+.settingsWechatQrState[data-tone="error"] {
+ border-color: oklch(from var(--destructive) l c h / 0.24);
+ background: oklch(from var(--destructive) l c h / 0.08);
+ color: var(--destructive-text);
+}
+
+.settingsWechatQrSecondary {
+ min-height: 30px;
+ border: 1px solid oklch(from var(--accent) l c h / 0.34);
+ border-radius: 8px;
+ background: var(--background);
+ color: var(--accent);
+ padding: 0 12px;
+ font-size: 12.5px;
+ font-weight: 500;
+}
+
+.settingsWechatQrSecondary:hover {
+ background: oklch(from var(--accent) l c h / 0.08);
+}
+
+.settingsWechatQrSecondary:disabled {
+ opacity: 0.58;
+ cursor: progress;
+}
+
+.settingsThemeLabel {
+ display: grid;
+ min-width: 0;
+ gap: 4px;
+}
+
+.settingsThemeLabel strong {
+ color: var(--foreground);
+ font-size: 14px;
+ font-weight: 600;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.settingsThemeLabel small {
+ color: var(--foreground-60);
+ font-size: 12px;
+ line-height: 1.4;
+ overflow-wrap: anywhere;
+}
+
+@media (max-width: 820px) {
+ .settingsThemeOptions {
+ grid-template-columns: 1fr;
+ }
+}
+
+.settingsSubheading {
+ margin: 0;
+ color: var(--foreground-70);
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+.settingsSubheading + .settingsThemeOptions {
+ margin-top: -6px;
+}
+
+.settingsBotLayout {
+ min-height: 470px;
+ display: grid;
+ grid-template-columns: 180px minmax(0, 1fr);
+ gap: 22px;
+}
+
+.settingsBotList {
+ display: grid;
+ align-content: start;
+ gap: 6px;
+ border-right: 1px solid var(--border);
+ padding-right: 22px;
+}
+
+.settingsBotList button {
+ min-height: 44px;
+ display: grid;
+ grid-template-columns: 30px minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 10px;
+ border: 0;
+ border-radius: 12px;
+ background: transparent;
+ color: var(--foreground-70);
+ padding: 7px 10px;
+ text-align: left;
+}
+
+.settingsBotList button[data-active="true"] {
+ background: var(--active);
+ color: var(--foreground);
+}
+
+.settingsBotList em {
+ font-size: 10px;
+ font-style: normal;
+ color: var(--foreground-55);
+}
+
+.settingsBotList em[data-tone="info"] { color: var(--accent); }
+.settingsBotList em[data-tone="success"] { color: var(--success-text); }
+.settingsBotList em[data-tone="warning"] { color: var(--info-text); }
+.settingsBotList em[data-tone="destructive"] { color: var(--destructive-text); }
+
+/* PR-PLACEHOLDER-SWEEP-0: planned-only platforms render their tab
+ muted so users don't mistake them for credentialed-and-ready
+ options. Clicking still navigates so the user can read the
+ unavailable-state notice on the detail pane. */
+.settingsBotList button[data-support="planned"] {
+ opacity: 0.55;
+}
+.settingsBotList button[data-support="planned"]:hover {
+ opacity: 0.75;
+}
+.settingsBotList button[data-support="planned"][data-active="true"] {
+ opacity: 1;
+}
+.settingsBotList button[data-support="planned"] em {
+ border: 1px solid var(--foreground-15);
+ border-radius: 999px;
+ padding: 0 6px;
+ color: var(--foreground-50);
+ font-style: normal;
+ letter-spacing: 0.04em;
+}
+
+/* PR-BOT-SETTINGS-UI-0 (WAWQAQ msg `51c7b4ff`): brand-tinted monogram
+ badge. `--bot-brand-color` is the per-platform brand color set
+ inline by BotBrandLogo. Background uses ~14% tint so the brand
+ reads clearly without overpowering the row. */
+.settingsBotLogo {
+ position: relative;
+ width: 28px;
+ height: 28px;
+ display: grid;
+ place-items: center;
+ border-radius: 9px;
+ background: color-mix(in srgb, var(--bot-brand-color, var(--accent)) 14%, var(--background));
+ color: var(--bot-brand-color, var(--accent));
+ font-size: 13px;
+ font-weight: 700;
+ letter-spacing: 0;
+ flex-shrink: 0;
+}
+
+.settingsBotLogo[data-large="true"] {
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ font-size: 20px;
+}
+
+.settingsBotLogoStatusDot {
+ position: absolute;
+ right: -2px;
+ bottom: -2px;
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+ background: var(--foreground-30);
+ box-shadow: 0 0 0 2px var(--background);
+}
+.settingsBotLogo[data-large="true"] .settingsBotLogoStatusDot {
+ width: 11px;
+ height: 11px;
+ right: -3px;
+ bottom: -3px;
+ box-shadow: 0 0 0 2.5px var(--background);
+}
+.settingsBotLogoStatusDot[data-tone="info"] { background: var(--accent); }
+.settingsBotLogoStatusDot[data-tone="success"] { background: var(--success); }
+.settingsBotLogoStatusDot[data-tone="warning"] { background: var(--info); }
+.settingsBotLogoStatusDot[data-tone="destructive"] { background: var(--destructive); }
+
+.settingsBotDetail {
+ display: grid;
+ align-content: start;
+ gap: 14px;
+}
+
+/* PR-BOT-SETTINGS-UI-0 (WAWQAQ msg `51c7b4ff`): brand-tinted hero
+ card. Background uses the brand color at ~7% so the platform
+ identity is visible without overpowering the form. Telegram-style
+ light blue, WeChat green, Discord purple, etc. */
+.settingsBotHero {
+ min-height: 76px;
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 14px;
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--bot-brand-color, var(--accent)) 7%, var(--background));
+ border: 1px solid color-mix(in srgb, var(--bot-brand-color, var(--accent)) 14%, transparent);
+ padding: 16px 18px;
+}
+
+.settingsBotHero[data-support="planned"] {
+ background: var(--foreground-5);
+ border-color: var(--border);
+}
+
+/* Re-export the brand color on the hero card so child logo + accents
+ pick up the right CSS var without needing a second prop pass. */
+.settingsBotHero[data-provider="telegram"] { --bot-brand-color: #229ED9; }
+.settingsBotHero[data-provider="feishu"] { --bot-brand-color: #00C6B7; }
+.settingsBotHero[data-provider="wecom"] { --bot-brand-color: #0089FF; }
+.settingsBotHero[data-provider="wechat"] { --bot-brand-color: #07C160; }
+.settingsBotHero[data-provider="discord"] { --bot-brand-color: #5865F2; }
+.settingsBotHero[data-provider="dingtalk"] { --bot-brand-color: #1372FB; }
+.settingsBotHero[data-provider="qq"] { --bot-brand-color: #EB1923; }
+
+.settingsBotHeroBody {
+ min-width: 0;
+}
+
+.settingsBotHero h3 {
+ margin: 0 0 4px;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.settingsBotHero small,
+.settingsHelpText {
+ color: var(--foreground-55);
+ font-size: 12.5px;
+ line-height: 1.5;
+}
+
+.settingsBotEnableHint {
+ display: block;
+ margin-top: 4px;
+ color: var(--warning-text, var(--foreground-70));
+}
+
+.settingsBotConfigDocLink {
+ color: var(--bot-brand-color, var(--accent));
+ text-decoration: none;
+ font-weight: 500;
+ white-space: nowrap;
+}
+.settingsBotConfigDocLink:hover {
+ text-decoration: underline;
+}
+
+.settingsBotStatusPill {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 2px 9px 2px 7px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--foreground) 5%, transparent);
+ color: var(--foreground-70);
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.01em;
+}
+
+.settingsBotStatusPillDot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--foreground-40);
+}
+.settingsBotStatusPill[data-tone="info"] .settingsBotStatusPillDot { background: var(--accent); }
+.settingsBotStatusPill[data-tone="success"] { color: var(--success-text); }
+.settingsBotStatusPill[data-tone="success"] .settingsBotStatusPillDot { background: var(--success); }
+.settingsBotStatusPill[data-tone="warning"] { color: var(--info-text); }
+.settingsBotStatusPill[data-tone="warning"] .settingsBotStatusPillDot { background: var(--info); }
+.settingsBotStatusPill[data-tone="destructive"] { color: var(--destructive-text); }
+.settingsBotStatusPill[data-tone="destructive"] .settingsBotStatusPillDot { background: var(--destructive); }
+
+.settingsHelpText {
+ margin: 0;
+}
+
+.settingsBotStatusGrid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 8px;
+ margin: 0;
+}
+
+.settingsBotStatusGrid div {
+ min-height: 52px;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: var(--foreground-3);
+ padding: 10px 12px;
+}
+
+@media (max-width: 1100px) {
+ .settingsBotStatusGrid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+.settingsBotStatusGrid dt {
+ margin-bottom: 4px;
+ color: var(--foreground-45);
+ font-size: 11px;
+}
+
+.settingsBotStatusGrid dd {
+ margin: 0;
+ color: var(--foreground);
+ font-size: 12px;
+ word-break: break-word;
+}
+
+.settingsBotReason {
+ border-left: 2px solid var(--accent);
+ color: var(--foreground-60);
+ font-size: 12px;
+ line-height: 1.5;
+ padding-left: 10px;
+}
+
+/* PR-BOT-CHAT-POLISH-0: error tone for persisted lastError so the
+ user can spot a failed test past the toast lifetime. */
+.settingsBotReason[data-tone="error"] {
+ border-left-color: var(--destructive, var(--warning));
+ color: var(--destructive-text, var(--info-text));
+}
+
+.settingsUsagePage {
+ display: grid;
+ align-content: start;
+ gap: 10px;
+}
+
+.settingsUsageToolbar,
+.settingsUsageFilters {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.settingsSegmented {
+ justify-self: start;
+ width: fit-content;
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ border-radius: 999px;
+ background: var(--foreground-5);
+ padding: 2px;
+}
+
+.settingsSegmented button {
+ min-height: 22px;
+ border: 0;
+ border-radius: 999px;
+ background: transparent;
+ color: var(--foreground-60);
+ padding: 0 8px;
+ font-size: 11px;
+}
+
+.settingsSegmented button[data-active="true"] {
+ background: var(--background);
+ color: var(--foreground);
+ box-shadow: 0 1px 2px oklch(0 0 0 / 0.08);
+}
+
+.settingsUsageSummary {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 6px;
+}
+
+.settingsMetricCard {
+ min-height: 52px;
+ display: grid;
+ align-content: center;
+ gap: 2px;
+ border-radius: 8px;
+ background: var(--foreground-5);
+ padding: 6px 10px;
+}
+
+.settingsMetricCard small,
+.settingsMetricCard span {
+ color: var(--foreground-50);
+ font-size: 10px;
+}
+
+.settingsMetricCard strong {
+ font-size: 14px;
+}
+
+.settingsStatsTable {
+ width: 100%;
+ border-collapse: collapse;
+ overflow: hidden;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ font-size: 11px;
+}
+
+.settingsStatsTable th,
+.settingsStatsTable td {
+ border-bottom: 1px solid var(--border);
+ color: var(--foreground-60);
+ padding: 5px 8px;
+ text-align: left;
+}
+
+.settingsStatsTable th {
+ color: var(--foreground-70);
+ font-weight: 600;
+}
+
+.settingsCloseButton {
+ width: 24px;
+ height: 24px;
+ display: grid;
+ place-items: center;
+ border: 0;
+ border-radius: 6px;
+ background: transparent;
+ color: var(--foreground-50);
+ line-height: 1;
+}
+
+.settingsCloseButton svg {
+ width: 14px;
+ height: 14px;
+}
+
+.settingsCloseButton:hover {
+ background: var(--foreground-5);
+ color: var(--foreground);
+}
+
+.settingsRow {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+}
+
+.settingsBadge {
+ display: inline-flex;
+ align-items: center;
+ min-height: 18px;
+ border-radius: 4px;
+ background: var(--foreground-5);
+ color: var(--foreground-60);
+ padding: 0 6px;
+ font-size: 10px;
+}
+
+/* PR-SETTINGS-GROUPED-CARD-0 (WAWQAQ msg `1abecd66`): same grouped-
+ card pattern as `.settingsStructuredPage` — outer card + hairline
+ dividers, no per-row chrome. */
+.settingsRows {
+ display: grid;
+ gap: 0;
+ border: 1px solid oklch(from var(--foreground) l c h / 0.08);
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+/* Per-connection card on AccountSettingsPage. Status badge tone derives
+ * from the closed-form ConnectionUiStatus enum:
+ * neutral / info / success / warning / destructive.
+ */
+.settingsConnectionList {
+ /* PR-CONNECTION-LIST-A11Y-0 (round 17/30): switched from
+ `
` to semantic `
`. Reset the UA's
+ default bullet + 40px padding-left so the grid layout
+ stays the same. */
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ gap: 10px;
+}
+
+.settingsConnectionList > li {
+ margin: 0;
+ padding: 0;
+}
+
+.settingsConnectionRow {
+ display: grid;
+ gap: 6px;
+ padding: 12px 14px;
+ border-radius: 8px;
+ border: 1px solid var(--border);
+ background: var(--foreground-2);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
+}
+
+.settingsConnectionRow[data-status="verified"] {
+ border-color: oklch(from var(--success) l c h / 0.24);
+ background: oklch(from var(--success) l c h / 0.04);
+}
+.settingsConnectionRow[data-status="needs_reauth"] {
+ border-color: oklch(from var(--info) l c h / 0.30);
+ background: oklch(from var(--info) l c h / 0.06);
+}
+.settingsConnectionRow[data-status="error"] {
+ border-color: oklch(from var(--destructive) l c h / 0.30);
+ background: oklch(from var(--destructive) l c h / 0.05);
+}
+.settingsConnectionRow[data-status="not_configured"] {
+ border-color: oklch(from var(--info) l c h / 0.20);
+ background: oklch(from var(--info) l c h / 0.03);
+}
+
+/* PR-OAUTH-SUBSCRIPTION-0: Claude subscription card states. */
+.settingsConnectionRow[data-status="not_logged_in"] {
+ border-color: var(--border);
+ background: var(--foreground-2);
+}
+.settingsConnectionRow[data-status="authorizing"],
+.settingsConnectionRow[data-status="refreshing"] {
+ border-color: oklch(from var(--info) l c h / 0.30);
+ background: oklch(from var(--info) l c h / 0.04);
+}
+.settingsConnectionRow[data-status="authenticated"] {
+ border-color: oklch(from var(--success) l c h / 0.24);
+ background: oklch(from var(--success) l c h / 0.04);
+}
+.settingsConnectionRow[data-status="refresh_failed"],
+.settingsConnectionRow[data-status="quota_unavailable"] {
+ border-color: oklch(from var(--warning, var(--info)) l c h / 0.30);
+ background: oklch(from var(--warning, var(--info)) l c h / 0.05);
+}
+.settingsConnectionRow[data-status="provider_rejected"] {
+ border-color: oklch(from var(--destructive) l c h / 0.30);
+ background: oklch(from var(--destructive) l c h / 0.05);
+}
+
+.settingsQuotaSection {
+ display: grid;
+ gap: 4px;
+ padding: 8px 10px;
+ border-radius: 8px;
+ background: oklch(from var(--foreground) l c h / 0.03);
+}
+.settingsQuotaRow {
+ display: flex;
+ justify-content: space-between;
+ font-size: 12px;
+ font-variant-numeric: tabular-nums;
+}
+
+.settingsOauthPastePanel {
+ display: grid;
+ gap: 8px;
+ margin-top: 4px;
+ padding: 12px 14px;
+ border-radius: 8px;
+ background: oklch(from var(--foreground) l c h / 0.04);
+}
+.settingsOauthPastePanel textarea {
+ width: 100%;
+ padding: 8px;
+ border-radius: 6px;
+ border: 1px solid var(--border);
+ background: var(--background);
+ color: var(--foreground);
+ font-family: var(--mono-font, monospace);
+ font-size: 12px;
+ resize: vertical;
+}
+.settingsErrorText {
+ color: var(--destructive-text, var(--destructive, red));
+}
+.settingsConnectionRow[data-status="disabled"] {
+ opacity: 0.72;
+}
+
+.settingsConnectionRow[data-default="true"] {
+ box-shadow: 0 0 0 2px oklch(from var(--accent) l c h / 0.12);
+}
+
+.settingsConnectionRowName {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+}
+
+.settingsConnectionDefaultBadge {
+ display: inline-flex;
+ align-items: center;
+ padding: 1px 7px;
+ border-radius: 4px;
+ background: oklch(from var(--accent) l c h / 0.14);
+ color: var(--accent);
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: 0;
+}
+
+.settingsConnectionRowHead {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.settingsConnectionRowText {
+ display: grid;
+ gap: 2px;
+ min-width: 0;
+}
+
+.settingsConnectionRowText strong {
+ color: var(--foreground);
+ font-size: 13px;
+ font-weight: 600;
+}
+
+.settingsConnectionRowText small {
+ color: var(--foreground-50);
+ font-family: var(--font-mono);
+ font-size: 11px;
+}
+
+.settingsConnectionBadge {
+ display: inline-flex;
+ align-items: center;
+ min-height: 20px;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0;
+ background: var(--foreground-5);
+ color: var(--foreground-60);
+ white-space: nowrap;
+}
+
+.settingsConnectionBadge[data-tone="success"] {
+ background: oklch(from var(--success) l c h / 0.12);
+ color: var(--success);
+}
+.settingsConnectionBadge[data-tone="info"] {
+ background: oklch(from var(--info) l c h / 0.14);
+ color: var(--info-text);
+}
+.settingsConnectionBadge[data-tone="warning"] {
+ background: oklch(from var(--info) l c h / 0.18);
+ color: var(--info-text);
+ font-weight: 700;
+}
+.settingsConnectionBadge[data-tone="destructive"] {
+ background: oklch(from var(--destructive) l c h / 0.15);
+ color: var(--destructive);
+ font-weight: 700;
+}
+
+.settingsConnectionDetail {
+ margin: 0;
+ color: var(--foreground-70);
+ font-size: 11px;
+ line-height: 1.45;
+}
+
+.settingsAuthContract {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 8px 10px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--foreground-3);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
+}
+
+.settingsAuthContractText {
+ display: grid;
+ gap: 2px;
+ min-width: 0;
+}
+
+.settingsAuthContractText strong {
+ color: var(--foreground);
+ font-size: 12px;
+ font-weight: 650;
+}
+
+.settingsAuthContractText span {
+ color: var(--foreground-60);
+ font-size: 11px;
+ line-height: 1.45;
+}
+
+.settingsAuthContractBadge,
+.settingsAuthActionPill {
+ display: inline-flex;
+ align-items: center;
+ min-height: 20px;
+ border-radius: 4px;
+ white-space: nowrap;
+ font-size: 11px;
+ font-weight: 650;
+}
+
+.settingsAuthContractBadge {
+ padding: 2px 9px;
+ background: var(--foreground-5);
+ color: var(--foreground-60);
+}
+
+.settingsAuthContractBadge[data-tone="success"],
+.settingsAuthActionPill[data-tone="success"] {
+ background: oklch(from var(--success) l c h / 0.12);
+ color: var(--success);
+}
+
+.settingsAuthContractBadge[data-tone="info"],
+.settingsAuthActionPill[data-tone="info"] {
+ background: oklch(from var(--info) l c h / 0.14);
+ color: var(--info-text);
+}
+
+.settingsAuthContractBadge[data-tone="warning"],
+.settingsAuthActionPill[data-tone="warning"] {
+ background: oklch(from var(--info) l c h / 0.18);
+ color: var(--info-text);
+}
+
+.settingsAuthContractBadge[data-tone="destructive"],
+.settingsAuthActionPill[data-tone="destructive"] {
+ background: oklch(from var(--destructive) l c h / 0.14);
+ color: var(--destructive);
+}
+
+.settingsConnectionMeta {
+ margin: 0;
+ display: flex;
+ gap: 12px;
+ color: var(--foreground-50);
+ font-size: 11px;
+ font-family: var(--font-mono);
+}
+
+.settingsInlineFileState {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.settingsInlineTextButton {
+ border: 0;
+ padding: 0;
+ background: transparent;
+ color: var(--accent);
+ font: inherit;
+}
+
+.settingsInlineTextButton:hover {
+ text-decoration: underline;
+}
+
+.settingsConnectionActions {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 8px;
+ margin-top: 4px;
+}
+
+.settingsAuthActionPill {
+ padding: 2px 9px;
+ background: var(--foreground-5);
+ color: var(--foreground-60);
+}
+
+.settingsAuthActionPill[data-kind="preview"] {
+ border: 1px dashed oklch(from var(--info) l c h / 0.30);
+}
+
+/* PR-SETTINGS-GROUPED-CARD-0: rows flush inside outer card, hairline
+ divider, last-child no border. Matches the reference grouped-list
+ pattern. */
+.settingsRow {
+ display: grid;
+ grid-template-columns: minmax(150px, 0.36fr) minmax(0, 1fr);
+ align-items: center;
+ gap: 16px;
+ padding: 16px 20px;
+ border: 0;
+ border-bottom: 1px solid oklch(from var(--foreground) l c h / 0.06);
+ border-radius: 0;
+ background: transparent;
+ transition: background var(--duration-base) var(--ease-out-strong);
+}
+
+.settingsRow:last-child {
+ border-bottom: 0;
+}
+
+.settingsRow:hover {
+ background: oklch(from var(--foreground) l c h / 0.025);
+}
+
+.settingsRow > div {
+ min-width: 0;
+}
+
+.settingsRow small {
+ display: block;
+ margin-top: 4px;
+ color: var(--foreground-55);
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+.providersPanel {
+ min-height: 420px;
+ display: grid;
+ grid-template-columns: minmax(190px, 0.82fr) minmax(0, 1.5fr);
+ gap: 12px;
+}
+
+.settingsSurface[data-modal="true"] .providersPanel {
+ min-height: 480px;
+ grid-template-columns: minmax(190px, 0.7fr) minmax(250px, 1fr);
+}
+
+.providerEditor h3 {
+ margin: 0;
+ color: var(--foreground);
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.providerEditor p {
+ margin: 0;
+ color: var(--foreground-50);
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+.catalogTabs {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.catalogTab {
+ min-height: 38px;
+ display: grid;
+ gap: 2px;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ background: var(--foreground-3);
+ color: var(--foreground);
+ padding: 6px 8px;
+ text-align: left;
+}
+
+.catalogTab:hover {
+ background: var(--foreground-5);
+}
+
+.catalogTab[data-active="true"] {
+ border-color: oklch(from var(--accent) l c h / 0.16);
+ background: oklch(from var(--accent) l c h / 0.08);
+}
+
+.catalogTabs strong {
+ font-size: 11px;
+ font-weight: 600;
+}
+
+.catalogTabs small {
+ color: var(--foreground-50);
+ font-size: 10px;
+ line-height: 1.2;
+}
+
+/* Provider catalog + OAuth login render as governed Item rows (media ·
+ * content · actions) — the same shadcn primitive the enabled connections
+ * use — inside a seamless hairline list. This replaces the hand-written
+ * .providerCatalogCard grid so the whole 模型 page speaks one component
+ * language (component governance). */
+.catalogGrid {
+ min-height: 0;
+ display: grid;
+ grid-template-columns: 1fr;
+ align-content: start;
+ overflow: auto;
+}
+
+.providerCatalogRow {
+ gap: 12px;
+ padding: 11px 6px;
+}
+
+/* Gated providers (preview / not-yet-open) stay inert and muted; the
+ * provider-surface gate forbids rendering them as actionable. */
+.providerCatalogRow[data-disabled="true"] {
+ opacity: 0.66;
+ cursor: not-allowed;
+ filter: grayscale(0.28);
+}
+
+.providerCatalogTitle {
+ font-weight: 600;
+}
+
+/* Trailing badge + chevron live in ItemActions (a direct child of the row),
+ * so they sit vertically centered against the full two-line row rather than
+ * pinned to the title line. */
+.providerCatalogActions {
+ gap: 10px;
+}
+
+.providerCatalogCount {
+ margin-left: 8px;
+ color: var(--foreground-50);
+ font-size: 12px;
+}
+
+.providerCatalogChevron {
+ flex: 0 0 auto;
+ color: var(--foreground-40);
+}
+
+/* category / preview / login badges — compact squared corners, never pills */
+.providerCatalogBadge {
+ flex: 0 0 auto;
+ width: fit-content;
+ border-radius: 4px;
+ background: var(--foreground-5);
+ color: var(--foreground-60);
+ padding: 2px 7px;
+ font-size: 10px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 1.35;
+}
+
+.providerEditor {
+ display: grid;
+ gap: 12px;
+}
+
+.providerUnavailableNotice {
+ display: grid;
+ gap: 4px;
+ border: 1px solid oklch(from var(--info) l c h / 0.22);
+ border-radius: 12px;
+ background: oklch(from var(--info) l c h / 0.10);
+ color: var(--info-text);
+ padding: 12px;
+}
+
+.providerUnavailableNotice span {
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+/* Direct child only: this is the sheet's own title/badges header. The
+ * descendant form (`.providerEditor header`) also matched the nested
+ * `.modelTableHeader`, leaking the close-button padding-right onto the
+ * model card and pushing its refresh button 36px off the right edge. */
+.providerEditor > header {
+ display: flex;
+ align-items: start;
+ justify-content: space-between;
+ gap: 12px;
+ /* Reserve the top-right corner for the absolutely-positioned sheet
+ * close button (.providerConfigSheetClose: 32px wide at right:14px,
+ * intruding ~30px into the 16px-padded content box). Without this the
+ * right-aligned header badges (默认 / 国内) slide under the X. */
+ padding-right: 36px;
+}
+
+.providerEditor label {
+ display: grid;
+ gap: 6px;
+ color: var(--foreground-60);
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.providerEditor input,
+.providerEditor select {
+ min-width: 0;
+ min-height: 36px;
+ border: 1px solid var(--border);
+ /* PR-UI-LAYOUT-22: provider editor inputs (used in the
+ * ConfigureProvider sheet) brought up to the LAYOUT-11/21 focus
+ * ring family. Was: radius 6 + border-only focus.
+ * Now: radius 8 + min-height 36 + 3px halo + smoother transition.
+ * Matches connectionForm inputs visually so users see the same
+ * "this is a focused input" signal everywhere. */
+ border-radius: 8px;
+ background: var(--background-elevated);
+ color: var(--foreground);
+ padding: 0 12px;
+ outline: none;
+ font-size: 13px;
+ transition: border-color 140ms var(--ease-out-strong), box-shadow 140ms var(--ease-out-strong), background 140ms var(--ease-out-strong);
+}
+
+.providerEditor input:focus,
+.providerEditor select:focus {
+ border-color: oklch(from var(--accent) l c h / 0.42);
+ background: var(--background);
+ box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.10);
+}
+
+.providerActions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.providerHeaderBadges {
+ display: inline-flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ gap: 6px;
+}
+
+.providerExternalLink {
+ width: fit-content;
+ color: var(--accent);
+ font-size: 12px;
+ font-weight: 600;
+ text-decoration: none;
+ /* PR-UI-LAYOUT-33: explicit padding + radius so the focus halo
+ * has a defined shape. Was a borderless inline link with no
+ * focus-visible — keyboard users couldn't see it. */
+ padding: 2px 6px;
+ margin: -2px -6px;
+ border-radius: 6px;
+ transition: background var(--duration-quick) var(--ease-out-strong), box-shadow var(--duration-quick) var(--ease-out-strong), text-decoration-color var(--duration-quick) var(--ease-out-strong);
+}
+
+.providerExternalLink:hover {
+ text-decoration: underline;
+}
+
+.providerExternalLink:focus-visible {
+ outline: none;
+ background: oklch(from var(--accent) l c h / 0.06);
+ box-shadow: 0 0 0 3px oklch(from var(--accent) l c h / 0.14);
+}
+
+/* UI-02 model table: per-row default radio, capability chips, search box,
+ * source/fetchedAt header. Replaces the legacy