/* Reusable widgets — every interactive control lives here so nothing in a screen-level stylesheet ever hand-rolls its own button/row look. */ /* ---------- icon ---------- */ .icon { display: inline-block; flex-shrink: 0; color: inherit; vertical-align: middle; } /* The duotone "background" layer — each tier gets its own semi-transparent soft fill so the two-tone effect is visible no matter the icon color. Using color-mix() instead of real alpha avoids muddy compositing on colorful backgrounds (see icons.ts). */ .icon { --icon-soft: color-mix(in srgb, var(--text-primary) 30%, transparent); } .icon .icon-soft { fill: var(--icon-soft); } /* ---- Icon color tiers ---- Unselected / not important → white (default) More important → orange (--accent) Inactive / other → gray (--text-dim) On orange button (CTA) → accent-color soft layer */ .icon { color: inherit; } .icon--important { color: var(--accent); --icon-soft: color-mix(in srgb, var(--accent) 30%, transparent); } .icon--inactive { color: var(--text-dim); --icon-soft: color-mix(in srgb, var(--text-dim) 30%, transparent); } .icon--on-accent-bg { color: var(--accent); --icon-soft: color-mix(in srgb, var(--accent) 30%, transparent); } /* ---------- buttons ---------- */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: none; border-radius: var(--radius-md); padding: 0 12px; height: 32px; font-size: 12.5px; cursor: pointer; background: transparent; color: var(--text-primary); transition: background-color 0.1s var(--ease-out), color 0.1s var(--ease-out), transform 0.06s var(--ease-out); white-space: nowrap; } .btn:disabled { cursor: default; opacity: 0.4; } .btn:active:not(:disabled) { transform: scale(0.97); } .btn-ghost:not(:disabled):hover { background: rgba(255, 255, 255, 0.06); } .btn-ghost:not(:disabled):active { background: rgba(255, 255, 255, 0.1); } .btn-accent { background: var(--accent); color: var(--on-accent); font-weight: 600; } .btn-accent:not(:disabled):hover { background: var(--accent-hover); } .btn-accent:not(:disabled):active { background: var(--accent-dim); } .btn-accent-dim { background: var(--accent-soft); color: var(--text-primary); } .btn-accent-dim:not(:disabled):hover { background: color-mix(in srgb, var(--accent) 24%, transparent); } .btn-danger { background: var(--red); color: var(--on-accent); font-weight: 600; } .btn-danger:not(:disabled):hover { background: var(--red-hover); } .btn-toggle.active { color: var(--accent); background: var(--accent-soft); } .btn-toggle:not(.active):not(:disabled):hover { background: rgba(255, 255, 255, 0.06); } .icon-btn { width: 32px; height: 32px; padding: 0; border-radius: var(--radius-md); } .btn-sep { width: 1px; height: 20px; background: var(--border); margin: 0 8px; flex-shrink: 0; } /* ---------- clickable rows (sidebar / recent-connections list) ---------- */ .row { display: flex; align-items: center; margin: 1px 10px; padding: 0 10px; height: 30px; border-radius: var(--radius-sm); cursor: pointer; color: var(--text-primary); transition: background-color 0.1s var(--ease-out); } .row:hover { background: var(--bg-row-hover); } .row:active { background: var(--bg-row-sel); } .row.active { background: var(--accent-soft); } .row .row-spacer { flex: 1; } /* ---------- spinner ---------- */ .spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--text-primary) 18%, transparent); border-top-color: var(--accent); animation: spin 0.7s linear infinite; flex-shrink: 0; } @keyframes spin { to { transform: rotate(360deg); } } /* ---------- keyboard shortcut chip ---------- */ .kbd { display: inline-flex; align-items: center; font-family: var(--font-sans); font-size: 10px; font-weight: 600; line-height: 1.5; color: var(--text-dim); background: rgba(255, 255, 255, 0.06); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; white-space: nowrap; } .kbd-on-accent { color: var(--on-accent); background: rgba(255, 255, 255, 0.24); border-color: transparent; } /* ---------- protocol badge (saved sites / history rows) ---------- */ .proto-badge { flex-shrink: 0; font-size: 9.5px; font-weight: 700; letter-spacing: 0.03em; color: var(--text-dim); border: 1px solid var(--border); border-radius: 4px; padding: 2px 5px; } /* ---------- status dot ---------- */ .status-dot { font-size: 10px; line-height: 1; } .status-dot.connected { color: var(--green); } .status-dot.connecting { color: var(--yellow); } .status-dot.error { color: var(--red); } .status-dot.disconnected { color: var(--text-hint); } /* ---------- form fields ---------- */ .field { display: flex; flex-direction: column; gap: 4px; } .field-label { font-size: 11px; color: var(--text-dim); } .field-input { height: 32px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--bg-content); color: var(--text-primary); padding: 0 10px; font-size: 13px; outline: none; transition: border-color 0.1s var(--ease-out), box-shadow 0.1s var(--ease-out); width: 100%; } .field-input::placeholder { color: var(--text-hint); } .field-input:hover { border-color: var(--accent-dim); } .field-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); } .field-hint { font-size: 10px; color: var(--text-hint); line-height: 1.4; } .field-row { display: flex; gap: 8px; align-items: flex-end; } .field-with-btn { display: flex; gap: 8px; } .field-with-icon { position: relative; } .field-with-icon .field-input { padding-right: 34px; } .field-icon-btn { position: absolute; right: 4px; top: 4px; width: 24px; height: 24px; border-radius: var(--radius-sm); border: none; background: transparent; color: var(--text-dim); display: flex; align-items: center; justify-content: center; cursor: pointer; } .field-icon-btn:hover { background: rgba(255, 255, 255, 0.08); color: var(--text-primary); } /* ---------- segmented control ---------- */ .segmented { display: flex; gap: 4px; padding: 3px; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-content); } .segmented-item { flex: 1; height: 26px; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text-dim); font-size: 11.5px; font-weight: 600; cursor: pointer; transition: background-color 0.1s var(--ease-out), color 0.1s var(--ease-out); } .segmented-item:hover:not(.active) { background: rgba(255, 255, 255, 0.05); } .segmented-item.active { background: var(--accent); color: var(--on-accent); } /* ---------- error box ---------- */ .error-box { display: flex; align-items: center; gap: 8px; background: var(--red-soft); border: 1px solid color-mix(in srgb, var(--red) 40%, transparent); border-radius: var(--radius-md); padding: 8px 10px; color: var(--red); font-size: 11px; animation: shake 0.32s var(--ease-out); } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } } /* ---------- overlay / dialog ---------- */ .overlay { position: fixed; inset: 0; background: color-mix(in srgb, var(--bg-base) 62%, transparent); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; z-index: 100; opacity: 0; animation: overlay-in 0.16s var(--ease-out) forwards; } .overlay.closing { animation: overlay-out 0.12s var(--ease-out) forwards; } @keyframes overlay-in { from { opacity: 0; } to { opacity: 1; } } @keyframes overlay-out { from { opacity: 1; } to { opacity: 0; } } .dialog { background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45); padding: 22px; width: 340px; transform: translateY(0) scale(1); opacity: 1; animation: dialog-in 0.18s var(--ease-out); } @keyframes dialog-in { from { opacity: 0; transform: translateY(6px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } } .dialog-header { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; } .dialog-title { font-size: 15.5px; font-weight: 600; color: var(--text-primary); flex: 1; } .dialog-close { width: 28px; height: 28px; border-radius: var(--radius-sm); border: none; background: transparent; color: var(--text-dim); cursor: pointer; display: flex; align-items: center; justify-content: center; } .dialog-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--text-primary); } .dialog-body > * + * { margin-top: 10px; } .dialog-footer { display: flex; align-items: center; margin-top: 18px; } .dialog-footer .btn { min-width: 90px; } .dialog-footer .spacer { flex: 1; } /* small confirm-style dialog */ .dialog.dialog-sm { width: 300px; } /* Settings dialog: fixed-width nav column + scrolling content column */ .dialog.dialog-settings { width: 640px; } .settings-body { display: flex; align-items: flex-start; gap: 18px; height: 420px; } .settings-nav { width: 150px; flex-shrink: 0; display: flex; flex-direction: column; gap: 2px; height: 100%; overflow-y: auto; border-right: 1px solid var(--border); padding-right: 12px; } .settings-nav-item { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; border-radius: var(--radius-sm); color: var(--text-dim); font-size: 12px; cursor: pointer; text-align: left; transition: background-color 0.1s var(--ease-out), color 0.1s var(--ease-out); } .settings-nav-item:hover { background: rgba(255, 255, 255, 0.05); color: var(--text-primary); } .settings-nav-item.active { background: var(--accent-soft); color: var(--accent); } .settings-content { flex: 1; min-width: 0; height: 100%; overflow-y: auto; padding-right: 4px; } .settings-section-title { font-size: 13px; font-weight: 700; color: var(--text-primary); margin-bottom: 4px; } .settings-label { font-size: 12.5px; font-weight: 600; color: var(--text-primary); } .settings-hint { font-size: 10.5px; color: var(--text-hint); line-height: 1.5; margin-top: 2px; } .settings-row { display: flex; align-items: center; gap: 10px; } .settings-check-row { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; } .settings-block + .settings-block { margin-top: 20px; } .settings-select { appearance: auto; background: var(--card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 6px 10px; font-size: 12px; color: var(--text-primary); cursor: pointer; min-width: 180px; } .settings-select option { background: var(--bg-panel); color: var(--text-primary); } .settings-select:hover { border-color: var(--accent); } .settings-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } /* ---------- theme picker (settings > appearance) ---------- */ .theme-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; } .theme-card { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 8px 10px; border-radius: var(--radius-md); border: 1.5px solid var(--border); background: var(--bg-content); cursor: pointer; transition: border-color 0.15s var(--ease-out), background-color 0.15s var(--ease-out), transform 0.06s var(--ease-out); } .theme-card:hover { border-color: var(--text-hint); background: var(--bg-panel); } .theme-card.active { border-color: var(--accent); background: var(--accent-soft); } .theme-card:active { transform: scale(0.96); } .theme-card-label { font-size: 10px; text-align: center; line-height: 1.3; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .theme-card.active .theme-card-label { color: var(--accent); font-weight: 600; } .theme-swatch { width: 100%; height: 44px; border-radius: var(--radius-sm); border: 1px solid var(--border); overflow: hidden; display: flex; flex-direction: column; } /* Swatch previews — mimic each theme's palette */ .theme-swatch--ember { background: linear-gradient(135deg, #0b0d11 40%, #181c24 70%, #d9804f 100%); } .theme-swatch--frost { background: linear-gradient(135deg, #f4f6fa 40%, #ffffff 70%, #4a8fe0 100%); } .theme-swatch--midnight { background: linear-gradient(135deg, #0d0e17 40%, #161827 70%, #8b7cf7 100%); } .theme-swatch--forest { background: linear-gradient(135deg, #0b120e 40%, #141f17 70%, #5bba70 100%); } .theme-swatch--dawn { background: linear-gradient(135deg, #faf6f3 40%, #ffffff 70%, #d9735a 100%); } .theme-swatch--ash { background: linear-gradient(135deg, #0a0a0c 40%, #141416 70%, #c0c4cc 100%); } .settings-list-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--separator); } .settings-list-row:last-child { border-bottom: none; } .settings-list-row-main { min-width: 0; flex: 1; } .settings-list-row-title { font-size: 12.5px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .settings-list-row-sub { font-size: 10.5px; color: var(--text-hint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .settings-value-row { display: flex; justify-content: space-between; gap: 10px; padding: 5px 0; font-size: 12px; } .settings-value-row .k { color: var(--text-dim); } .settings-value-row .v { color: var(--text-primary); font-family: var(--font-mono); font-size: 11px; text-align: right; overflow-wrap: anywhere; } .settings-empty { color: var(--text-hint); font-size: 12px; padding: 10px 0; } .dialog-text { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; } /* ---------- context menu ---------- */ .context-menu { position: fixed; z-index: 200; min-width: 180px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); padding: 4px; animation: menu-in 0.12s var(--ease-out); } @keyframes menu-in { from { opacity: 0; transform: scale(0.97); } to { opacity: 1; transform: scale(1); } } .context-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; border: none; background: transparent; color: var(--text-primary); font-size: 12px; padding: 6px 10px; border-radius: var(--radius-sm); cursor: pointer; text-align: left; } .context-menu-item:hover { background: rgba(255, 255, 255, 0.07); } .context-menu-item.danger { color: var(--red); } .context-menu-sep { height: 1px; background: var(--separator); margin: 4px 6px; } /* ---------- popover (history) ---------- */ .popover { position: fixed; z-index: 90; width: 270px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); padding: 10px; opacity: 0; transform: translateY(-4px); animation: pop-in 0.14s var(--ease-out) forwards; } @keyframes pop-in { to { opacity: 1; transform: translateY(0); } } .popover-header { display: flex; align-items: center; gap: 6px; padding: 2px 4px 8px; color: var(--text-dim); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; } .popover-empty { color: var(--text-hint); padding: 6px 4px; font-size: 12px; } .popover-list { max-height: 220px; overflow-y: auto; } .popover-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; height: 30px; padding: 0 8px; border: none; background: transparent; border-radius: var(--radius-sm); color: var(--text-primary); font-size: 12px; cursor: pointer; text-align: left; } .popover-row:hover { background: rgba(255, 255, 255, 0.06); } /* ---------- toggle switch (settings) ---------- */ .switch { position: relative; width: 34px; height: 20px; border-radius: 10px; background: var(--border); border: none; cursor: pointer; flex-shrink: 0; transition: background-color 0.14s var(--ease-out); } .switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--text-primary); transition: transform 0.14s var(--ease-out); } .switch.on { background: var(--accent); } .switch.on::after { transform: translateX(14px); background: var(--on-accent); } /* ---------- progress bar ---------- */ .progress { flex: 1; height: 6px; border-radius: 3px; background: rgba(255, 255, 255, 0.06); overflow: hidden; } .progress-fill { height: 100%; border-radius: 3px; background: var(--accent); transition: width 0.15s linear; } .progress-fill.running { background: var(--accent); } .progress-fill.completed { background: var(--green); } .progress-fill.failed { background: var(--red); } .progress-fill.paused { background: var(--text-hint); } /* ---------- empty state ---------- */ .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--text-hint); gap: 8px; font-size: 12.5px; }