/* Warpgate Dashboard — unified stylesheet */ :root { --bg: #0f1117; --surface: #1a1d27; --border: #2a2d3a; --text: #e1e4ed; --text-muted: #8b8fa3; --accent: #6c8aff; --green: #4ade80; --red: #f87171; --yellow: #fbbf24; --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; --mono: "SF Mono", "Fira Code", "Cascadia Code", monospace; } * { box-sizing: border-box; margin: 0; padding: 0; } [x-cloak] { display: none !important; } body { background: var(--bg); color: var(--text); font-family: var(--font); padding: 0; margin: 0; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } .mono { font-family: var(--mono); } /* ─── Shell layout ─────────────────────────────────────── */ .shell { max-width: 1080px; margin: 0 auto; padding: 24px; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0; padding-bottom: 16px; } .header h1 { font-size: 1.4em; } .header .status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--green); margin-right: 8px; } .meta { color: var(--text-muted); font-size: 0.85em; } /* ─── Tab navigation ───────────────────────────────────── */ .tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 24px; } .tab-btn { padding: 10px 20px; background: none; border: none; border-bottom: 2px solid transparent; color: var(--text-muted); font-family: var(--font); font-size: 0.9em; font-weight: 500; cursor: pointer; transition: color 0.15s, border-color 0.15s; } .tab-btn:hover { color: var(--text); } .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); } /* ─── Stat cards (dashboard) ───────────────────────────── */ .stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 24px; } .stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; } .stat-card .label { font-size: 0.8em; color: var(--text-muted); margin-bottom: 4px; } .stat-card .value { font-size: 1.3em; font-weight: 600; } /* ─── Share cards ──────────────────────────────────────── */ .cards { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; } .card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; } .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .card-header h2 { font-size: 1.1em; } .card-header h2 a { color: var(--accent); text-decoration: none; } .card-header h2 a:hover { text-decoration: underline; } /* ─── Badges ───────────────────────────────────────────── */ .badge { font-size: 0.75em; padding: 2px 8px; border-radius: 4px; font-weight: 600; vertical-align: middle; } .badge-ok { background: rgba(74,222,128,0.15); color: var(--green); } .badge-error { background: rgba(248,113,113,0.15); color: var(--red); } .badge-ro { background: rgba(251,191,36,0.15); color: var(--yellow); } .badge-warn { background: rgba(251,191,36,0.15); color: var(--yellow); } .badge-warmup { background: rgba(251,191,36,0.15); color: var(--yellow); } /* ─── Stats row (inside share cards) ──────────────────── */ .stats { display: flex; gap: 24px; font-size: 0.9em; color: var(--text-muted); flex-wrap: wrap; } .stats span { white-space: nowrap; } .stats .label { color: var(--text-muted); } .stats .value { color: var(--text); } .error-msg { margin-top: 8px; padding: 8px 12px; background: rgba(248,113,113,0.08); border-radius: 4px; color: var(--red); font-size: 0.85em; } /* ─── Protocol badges ──────────────────────────────────── */ .protocols { display: flex; gap: 16px; margin-bottom: 20px; font-size: 0.9em; } .proto-badge { padding: 4px 12px; border-radius: 4px; font-weight: 600; } .proto-on { background: rgba(74,222,128,0.15); color: var(--green); } .proto-off { background: rgba(248,113,113,0.1); color: var(--text-muted); } /* ─── Share table (shares tab) ─────────────────────────── */ .share-table { width: 100%; border-collapse: collapse; margin-bottom: 24px; } .share-table th { text-align: left; padding: 8px 12px; border-bottom: 2px solid var(--border); color: var(--text-muted); font-size: 0.8em; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; } .share-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); } .share-row { cursor: pointer; transition: background 0.1s; } .share-row:hover { background: rgba(108,138,255,0.05); } .detail-row td { padding: 0; border-bottom: 1px solid var(--border); } .detail-panel { background: var(--surface); padding: 16px 20px; } .detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; } .detail-card { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px; } .detail-card .label { font-size: 0.8em; color: var(--text-muted); margin-bottom: 4px; } .detail-card .value { font-size: 1.2em; font-weight: 600; } .info-table { width: 100%; border-collapse: collapse; } .info-table td { padding: 6px 12px; border-bottom: 1px solid var(--border); font-size: 0.9em; } .info-table td:first-child { color: var(--text-muted); width: 140px; } .error-text { color: var(--red); } /* ─── Config editor ────────────────────────────────────── */ .message { padding: 12px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9em; } .message-error { background: rgba(248,113,113,0.15); color: var(--red); border: 1px solid rgba(248,113,113,0.3); } .message-ok { background: rgba(74,222,128,0.15); color: var(--green); border: 1px solid rgba(74,222,128,0.3); } textarea { width: 100%; min-height: 500px; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 16px; font-family: var(--mono); font-size: 0.85em; line-height: 1.5; resize: vertical; tab-size: 4; } textarea:focus { outline: none; border-color: var(--accent); } .form-actions { margin-top: 12px; display: flex; gap: 12px; } /* ─── Config form (interactive editor) ─────────────────── */ .config-section { margin-bottom: 16px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; } .section-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: var(--surface); cursor: pointer; user-select: none; } .section-header h3 { font-size: 1em; display: flex; align-items: center; gap: 8px; } .section-body { padding: 16px; } .tier-badge { font-size: 0.7em; padding: 2px 8px; border-radius: 4px; font-weight: 600; } .tier-live { background: rgba(74,222,128,0.15); color: var(--green); } .tier-protocol { background: rgba(251,191,36,0.15); color: var(--yellow); } .tier-pershare { background: rgba(251,191,36,0.15); color: var(--yellow); } .tier-global { background: rgba(248,113,113,0.15); color: var(--red); } .tier-none { background: rgba(74,222,128,0.15); color: var(--green); } .field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .field-row { margin-bottom: 12px; } .field-grid .field-row { margin-bottom: 0; } .field-row label:not(.toggle) { display: block; font-size: 0.8em; color: var(--text-muted); margin-bottom: 4px; } .field-row input[type="text"], .field-row input[type="password"], .field-row input[type="number"], .field-row select { width: 100%; padding: 8px 12px; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 6px; font-family: var(--font); font-size: 0.9em; } .field-row input:focus, .field-row select:focus { outline: none; border-color: var(--accent); } .field-row input.mono { font-family: var(--mono); } .array-item { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 16px; margin-bottom: 12px; position: relative; } .array-item .item-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .remove-btn { background: none; border: 1px solid rgba(248,113,113,0.3); color: var(--red); padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 0.8em; } .remove-btn:hover { background: rgba(248,113,113,0.1); } .add-btn { width: 100%; padding: 8px; background: none; border: 1px dashed var(--border); border-radius: 8px; color: var(--text-muted); cursor: pointer; font-size: 0.9em; } .add-btn:hover { border-color: var(--accent); color: var(--accent); } /* ─── Connection test button ──────────────────────────────── */ .item-header-actions { display: flex; align-items: center; gap: 8px; } .test-btn { background: none; border: 1px solid rgba(108,138,255,0.4); color: var(--accent); padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 0.8em; } .test-btn:hover:not(:disabled) { background: rgba(108,138,255,0.1); } .test-btn:disabled { opacity: 0.5; cursor: default; } .test-ok { font-size: 0.8em; color: var(--green); white-space: nowrap; } .test-fail { font-size: 0.8em; color: var(--red); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ─── Remote Path browse combo ────────────────────────────── */ .browse-combo { display: flex; gap: 6px; align-items: center; width: 100%; } .browse-combo input { flex: 1; } .browse-btn { background: none; border: 1px solid var(--border); color: var(--text-muted); padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 0.85em; white-space: nowrap; flex-shrink: 0; } .browse-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); } .browse-btn:disabled { opacity: 0.5; cursor: default; } .dir-dropdown { margin-top: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; max-height: 200px; overflow-y: auto; } .dir-item { display: flex; align-items: center; justify-content: space-between; padding: 6px 10px; border-bottom: 1px solid var(--border); gap: 8px; } .dir-item:last-child { border-bottom: none; } .dir-item:hover { background: rgba(108,138,255,0.06); } .dir-name { font-family: var(--mono); font-size: 0.85em; cursor: pointer; color: var(--text); flex: 1; } .dir-name:hover { color: var(--accent); } .dir-enter { background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 0.9em; padding: 2px 6px; border-radius: 3px; flex-shrink: 0; } .dir-enter:hover { color: var(--accent); background: rgba(108,138,255,0.1); } .browse-error { margin-top: 4px; font-size: 0.82em; color: var(--red); } /* Toggle switch */ .toggle { position: relative; display: inline-flex; align-items: center; cursor: pointer; gap: 8px; font-size: 0.9em; color: var(--text); } .toggle input { display: none; } .toggle .slider { width: 36px; height: 20px; background: var(--border); border-radius: 10px; position: relative; transition: background 0.2s; flex-shrink: 0; } .toggle .slider::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background: var(--text-muted); border-radius: 50%; transition: transform 0.2s; } .toggle input:checked + .slider { background: var(--accent); } .toggle input:checked + .slider::after { transform: translateX(16px); background: #fff; } .chevron { font-size: 0.9em; color: var(--text-muted); } /* ─── Buttons ──────────────────────────────────────────── */ .btn { display: inline-block; padding: 8px 20px; border-radius: 6px; font-size: 0.9em; font-weight: 500; cursor: pointer; border: none; text-decoration: none; text-align: center; } .btn-primary { background: var(--accent); color: #fff; } .btn-primary:hover { opacity: 0.9; } .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; } .btn-secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); } .btn-secondary:hover { border-color: var(--accent); color: var(--accent); } /* ─── Log viewer ──────────────────────────────────────── */ .log-viewer { background: #0a0c10; border: 1px solid var(--border); border-radius: 8px; padding: 16px; font-family: var(--mono); font-size: 0.8em; line-height: 1.6; height: 70vh; overflow-y: auto; overflow-x: hidden; word-break: break-all; } .log-viewer .log-line { color: var(--text-muted); } .log-viewer .log-ts { color: var(--accent); margin-right: 8px; opacity: 0.7; } .log-viewer .log-msg { color: var(--text); } .log-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-size: 0.85em; color: var(--text-muted); } .log-toolbar .log-count { font-family: var(--mono); } .empty-state { text-align: center; padding: 48px 24px; color: var(--text-muted); } .empty-state h2 { font-size: 1.2em; margin-bottom: 8px; color: var(--text); } /* ─── Auto-refresh controls ───────────────────────────── */ .auto-refresh-controls { display: inline-flex; align-items: center; gap: 8px; font-size: 0.8em; color: var(--text-muted); } .auto-refresh-controls .label-text { user-select: none; } .interval-select { background: var(--surface); color: var(--text-muted); border: 1px solid var(--border); border-radius: 4px; padding: 2px 6px; font-size: 1em; font-family: var(--font); } .interval-select:focus { outline: none; border-color: var(--accent); } .interval-select:disabled { opacity: 0.4; cursor: not-allowed; } .toggle-sm .slider { width: 28px; height: 16px; } .toggle-sm .slider::after { width: 12px; height: 12px; } .toggle-sm input:checked + .slider::after { transform: translateX(12px); } /* ─── Offline banner ───────────────────────────────────── */ .offline-banner { background: #f59e0b; color: #1c1917; padding: 10px 20px; display: flex; align-items: center; gap: 10px; font-size: 0.9rem; font-weight: 500; border-bottom: 2px solid #d97706; position: sticky; top: 0; z-index: 100; } .offline-banner .offline-icon { font-size: 1.1rem; } .offline-banner .offline-sub { margin-left: auto; opacity: 0.7; font-size: 0.8rem; } /* ─── Sync indicator ──────────────────────────────────── */ .sync-indicator { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: 8px; margin-bottom: 16px; font-size: 0.9rem; font-weight: 500; } .sync-ok { background: rgba(34, 197, 94, 0.12); color: #16a34a; border: 1px solid rgba(34, 197, 94, 0.3); } .sync-pending { background: rgba(245, 158, 11, 0.12); color: #d97706; border: 1px solid rgba(245, 158, 11, 0.3); } .sync-indicator .sync-icon { font-size: 1.1rem; } .sync-indicator .sync-sub { margin-left: auto; opacity: 0.65; font-size: 0.8rem; } /* ─── Preset section (config tab) ─────────────────────── */ .preset-section { margin-bottom: 20px; padding: 16px; background: var(--surface, #1e1e2e); border-radius: 10px; border: 1px solid var(--border, rgba(255,255,255,0.08)); } .preset-header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; } .preset-hint { font-size: 0.78rem; opacity: 0.6; } .preset-buttons { display: flex; gap: 10px; flex-wrap: wrap; } .preset-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 10px 14px; border-radius: 8px; border: 1px solid var(--border, rgba(255,255,255,0.12)); background: var(--surface2, rgba(255,255,255,0.04)); cursor: pointer; color: inherit; min-width: 140px; transition: background 0.15s, border-color 0.15s; } .preset-btn:hover { background: rgba(99,102,241,0.15); border-color: rgba(99,102,241,0.4); } .preset-btn .preset-icon { font-size: 1.2rem; } .preset-btn .preset-name { font-weight: 600; font-size: 0.9rem; } .preset-btn .preset-desc { font-size: 0.72rem; opacity: 0.65; } .preset-result { margin-top: 10px; min-height: 20px; font-size: 0.85rem; } .preset-result .ok { color: #22c55e; } .preset-result .error { color: #ef4444; } .preset-spinner { display: none; font-size: 0.85rem; opacity: 0.7; } .htmx-request .preset-spinner { display: block; } /* ─── Share error banner & action buttons ─────────────── */ .share-error-banner { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 6px; margin-top: 8px; font-size: 0.85rem; color: #f87171; } .action-btn { padding: 6px 14px; border-radius: 6px; border: 1px solid var(--border, rgba(255,255,255,0.12)); background: var(--surface2, rgba(255,255,255,0.06)); cursor: pointer; color: inherit; font-size: 0.85rem; transition: background 0.15s; } .action-btn:hover { background: rgba(99,102,241,0.2); } .action-btn-sm { padding: 3px 10px; border-radius: 4px; border: 1px solid rgba(239,68,68,0.4); background: rgba(239,68,68,0.1); cursor: pointer; color: #f87171; font-size: 0.78rem; margin-left: auto; } /* ─── Apply modal ─────────────────────────────────────── */ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1000; } .modal-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 28px 32px; min-width: 380px; max-width: 460px; } .modal-title { font-size: 1.1em; margin-bottom: 20px; } .modal-steps { display: flex; flex-direction: column; gap: 14px; } .modal-step { display: flex; align-items: center; gap: 12px; font-size: 0.92em; color: var(--text-muted); transition: color 0.2s; } .modal-step.step-done { color: var(--green); } .modal-step.step-active { color: var(--text); } .modal-step.step-error { color: var(--red); } .step-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .step-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border); } .step-spinner { width: 16px; height: 16px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .modal-error { margin-top: 14px; padding: 10px 14px; background: rgba(248,113,113,0.1); border: 1px solid var(--red); border-radius: 6px; color: var(--red); font-size: 0.85em; } .modal-footer { margin-top: 20px; text-align: right; } /* ─── Responsive ───────────────────────────────────────── */ @media (max-width: 768px) { .stat-cards { grid-template-columns: repeat(2, 1fr); } .detail-grid { grid-template-columns: 1fr; } .field-grid { grid-template-columns: 1fr; } .share-table th:nth-child(n+5), .share-table td:nth-child(n+5) { display: none; } } @media (max-width: 480px) { .shell { padding: 12px; } .stat-cards { grid-template-columns: 1fr; } .tabs { overflow-x: auto; } .tab-btn { padding: 8px 14px; font-size: 0.85em; } }