/* ============================================================ * Handler dashboard — visual language ported from the Leeworks * design system (flat, dark, border-led "developer-native"). * Token hex values are copied verbatim from the design system. * ============================================================ */ :root { /* ---- surfaces ---- */ --lw-bg: #0f1117; --lw-surface: #1a1d27; --lw-surface-2: #232733; --lw-surface-3: #2b3040; /* ---- borders ---- */ --lw-border: #2d3748; --lw-border-strong: #3a4861; /* ---- text ---- */ --lw-white: #ffffff; --lw-text: #e2e8f0; --lw-text-muted: #a0aec0; --lw-text-faint: #718096; /* ---- accents ---- */ --lw-blue-200: #bee3f8; --lw-blue-300: #90cdf4; --lw-blue-400: #63b3ed; --lw-blue-500: #4299e1; --lw-indigo-400: #7f9cf5; --lw-indigo-500: #667eea; --lw-indigo-600: #5a67d8; --lw-indigo-700: #4c51bf; /* ---- status chip pairs ---- */ --lw-success-bg: #1a4731; --lw-success-fg: #9ae6b4; --lw-warning-bg: #744210; --lw-warning-fg: #fbd38d; --lw-danger-bg: #63171b; --lw-danger-fg: #feb2b2; --lw-info-bg: #1a365d; --lw-info-fg: #90cdf4; --lw-neutral-bg: #232733; --lw-neutral-fg: #a0aec0; /* ---- semantic aliases ---- */ --surface-page: var(--lw-bg); --surface-card: var(--lw-surface); --surface-raised: var(--lw-surface-2); --surface-inset: var(--lw-surface-3); --border-default: var(--lw-border); --border-strong: var(--lw-border-strong); --text-heading: var(--lw-white); --text-body: var(--lw-text); --text-muted: var(--lw-text-muted); --text-faint: var(--lw-text-faint); --link: var(--lw-blue-300); --link-hover: var(--lw-white); --accent: var(--lw-blue-300); --accent-hover: var(--lw-blue-400); --action-bg: var(--lw-indigo-500); --action-bg-hover: var(--lw-indigo-600); --action-bg-active: var(--lw-indigo-700); --action-fg: var(--lw-white); --focus-ring: var(--lw-blue-400); /* ---- type ---- */ --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; --fw-regular: 400; --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700; --fw-black: 800; --text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 0.95rem; --text-base: 1rem; --text-lg: 1.25rem; --text-xl: 1.5rem; --text-2xl: 2rem; --lh-tight: 1.15; --lh-snug: 1.3; --lh-normal: 1.6; --ls-tight: -0.02em; --ls-wide: 0.04em; /* ---- radii / motion ---- */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-full: 9999px; --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45); --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.55); --shadow-focus: 0 0 0 3px rgba(99, 179, 237, 0.45); --ease-standard: cubic-bezier(0.4, 0, 0.2, 1); --dur-fast: 120ms; --dur-normal: 180ms; } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; } body { font-family: var(--font-sans); font-size: var(--text-base); line-height: var(--lh-normal); color: var(--text-body); background: var(--surface-page); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } a { color: var(--link); text-decoration: none; transition: color var(--dur-fast) var(--ease-standard); } a:hover { color: var(--link-hover); } ::selection { background: rgba(102, 126, 234, 0.4); color: #fff; } :focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } ::-webkit-scrollbar { width: 9px; height: 9px; } ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-full); } ::-webkit-scrollbar-track { background: transparent; } .mono { font-family: var(--font-mono); } .muted { color: var(--text-muted); } .faint { color: var(--text-faint); } .nowrap { white-space: nowrap; } .truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* ---------------- Badge ---------------- */ .badge { display: inline-flex; align-items: center; gap: 5px; font-size: var(--text-xs); font-weight: var(--fw-semibold); line-height: 1; padding: 4px 9px; border-radius: var(--radius-sm); white-space: nowrap; } .badge.pill { border-radius: var(--radius-full); } .badge-success { background: var(--lw-success-bg); color: var(--lw-success-fg); } .badge-warning { background: var(--lw-warning-bg); color: var(--lw-warning-fg); } .badge-danger { background: var(--lw-danger-bg); color: var(--lw-danger-fg); } .badge-info { background: var(--lw-info-bg); color: var(--lw-info-fg); } .badge-neutral { background: var(--lw-neutral-bg); color: var(--lw-neutral-fg); } .badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; } /* ---------------- Card ---------------- */ .card { background: var(--surface-card); border: 1px solid var(--border-default); border-radius: var(--radius-lg); padding: 20px 22px; transition: border-color var(--dur-normal) var(--ease-standard); } .card.interactive { cursor: pointer; } .card.interactive:hover { border-color: var(--border-strong); } /* ---------------- Button ---------------- */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-family: inherit; font-size: var(--text-sm); font-weight: var(--fw-semibold); line-height: 1; padding: 0 16px; height: 40px; border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer; white-space: nowrap; transition: background var(--dur-normal) var(--ease-standard), border-color var(--dur-normal) var(--ease-standard), color var(--dur-normal) var(--ease-standard); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .btn-sm { height: 32px; padding: 0 12px; font-size: var(--text-xs); } .btn-primary { background: var(--action-bg); color: var(--action-fg); } .btn-primary:not(:disabled):hover { background: var(--action-bg-hover); } .btn-primary:not(:disabled):active { background: var(--action-bg-active); } .btn-secondary { background: transparent; border-color: var(--border-strong); color: var(--text-body); } .btn-secondary:not(:disabled):hover { border-color: var(--accent); color: var(--text-heading); } .btn-ghost { background: transparent; color: var(--accent); } .btn-ghost:not(:disabled):hover { color: var(--link-hover); background: rgba(255, 255, 255, 0.04); } .btn-danger { background: transparent; border-color: var(--border-strong); color: var(--lw-danger-fg); } .btn-danger:not(:disabled):hover { border-color: var(--lw-danger-fg); background: rgba(99, 23, 27, 0.35); } /* ---------------- Field (Input / Select / Textarea) ---------------- */ .field { display: flex; flex-direction: column; gap: 6px; } .field-label { font-size: var(--text-xs); font-weight: var(--fw-semibold); color: var(--text-faint); text-transform: uppercase; letter-spacing: var(--ls-wide); } .input, .select, .textarea { width: 100%; font-family: inherit; font-size: var(--text-sm); color: var(--text-body); background: var(--surface-page); border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 0 12px; height: 40px; transition: border-color var(--dur-normal) var(--ease-standard), box-shadow var(--dur-normal) var(--ease-standard); } .textarea { height: auto; padding: 10px 12px; line-height: var(--lh-normal); resize: vertical; min-height: 68px; } .input::placeholder, .textarea::placeholder { color: var(--text-faint); } .input:focus, .select:focus, .textarea:focus { outline: none; border-color: var(--accent); box-shadow: var(--shadow-focus); } .select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23718096' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; } .select option { background: var(--surface-card); color: var(--text-body); } /* ---------------- Tabs ---------------- */ .tabs { display: inline-flex; gap: 4px; background: var(--surface-page); border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 3px; } .tab { font-family: inherit; font-size: var(--text-sm); font-weight: var(--fw-semibold); color: var(--text-muted); background: transparent; border: 0; border-radius: 6px; padding: 6px 12px; cursor: pointer; transition: background var(--dur-fast), color var(--dur-fast); } .tab:hover { color: var(--text-body); } .tab.active { background: var(--surface-inset); color: var(--text-heading); } /* ---------------- Stat ---------------- */ .stat-value { font-size: var(--text-2xl); font-weight: var(--fw-bold); line-height: 1.1; color: var(--text-heading); letter-spacing: var(--ls-tight); } .stat-value.accent { color: var(--accent); } .stat-label { font-size: var(--text-sm); color: var(--text-muted); margin-top: 4px; } .stat-sub { font-size: var(--text-xs); color: var(--text-faint); font-family: var(--font-mono); } /* Inline text button (the auth gate's "Forgot password?" / mode switches). */ .btn-link { background: none; border: none; padding: 0; color: var(--lw-blue-200, #90cdf4); font-size: inherit; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; } .btn-link:hover { opacity: 0.85; } /* ---------------- Callout ---------------- */ .callout { border-radius: var(--radius-md); padding: 12px 14px; font-size: var(--text-sm); border: 1px solid transparent; } .callout-info { background: rgba(26, 54, 93, 0.35); border-color: rgba(144, 205, 244, 0.25); color: var(--lw-blue-200); } .callout-danger { background: rgba(99, 23, 27, 0.35); border-color: rgba(254, 178, 178, 0.25); color: var(--lw-danger-fg); } .callout-success { background: rgba(26, 71, 49, 0.35); border-color: rgba(154, 230, 180, 0.25); color: var(--lw-success-fg); } /* ---------------- CodeBlock ---------------- */ .codeblock { background: var(--surface-page); border: 1px solid var(--border-default); border-radius: var(--radius-md); overflow: hidden; } .codeblock-head { display: flex; align-items: center; justify-content: space-between; padding: 7px 12px; border-bottom: 1px solid var(--border-default); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); background: var(--surface-card); } .codeblock pre { margin: 0; padding: 14px 16px; overflow-x: auto; font-family: var(--font-mono); font-size: var(--text-xs); line-height: 1.55; color: var(--text-body); } .codeblock .diff-add { color: var(--lw-success-fg); } .codeblock .diff-del { color: var(--lw-danger-fg); } /* ---------------- Toggle ---------------- */ .toggle { width: 38px; height: 22px; border-radius: var(--radius-full); background: var(--surface-inset); border: 0; position: relative; cursor: pointer; flex-shrink: 0; transition: background var(--dur-normal); padding: 0; } .toggle.on { background: var(--action-bg); } .toggle .knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: left var(--dur-normal); } .toggle.on .knob { left: 18px; } /* ---------------- App shell ---------------- */ .app { display: flex; min-height: 100vh; } .sidebar { width: 216px; flex-shrink: 0; border-right: 1px solid var(--border-default); padding: 20px 12px; display: flex; flex-direction: column; gap: 3px; position: sticky; top: 0; height: 100vh; overflow-y: auto; } .brand { display: flex; align-items: center; gap: 9px; font-size: var(--text-md); font-weight: var(--fw-bold); color: var(--text-heading); padding: 2px 10px 16px; } .brand .logo { width: 22px; height: 22px; border-radius: 6px; background: linear-gradient(135deg, #90cdf4, #667eea); flex-shrink: 0; } .nav-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 10px; border-radius: var(--radius-md); color: var(--text-muted); font-size: var(--text-sm); font-weight: var(--fw-semibold); background: transparent; border: 0; width: 100%; cursor: pointer; text-align: left; transition: background var(--dur-fast), color var(--dur-fast); } .nav-item:hover { background: var(--surface-raised); color: var(--text-body); } .nav-item.active { background: var(--surface-inset); color: var(--text-heading); } .nav-item .count { font-size: var(--text-xs); font-family: var(--font-mono); color: var(--text-faint); } .nav-item.active .count { color: var(--accent); } .sidebar-spacer { flex: 1; } .sidebar-foot { border-top: 1px solid var(--border-default); padding-top: 12px; display: flex; flex-direction: column; gap: 6px; } .main { flex: 1; min-width: 0; display: flex; flex-direction: column; height: 100vh; overflow: hidden; } .main-scroll { flex: 1; overflow-y: auto; } /* section header */ .section-head { padding: 26px 32px 18px; } .section-title { font-size: var(--text-xl); font-weight: var(--fw-bold); color: var(--text-heading); letter-spacing: var(--ls-tight); } .section-desc { font-size: var(--text-sm); color: var(--text-muted); margin-top: 4px; } .section-body { padding: 0 32px 40px; display: flex; flex-direction: column; gap: 18px; } /* stat row */ .stat-row { display: flex; background: var(--surface-card); border: 1px solid var(--border-default); border-radius: var(--radius-lg); overflow: hidden; flex-wrap: wrap; } .stat-cell { flex: 1; min-width: 150px; padding: 18px 22px; border-left: 1px solid var(--border-default); } .stat-cell:first-child { border-left: 0; } /* toolbar / form grid */ .row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; } .form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; } .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; } .grow { flex: 1; min-width: 180px; } .eyebrow { font-size: var(--text-xs); font-weight: var(--fw-semibold); color: var(--text-faint); text-transform: uppercase; letter-spacing: var(--ls-wide); } /* inbox split (Runs) */ .runs { display: flex; flex-direction: column; flex: 1; min-height: 0; } .runs-stats { padding: 16px 20px; border-bottom: 1px solid var(--border-default); flex-shrink: 0; } .split { display: flex; flex: 1; min-height: 0; } .split-list { width: 340px; flex-shrink: 0; border-right: 1px solid var(--border-default); display: flex; flex-direction: column; height: 100%; } .split-list-head { padding: 22px 20px 12px; display: flex; flex-direction: column; gap: 12px; } .split-list-scroll { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 4px; } .split-detail { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow-y: auto; } .run-row { cursor: pointer; padding: 10px 12px; border-radius: var(--radius-md); border-left: 3px solid transparent; display: flex; flex-direction: column; gap: 5px; background: transparent; border-top: 0; border-right: 0; border-bottom: 0; width: 100%; text-align: left; font: inherit; color: inherit; transition: background var(--dur-fast); } .run-row:hover { background: var(--surface-raised); } .run-row.selected { background: var(--surface-inset); border-left-color: var(--accent); } .run-row-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; } .run-project { color: var(--accent); font-size: var(--text-sm); font-weight: var(--fw-semibold); } /* key/value */ .kv { display: grid; grid-template-columns: 150px 1fr; gap: 8px 16px; margin: 0; } .kv dt { color: var(--text-faint); font-size: var(--text-sm); } .kv dd { margin: 0; color: var(--text-body); font-size: var(--text-sm); } .kv dd ul { margin: 0; padding-left: 18px; } /* table */ .table-wrap { overflow-x: auto; border: 1px solid var(--border-default); border-radius: var(--radius-lg); } table.tbl { width: 100%; border-collapse: collapse; font-size: var(--text-sm); } table.tbl th { text-align: left; font-size: var(--text-xs); font-weight: var(--fw-semibold); color: var(--text-faint); text-transform: uppercase; letter-spacing: var(--ls-wide); padding: 11px 14px; border-bottom: 1px solid var(--border-default); background: var(--surface-card); white-space: nowrap; } table.tbl td { padding: 11px 14px; border-bottom: 1px solid var(--border-default); color: var(--text-body); vertical-align: top; } table.tbl tr:last-child td { border-bottom: 0; } table.tbl tbody tr:hover { background: rgba(255, 255, 255, 0.02); } .chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: var(--radius-sm); background: var(--surface-card); border: 1px solid var(--border-default); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); cursor: pointer; transition: border-color var(--dur-fast); } .chip:hover { border-color: var(--border-strong); } .chip.selected { border-color: var(--accent); } .card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; } .card-title { color: var(--accent); font-weight: var(--fw-bold); font-size: var(--text-lg); } .stack { display: flex; flex-direction: column; gap: 10px; } .divider { height: 1px; background: var(--border-default); border: 0; margin: 0; } .empty { color: var(--text-muted); font-size: var(--text-sm); padding: 4px 0; } .pager { display: flex; align-items: center; gap: 12px; } /* banner */ .banner { margin: 0 32px; padding: 10px 14px; border-radius: var(--radius-md); font-size: var(--text-sm); } .banner.ok { background: rgba(26, 71, 49, 0.35); color: var(--lw-success-fg); border: 1px solid rgba(154, 230, 180, 0.25); } .banner.err { background: rgba(99, 23, 27, 0.35); color: var(--lw-danger-fg); border: 1px solid rgba(254, 178, 178, 0.25); } /* ---------------- Token gate ---------------- */ .gate { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; } .gate-card { width: 100%; max-width: 420px; background: var(--surface-card); border: 1px solid var(--border-default); border-radius: var(--radius-lg); padding: 32px; display: flex; flex-direction: column; gap: 14px; box-shadow: var(--shadow-lg); } .gate-brand { display: flex; align-items: center; gap: 10px; font-size: var(--text-xl); font-weight: var(--fw-bold); color: var(--text-heading); } /* small helpers */ .hstack { display: flex; align-items: center; gap: 10px; } .hstack.wrap { flex-wrap: wrap; } .spacer { flex: 1; } .mt8 { margin-top: 8px; } .mt14 { margin-top: 14px; } @media (max-width: 860px) { .split-list { width: 280px; } .grid-2 { grid-template-columns: 1fr; } }