/* my-deepagent Web GUI — v0.2 PR #3 polish pass. * * Visual reference: modern dev-tool dashboards (Linear / Vercel / * Resend / Railway). Goals: * - Refined dark palette (deeper background, soft surfaces) * - Card-based layout with clear hierarchy * - Pill badges for run / phase state * - Tabular numbers for metrics, monospace for IDs * - 8 px spacing grid, generous padding * - Subtle hover / transition without animation chaos * * Vanilla CSS only. No build system. No CSS-in-JS. */ /* ---------- Reset + tokens ---------- */ *, *::before, *::after { box-sizing: border-box; } :root { /* Surface (background → cards → elevated) */ --bg: #0a0b0e; --surface-1: #13141a; --surface-2: #181a22; --surface-3: #1f222c; --surface-hover: #232732; /* Borders */ --border: #262834; --border-strong: #313548; /* Text */ --text-primary: #f5f6f9; --text-secondary: #b6b9c4; --text-muted: #6c707f; --text-faint: #4a4d59; /* Accents */ --accent: #7c9eff; --accent-hover: #93b1ff; --accent-bg: rgba(124, 158, 255, 0.12); --success: #6cdba2; --success-bg: rgba(108, 219, 162, 0.12); --warning: #f5cc73; --warning-bg: rgba(245, 204, 115, 0.14); --danger: #ef7a7a; --danger-bg: rgba(239, 122, 122, 0.14); --info: #8a9cc7; --info-bg: rgba(138, 156, 199, 0.12); /* Type */ --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Pretendard", "SF Pro Text", "Apple SD Gothic Neo", "Noto Sans KR", "Segoe UI", Helvetica, Arial, sans-serif; --font-mono: "SF Mono", "JetBrains Mono", "Cascadia Code", "Menlo", "Monaco", "Consolas", monospace; /* Geometry */ --radius-sm: 6px; --radius: 10px; --radius-lg: 14px; --shadow-sm: 0 1px 0 rgba(255, 255, 255, 0.03) inset; --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 1px 12px rgba(0, 0, 0, 0.2); } /* ---------- Base ---------- */ html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text-primary); font-family: var(--font-sans); font-size: 14px; line-height: 1.55; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-feature-settings: "cv05", "ss01"; font-variant-numeric: tabular-nums; } body { min-height: 100vh; display: flex; flex-direction: column; } a { color: var(--accent); text-decoration: none; transition: color 0.15s ease; } a:hover { color: var(--accent-hover); } code, kbd, .mono { font-family: var(--font-mono); font-size: 0.85em; letter-spacing: -0.01em; } ::selection { background: var(--accent-bg); color: var(--text-primary); } /* ---------- Header / nav ---------- */ header { background: var(--surface-1); border-bottom: 1px solid var(--border); padding: 16px 32px; display: flex; justify-content: space-between; align-items: center; gap: 24px; } header h1 { margin: 0; font-size: 15px; font-weight: 600; color: var(--text-primary); letter-spacing: -0.01em; display: flex; align-items: center; gap: 10px; } header h1::before { content: ""; width: 8px; height: 8px; background: linear-gradient(135deg, var(--accent), var(--success)); border-radius: 2px; } header nav { display: flex; gap: 4px; } header nav a { color: var(--text-secondary); padding: 6px 12px; border-radius: var(--radius-sm); font-size: 13px; font-weight: 500; transition: all 0.15s ease; } header nav a:hover { color: var(--text-primary); background: var(--surface-hover); } header nav a.active { color: var(--accent); background: var(--accent-bg); } /* ---------- Main ---------- */ main { flex: 1; max-width: 1280px; width: 100%; margin: 0 auto; padding: 32px; } .page-title { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 24px; } .page-title h2 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.02em; } .page-subtitle { color: var(--text-muted); font-size: 13px; } h2 { margin: 28px 0 14px; font-size: 13px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); } h2.section-title { display: flex; align-items: center; gap: 10px; } h2.section-title::after { content: ""; flex: 1; height: 1px; background: var(--border); } /* ---------- Cards / tables ---------- */ .card { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-card); overflow: hidden; } table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; } th, td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; } tbody tr:last-child td { border-bottom: none; } tbody tr { transition: background 0.12s ease; } tbody tr:hover { background: var(--surface-2); } th { background: var(--surface-2); color: var(--text-muted); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; padding-top: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border-strong); } td .mono { color: var(--text-secondary); } td a { font-weight: 500; } /* ---------- State badges (pill) ---------- */ .badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 500; letter-spacing: 0.02em; border: 1px solid transparent; } .badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; } .badge.state-completed, .badge.state-ok { color: var(--success); background: var(--success-bg); } .badge.state-running, .badge.state-executing, .badge.state-validating, .badge.state-awaiting_artifact, .badge.state-awaiting_approval { color: var(--warning); background: var(--warning-bg); } .badge.state-failed, .badge.state-aborted { color: var(--danger); background: var(--danger-bg); } .badge.state-pending, .badge.state-created, .badge.state-bound, .badge.state-planning, .badge.state-paused, .badge.state-skipped { color: var(--info); background: var(--info-bg); } /* Animated dot for in-progress states */ .badge.state-running::before, .badge.state-executing::before, .badge.state-validating::before, .badge.state-awaiting_artifact::before { animation: pulse 1.6s ease-in-out infinite; } @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.85); } } /* ---------- Buttons ---------- */ button, .button { appearance: none; background: var(--surface-3); color: var(--text-primary); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 14px; font-family: inherit; font-size: 13px; font-weight: 500; letter-spacing: -0.005em; cursor: pointer; transition: all 0.15s ease; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; } button:hover:not(:disabled), .button:hover { background: var(--surface-hover); border-color: var(--border-strong); color: var(--text-primary); } button:active:not(:disabled) { transform: translateY(0.5px); } button:disabled { opacity: 0.4; cursor: not-allowed; } button.primary { background: var(--accent); border-color: var(--accent); color: #0a0b0e; font-weight: 600; } button.primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); } button.danger { background: transparent; color: var(--danger); border-color: rgba(239, 122, 122, 0.3); } button.danger:hover:not(:disabled) { background: var(--danger-bg); border-color: var(--danger); } /* ---------- Forms ---------- */ label { display: block; margin: 0 0 6px; color: var(--text-secondary); font-size: 12px; font-weight: 500; letter-spacing: 0.01em; } label .hint { color: var(--text-muted); font-weight: 400; margin-left: 6px; } input[type="text"], input[type="number"], textarea, select { width: 100%; background: var(--surface-1); color: var(--text-primary); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 12px; font-family: inherit; font-size: 13px; line-height: 1.5; transition: border-color 0.15s ease, background 0.15s ease; } input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); background: var(--surface-2); } input::placeholder, textarea::placeholder { color: var(--text-faint); } textarea { resize: vertical; min-height: 100px; font-family: var(--font-mono); font-size: 12px; } select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23b6b9c4' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; } .form-row { margin-bottom: 16px; } .form-row.compact { margin-bottom: 8px; } .form-grid { display: grid; grid-template-columns: 1fr 200px; gap: 16px; } .action-bar { display: flex; gap: 8px; margin: 24px 0 0; padding-top: 16px; border-top: 1px solid var(--border); } .action-bar.no-top-border { border-top: none; padding-top: 0; } /* ---------- Meta panel (key/value lists) ---------- */ .meta-panel { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); padding: 4px 0; } .meta-row { display: grid; grid-template-columns: 140px 1fr; gap: 16px; padding: 10px 16px; border-bottom: 1px solid var(--border); align-items: center; } .meta-row:last-child { border-bottom: none; } .meta-row .key { color: var(--text-muted); font-size: 12px; font-weight: 500; letter-spacing: 0.01em; text-transform: uppercase; } .meta-row .value { color: var(--text-primary); font-size: 13px; word-break: break-all; } .meta-row .value.mono { font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary); } .meta-row .value.dim { color: var(--text-muted); } /* ---------- Budget summary cards ---------- */ .budget-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; } .budget-card { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; } .budget-card .scope { color: var(--text-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: 6px; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .budget-card .amount { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; color: var(--text-primary); } .budget-card .amount.warn { color: var(--warning); } .budget-card .amount.over { color: var(--danger); } .budget-card .cap { color: var(--text-muted); font-size: 12px; font-weight: 500; margin-left: 6px; } .budget-card .bar { height: 4px; background: var(--surface-3); border-radius: 2px; margin-top: 10px; overflow: hidden; } .budget-card .bar > div { height: 100%; background: var(--success); transition: width 0.3s ease; } .budget-card .bar.warn > div { background: var(--warning); } .budget-card .bar.over > div { background: var(--danger); } /* ---------- Event log (SSE) ---------- */ .events { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); max-height: 60vh; overflow-y: auto; padding: 8px 0; font-family: var(--font-mono); font-size: 12px; } .event-line { display: grid; grid-template-columns: 80px 1fr; gap: 12px; padding: 5px 16px; border-bottom: 1px solid transparent; } .event-line:hover { background: var(--surface-2); } .event-line .ts { color: var(--text-faint); font-size: 11px; } .event-line .body .type { color: var(--accent); font-weight: 500; } .event-line .body .payload { color: var(--text-muted); margin-left: 8px; font-size: 11px; word-break: break-all; } .event-line.run-completed .body .type { color: var(--success); } .event-line.run-failed .body .type, .event-line.run-aborted .body .type { color: var(--danger); } .event-line.run-resumed .body .type { color: var(--warning); } .events::-webkit-scrollbar { width: 8px; } .events::-webkit-scrollbar-track { background: transparent; } .events::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; } /* ---------- Empty / error states ---------- */ .empty { padding: 48px 24px; text-align: center; color: var(--text-muted); font-size: 13px; } .empty .empty-icon { font-size: 28px; margin-bottom: 8px; opacity: 0.4; } .empty .cta { margin-top: 16px; } .error-banner { background: var(--danger-bg); border: 1px solid rgba(239, 122, 122, 0.3); border-radius: var(--radius); padding: 12px 16px; margin-bottom: 16px; color: var(--danger); font-size: 13px; display: flex; align-items: center; gap: 10px; } .error-banner::before { content: "!"; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; background: var(--danger); color: #0a0b0e; border-radius: 50%; font-weight: 700; font-size: 11px; flex-shrink: 0; } /* ---------- Tag chip (per-role override input) ---------- */ .chips { display: grid; grid-template-columns: 140px 1fr; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--border); align-items: center; } .chips:last-child { border-bottom: none; } .chips .role { font-size: 12px; color: var(--text-secondary); font-weight: 500; } .chips .role .hint { display: block; font-size: 11px; color: var(--text-muted); font-weight: 400; margin-top: 2px; } .chips input { font-family: var(--font-mono); font-size: 12px; } /* ---------- Responsive niceties (desktop-focused, but readable narrower) ---------- */ @media (max-width: 720px) { main { padding: 20px 16px; } header { padding: 14px 16px; } .form-grid { grid-template-columns: 1fr; } .meta-row { grid-template-columns: 1fr; gap: 4px; padding: 12px 14px; } .event-line { grid-template-columns: 1fr; gap: 2px; } .chips { grid-template-columns: 1fr; gap: 6px; } }