Files
ZachariahSharma f473be4033 feat: add authenticated URL-mode MCP servers from Control Panel
Add an optional auth-token (bearer) field and a custom-headers textarea to
the MCP 'Add server' pane. URL-mode servers are now registered by writing
hermes config directly via the venv's config helpers (save_env_value +
save_config) instead of shelling into the interactive, network-probing
'hermes mcp add --url' flow that hung/504'd on auth challenges.

- Token is stored in ~/.hermes/.env (mode 600) and referenced in config.yaml
  as 'Authorization: Bearer ${MCP_<NAME>_API_KEY}' — never plaintext config.
- Registration is non-blocking: no live probe, so a slow/unreachable server
  can't hang the request.
- Add a per-server 'Test' button (POST /api/mcp/test -> hermes mcp test).
- Contract tests for the new fields, payload, no-probe path, and test route.
2026-06-10 21:19:05 -06:00

1665 lines
38 KiB
CSS

/* HERMES · Operations Console
* ─────────────────────────────────────────────────────────────────────────
* Aesthetic: aerospace technical document. Warm document cream, deep ink,
* single hazard-orange accent. Stencil display + grotesk body + slab mono.
* Generous whitespace, hairline rules, corner registration marks, faint
* grid background. The look of an FAA ops binder, deliberately.
*/
:root {
/* Paper stock */
--paper: #f6f1e3;
--paper-2: #efe9d6;
--paper-3: #e6ddc4;
--paper-4: #ddd2b6;
--paper-card: #faf6ea;
/* Ink */
--ink: #1c1c1f;
--ink-2: #46443d;
--ink-3: #807c6c;
--ink-4: #b0aa97;
/* Rules */
--rule: #c4bba1;
--rule-soft: #d9d1ba;
--rule-faint: #e5ddc6;
/* Accents — single hazard orange, used surgically */
--hazard: #c95a2b;
--hazard-deep: #a4471f;
--hazard-soft: rgba(201, 90, 43, 0.10);
--hazard-line: rgba(201, 90, 43, 0.30);
/* Signal — quieted */
--ok: #5d7a32;
--ok-soft: rgba(93, 122, 50, 0.10);
--err: #8c2727;
--err-soft: rgba(140, 39, 39, 0.08);
--info: #2d5d80;
/* Type */
--ff-display: "Big Shoulders Stencil Display", "Big Shoulders Display", "Impact", sans-serif;
--ff-sans: "Public Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
--ff-mono: "JetBrains Mono", ui-monospace, "Menlo", "Consolas", monospace;
/* Geometry */
--nav-w: 236px;
--readout-h: 32px;
--gutter: 28px;
--radius: 3px; /* very small — feels printed, not webby */
--radius-md: 5px;
}
*, *::before, *::after { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--ff-sans);
font-size: 14px;
line-height: 1.5;
height: 100vh;
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
body {
display: grid;
grid-template-columns: var(--nav-w) 1fr;
grid-template-rows: 1fr var(--readout-h);
grid-template-areas: "nav main" "bar bar";
}
/* Faint 16px grid — like the squared paper inside an engineering pad */
body::before {
content: "";
position: fixed; inset: 0;
pointer-events: none; z-index: 0;
background-image:
linear-gradient(to right, rgba(28, 28, 31, 0.022) 1px, transparent 1px),
linear-gradient(to bottom, rgba(28, 28, 31, 0.022) 1px, transparent 1px);
background-size: 16px 16px;
}
/* Decorative diagonal stamp watermark — barely visible, in one corner */
body::after {
content: "REV / 0.15.1 / CONFIDENTIAL / NOT FOR DISTRIBUTION";
position: fixed;
bottom: 60px;
right: -90px;
transform: rotate(-90deg);
transform-origin: bottom right;
font-family: var(--ff-mono);
font-size: 9.5px;
font-weight: 500;
letter-spacing: 0.4em;
color: var(--ink);
opacity: 0.05;
pointer-events: none;
z-index: 0;
white-space: nowrap;
}
/* ─── Registration crosshairs in viewport corners ────────────────────── */
.reg {
position: fixed;
width: 16px; height: 16px;
z-index: 4;
pointer-events: none;
}
.reg::before, .reg::after {
content: ""; position: absolute;
background: var(--ink-3);
}
.reg::before { left: 7px; top: 0; bottom: 0; width: 1px; }
.reg::after { top: 7px; left: 0; right: 0; height: 1px; }
.reg-tl { top: 12px; left: 12px; }
.reg-tr { top: 12px; right: 12px; }
.reg-bl { bottom: 44px; left: 12px; }
.reg-br { bottom: 44px; right: 12px; }
/* ─── Sidebar / Masthead ─────────────────────────────────────────────── */
.nav {
grid-area: nav;
background: var(--paper);
border-right: 1px solid var(--rule);
padding: 28px 22px 18px;
display: flex;
flex-direction: column;
z-index: 2;
overflow-y: auto;
position: relative;
}
.masthead {
margin-bottom: 26px;
padding-bottom: 18px;
border-bottom: 1px solid var(--rule);
}
.masthead-title {
font-family: var(--ff-display);
font-weight: 800;
font-size: 38px;
line-height: 0.85;
letter-spacing: 0.04em;
color: var(--ink);
position: relative;
}
.masthead-title::after {
content: "";
display: inline-block;
width: 7px; height: 7px;
background: var(--hazard);
margin-left: 6px;
vertical-align: super;
transform: translateY(-2px);
}
.masthead-sub {
font-family: var(--ff-mono);
font-size: 9.5px;
font-weight: 500;
letter-spacing: 0.22em;
color: var(--ink-2);
margin-top: 8px;
text-transform: uppercase;
}
.masthead-meta {
font-family: var(--ff-mono);
font-size: 9.5px;
letter-spacing: 0.16em;
color: var(--ink-3);
margin-top: 14px;
text-transform: uppercase;
display: flex;
gap: 6px;
}
.masthead-meta span:first-child { color: var(--ink-3); }
.masthead-meta span:last-child { color: var(--ink-2); font-weight: 500; }
.masthead-stamp {
margin-top: 16px;
padding: 6px 8px;
border: 1px dashed var(--rule);
border-radius: var(--radius);
font-family: var(--ff-mono);
font-size: 9px;
letter-spacing: 0.14em;
color: var(--ink-3);
text-transform: uppercase;
display: flex;
align-items: center;
gap: 6px;
}
.masthead-stamp .stamp-line { color: var(--hazard); font-size: 8px; }
/* Numbered nav — large stencil index on the left, label beside */
.dial {
display: flex;
flex-direction: column;
gap: 0;
}
.dial-item {
display: grid;
grid-template-columns: 40px 1fr;
align-items: baseline;
gap: 14px;
padding: 11px 0 11px 10px;
margin-left: -10px;
border: 0;
background: transparent;
cursor: pointer;
text-align: left;
border-left: 2px solid transparent;
color: var(--ink-2);
transition: color 0.14s, border-left-color 0.14s;
}
.dial-item:hover { color: var(--ink); }
.dial-item:hover .dial-num { color: var(--ink-2); }
.dial-item[data-active] {
color: var(--ink);
border-left-color: var(--hazard);
}
.dial-item[data-active] .dial-num { color: var(--hazard); }
.dial-item[data-active] .dial-name::after {
content: "←";
margin-left: 8px;
color: var(--hazard);
font-family: var(--ff-mono);
font-size: 11px;
}
.dial-num {
font-family: var(--ff-display);
font-weight: 700;
font-size: 24px;
letter-spacing: 0.04em;
line-height: 0.9;
color: var(--ink-3);
}
.dial-name {
font-family: var(--ff-sans);
font-weight: 500;
font-size: 14.5px;
letter-spacing: -0.005em;
}
.nav-foot {
margin-top: auto;
padding: 14px 0 0;
border-top: 1px solid var(--rule);
font-family: var(--ff-mono);
font-size: 10px;
color: var(--ink-3);
display: flex;
flex-direction: column;
gap: 4px;
}
.kv { display: flex; justify-content: space-between; gap: 10px; }
.kv-k {
color: var(--ink-4);
text-transform: uppercase;
letter-spacing: 0.16em;
}
.kv-v {
color: var(--ink-2);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
max-width: 150px; text-align: right;
}
/* ─── Main column ────────────────────────────────────────────────────── */
.main {
grid-area: main;
overflow-y: auto;
padding: 32px 48px 48px;
position: relative;
z-index: 1;
}
.pane {
display: none;
flex-direction: column;
gap: 28px;
max-width: 1180px;
}
.pane[data-active] { display: flex; animation: fade-in 240ms ease-out; }
@keyframes fade-in {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
/* Folio — chapter-opener header for each pane */
.folio {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 24px;
align-items: end;
padding-bottom: 22px;
border-bottom: 1px solid var(--rule);
position: relative;
}
.folio::after {
/* a thin orange tick at the folio bottom-left corner */
content: "";
position: absolute;
left: 0; bottom: -1px;
width: 56px; height: 2px;
background: var(--hazard);
}
.folio-rail {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 110px;
}
.folio-num {
font-family: var(--ff-display);
font-weight: 800;
font-size: 72px;
line-height: 0.82;
letter-spacing: 0.04em;
color: var(--ink);
}
.folio-tag {
font-family: var(--ff-mono);
font-size: 9.5px;
font-weight: 500;
letter-spacing: 0.22em;
color: var(--ink-3);
text-transform: uppercase;
}
.folio-title {
margin: 0;
font-family: var(--ff-sans);
font-weight: 300;
font-size: 44px;
line-height: 1;
letter-spacing: -0.03em;
color: var(--ink);
align-self: end;
padding-bottom: 6px;
}
.folio-actions {
display: flex;
gap: 8px;
padding-bottom: 8px;
}
/* ─── Block (sub-section panel) ──────────────────────────────────────── */
.block {
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 22px 24px;
position: relative;
}
.block-head {
display: flex;
align-items: baseline;
gap: 12px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--rule-soft);
}
.block-id {
font-family: var(--ff-mono);
font-size: 10px;
font-weight: 600;
letter-spacing: 0.18em;
color: var(--hazard);
text-transform: uppercase;
}
.block-name {
font-family: var(--ff-sans);
font-weight: 500;
font-size: 14.5px;
letter-spacing: -0.005em;
color: var(--ink);
}
.block-meta {
font-family: var(--ff-mono);
font-size: 10px;
letter-spacing: 0.1em;
color: var(--ink-3);
text-transform: uppercase;
}
.block-actions {
margin-left: auto;
display: flex;
gap: 6px;
}
/* ─── Grid ───────────────────────────────────────────────────────────── */
.grid { display: grid; gap: 14px; }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(252px, 1fr)); }
/* ─── Provider cards ─────────────────────────────────────────────────── */
.pcard {
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 14px 16px 14px;
display: flex;
flex-direction: column;
gap: 12px;
position: relative;
transition: border-color 0.16s;
}
.pcard:hover { border-color: var(--ink-3); }
/* Stencil number badge in corner — like a part-number stamp */
.pcard-mark {
position: absolute;
top: 12px; right: 14px;
font-family: var(--ff-display);
font-weight: 700;
font-size: 26px;
letter-spacing: 0.04em;
line-height: 1;
color: var(--ink-4);
pointer-events: none;
}
.pcard.busy .pcard-mark { color: var(--hazard); }
/* Top busy-LED bar — only when an OAuth is in flight */
.pcard::before {
content: "";
position: absolute;
top: -1px; left: -1px;
width: 56px; height: 2px;
background: var(--rule);
border-radius: 0;
transition: background 0.2s;
}
.pcard.busy::before {
background: var(--hazard);
animation: led-pulse 1.4s ease-in-out infinite;
}
@keyframes led-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.45; }
}
.pcard-kind {
font-family: var(--ff-mono);
font-size: 9px;
font-weight: 600;
letter-spacing: 0.18em;
color: var(--ink-3);
text-transform: uppercase;
}
.pcard-title {
font-family: var(--ff-sans);
font-weight: 500;
font-size: 20px;
letter-spacing: -0.02em;
line-height: 1;
color: var(--ink);
margin: -2px 0 0;
}
.pcard-sub {
font-family: var(--ff-mono);
font-size: 10.5px;
color: var(--ink-3);
letter-spacing: 0.04em;
margin-top: 2px;
}
.auth-pill {
display: inline-flex;
align-items: center;
width: fit-content;
margin-top: 7px;
padding: 3px 7px;
border: 1px solid var(--rule);
border-radius: 999px;
font-family: var(--ff-mono);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-2);
background: rgba(255,255,255,0.03);
}
.auth-pill[data-state="authenticated"] {
border-color: rgba(168, 210, 122, 0.45);
color: #a8d27a;
}
.auth-pill[data-state="usage_limited"] {
border-color: rgba(232, 138, 138, 0.55);
color: #e88a8a;
}
.auth-pill[data-state="error"] {
border-color: rgba(232, 138, 138, 0.55);
color: #e88a8a;
}
.auth-pill[data-state="unauthenticated"] {
border-color: rgba(246, 241, 227, 0.22);
color: var(--ink-3);
}
.pcard-creds {
list-style: none;
margin: 0; padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
min-height: 22px;
border-top: 1px solid var(--rule-soft);
padding-top: 10px;
}
.pcard-creds li {
display: grid;
grid-template-columns: 22px 1fr auto;
gap: 6px;
align-items: center;
font-family: var(--ff-mono);
font-size: 10.5px;
color: var(--ink-2);
padding: 4px 0;
border-bottom: 1px dotted var(--rule-soft);
}
.pcard-creds li:last-child { border-bottom: 0; }
.pcard-creds .ix {
color: var(--ink-3);
font-weight: 600;
}
.pcard-creds .label {
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
min-width: 0;
}
.pcard-creds .label > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.pcard-creds .account-id {
color: var(--ink-3);
font-size: 9.5px;
font-weight: 400;
}
.pcard-creds li.active .ix,
.pcard-creds li.active .label {
color: var(--hazard);
font-weight: 500;
}
.pcard-creds .x {
color: var(--ink-4);
background: none; border: 0; cursor: pointer; font: inherit;
padding: 0 6px;
font-family: var(--ff-mono);
font-size: 13px;
line-height: 1;
}
.pcard-creds .x:hover { color: var(--err); }
.pcard-empty {
color: var(--ink-4);
font-family: var(--ff-mono);
font-size: 10px;
letter-spacing: 0.18em;
padding: 8px 0 4px;
text-align: center;
text-transform: uppercase;
}
.pcard-actions {
display: flex;
gap: 6px;
margin-top: auto;
}
.pcard-actions .grow { flex: 1; }
.pcard-log {
background: var(--paper-2);
color: var(--ink-2);
border: 1px solid var(--rule-soft);
border-radius: var(--radius);
padding: 7px 9px;
font-family: var(--ff-mono);
font-size: 10px;
line-height: 1.55;
max-height: 90px;
overflow-y: auto;
white-space: pre-wrap;
margin: 0;
}
.oauth-input {
display: grid;
gap: 5px;
color: var(--ink-3);
font-family: var(--ff-mono);
font-size: 10px;
}
.oauth-input > div {
display: flex;
gap: 6px;
}
.oauth-input input {
min-width: 0;
flex: 1;
border: 1px solid var(--rule);
border-radius: var(--radius);
background: var(--paper-card);
color: var(--ink);
font: inherit;
padding: 6px 8px;
}
/* ─── Buttons ────────────────────────────────────────────────────────── */
.btn, .btn-primary, .btn-mini, .btn-danger, .btn-ghost {
font: inherit;
font-family: var(--ff-sans);
font-weight: 500;
cursor: pointer;
border-radius: var(--radius);
border: 1px solid var(--ink);
background: var(--paper-card);
color: var(--ink);
padding: 7px 14px;
font-size: 12.5px;
letter-spacing: 0;
transition: background 0.12s, color 0.12s, border-color 0.12s, transform 0.06s;
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn:active { transform: translateY(0.5px); }
.btn-primary {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
font-weight: 600;
letter-spacing: 0.005em;
}
.btn-primary:hover {
background: var(--hazard);
border-color: var(--hazard);
color: var(--paper-card);
}
.btn-mini {
padding: 4px 9px;
font-size: 10px;
background: transparent;
color: var(--ink-2);
border-color: var(--rule);
font-family: var(--ff-mono);
text-transform: uppercase;
letter-spacing: 0.14em;
font-weight: 600;
}
.btn-mini:hover { color: var(--ink); border-color: var(--ink); background: transparent; }
.btn-danger {
background: transparent;
color: var(--err);
border-color: var(--err);
}
.btn-danger:hover { background: var(--err); color: var(--paper); }
.btn-ghost {
background: transparent;
border-color: transparent;
color: var(--ink-2);
font-family: var(--ff-mono);
font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
font-weight: 600;
padding: 6px 10px;
}
.btn-ghost:hover { color: var(--hazard); background: transparent; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
/* ─── Forms ──────────────────────────────────────────────────────────── */
input, select {
font: inherit;
font-family: var(--ff-sans);
background: var(--paper-card);
color: var(--ink);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 7px 11px;
font-size: 13px;
}
input:focus, select:focus {
outline: none;
border-color: var(--ink);
box-shadow: inset 0 -2px 0 var(--hazard);
}
input::placeholder {
color: var(--ink-4);
font-family: var(--ff-mono);
font-size: 11px;
letter-spacing: 0.04em;
}
select {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0L4 5L8 0Z' fill='%23807c6c'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
padding-right: 28px;
}
.row { display: flex; align-items: center; gap: 8px; }
.row.gap { gap: 14px; }
.grow { flex: 1 1 auto; }
.field-inline {
display: inline-flex;
align-items: center;
gap: 6px;
font-family: var(--ff-mono);
font-size: 10.5px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
}
.field-inline input {
width: 70px;
padding: 4px 8px;
font-size: 12px;
}
.key-row { display: flex; gap: 8px; align-items: center; }
.key-row input { flex: 1; font-family: var(--ff-mono); }
.status {
font-family: var(--ff-mono);
font-size: 10.5px;
color: var(--ink-3);
min-height: 14px;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.status.ok { color: var(--ok); }
.status.err { color: var(--err); }
/* ─── Routing pane ───────────────────────────────────────────────────── */
.primary-row {
display: grid;
grid-template-columns: 220px 1fr auto;
gap: 10px;
}
.chain-edit {
list-style: none;
margin: 0 0 16px;
padding: 0;
display: flex;
flex-direction: column;
gap: 5px;
counter-reset: hop;
}
.chain-edit li {
display: grid;
grid-template-columns: 36px 200px 1fr auto auto;
gap: 10px;
align-items: center;
padding: 7px 10px;
background: var(--paper);
border: 1px solid var(--rule);
border-radius: var(--radius);
position: relative;
}
.chain-edit li::before {
/* hop arrow on the left */
content: "▸";
position: absolute;
left: -14px;
color: var(--ink-4);
font-size: 11px;
}
.chain-edit li:first-child::before { color: var(--hazard); }
.chain-edit li .idx {
font-family: var(--ff-display);
font-weight: 700;
font-size: 22px;
text-align: center;
color: var(--hazard);
line-height: 1;
}
.chain-edit li select, .chain-edit li input {
background: var(--paper-card);
padding: 5px 10px;
font-size: 12px;
}
.chain-edit li .moves { display: flex; gap: 2px; }
.chain-edit li .moves button {
background: var(--paper-card);
border: 1px solid var(--rule);
color: var(--ink-2);
padding: 4px 9px;
font-size: 11px;
font-family: var(--ff-mono);
}
.chain-edit li .moves button:hover { color: var(--hazard); border-color: var(--hazard); }
/* ─── Tables ─────────────────────────────────────────────────────────── */
.table {
width: 100%;
border-collapse: collapse;
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
overflow: hidden;
}
.table th, .table td {
padding: 9px 14px;
font-size: 12.5px;
text-align: left;
border-bottom: 1px solid var(--rule-soft);
}
.table th {
background: var(--paper-2);
color: var(--ink-3);
font-family: var(--ff-mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 10px;
}
.table tr:last-child td { border-bottom: 0; }
.table tr:hover td { background: var(--paper-2); }
.table .badge {
display: inline-block;
padding: 2px 8px;
font-size: 9.5px;
border-radius: 0;
background: transparent;
border: 1px solid var(--rule);
font-family: var(--ff-mono);
letter-spacing: 0.12em;
color: var(--ink-3);
text-transform: uppercase;
}
/* ─── Lists ──────────────────────────────────────────────────────────── */
.list {
list-style: none; margin: 0; padding: 0;
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
overflow: hidden;
}
.list li {
padding: 12px 18px;
border-bottom: 1px solid var(--rule-soft);
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
font-size: 13px;
}
.list li:last-child { border-bottom: 0; }
.list .id {
font-family: var(--ff-mono);
font-size: 10.5px;
color: var(--ink-3);
letter-spacing: 0.04em;
}
.list .empty {
color: var(--ink-4);
font-family: var(--ff-mono);
font-size: 10.5px;
letter-spacing: 0.18em;
text-transform: uppercase;
justify-content: center;
padding: 24px 0;
}
/* ─── Raw output / Logs ──────────────────────────────────────────────── */
.raw {
background: var(--paper-card);
color: var(--ink-2);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 18px 22px;
margin: 0;
font-family: var(--ff-mono);
font-size: 11.5px;
line-height: 1.7;
white-space: pre-wrap;
max-height: 62vh;
overflow-y: auto;
position: relative;
}
.raw::before {
/* dog-ear / corner-fold marker */
content: "";
position: absolute;
top: 0; right: 0;
width: 14px; height: 14px;
background: linear-gradient(135deg, transparent 50%, var(--rule-soft) 50%);
}
.log {
background: var(--paper-2);
color: var(--ink-2);
border: 1px solid var(--rule-soft);
border-radius: var(--radius);
padding: 10px 12px;
margin: 12px 0 0;
font-family: var(--ff-mono);
font-size: 10.5px;
line-height: 1.6;
max-height: 160px;
overflow-y: auto;
white-space: pre-wrap;
}
/* ─── MCP form ───────────────────────────────────────────────────────── */
.mcp-form {
display: grid;
grid-template-columns: repeat(2, 1fr) auto;
gap: 10px;
align-items: start;
}
.mcp-form textarea {
grid-column: 1 / 3;
resize: vertical;
min-height: 2.4em;
font-family: var(--ff-mono);
}
.mcp-form button { grid-row: 1 / -1; align-self: stretch; }
.mcp-hint {
margin: 10px 0 0;
font-size: 0.8rem;
line-height: 1.5;
color: var(--ink-60, #555);
}
.mcp-hint code { font-family: var(--ff-mono); font-size: 0.92em; }
.row-actions { display: inline-flex; gap: 8px; align-items: center; }
/* ─── Readout / bottom status bar ────────────────────────────────────── */
.readout {
grid-area: bar;
background: var(--ink);
color: var(--paper);
border-top: 1px solid var(--ink);
display: flex;
align-items: center;
gap: 0;
padding: 0 16px;
font-family: var(--ff-mono);
font-size: 10.5px;
letter-spacing: 0.08em;
z-index: 5;
text-transform: uppercase;
}
.rd-tag {
font-family: var(--ff-display);
font-weight: 800;
font-size: 13px;
letter-spacing: 0.1em;
color: var(--hazard);
padding: 0 10px 0 0;
}
.rd-sep { color: var(--ink-3); padding: 0 12px; }
.rd-cell { display: inline-flex; align-items: baseline; gap: 6px; }
.rd-k {
color: var(--ink-4);
font-weight: 500;
letter-spacing: 0.18em;
}
#bar-primary { color: var(--hazard); font-weight: 600; }
#bar-chain { color: var(--paper); font-weight: 600; }
#bar-version { color: var(--paper); font-weight: 500; }
.rd-msg { color: var(--paper); padding-right: 12px; min-height: 14px; }
.rd-led {
width: 7px; height: 7px;
border-radius: 50%;
background: var(--ok);
margin-left: 4px;
box-shadow: 0 0 6px var(--ok);
}
.readout.busy .rd-led { background: var(--hazard); box-shadow: 0 0 6px var(--hazard); animation: led-pulse 1.4s ease-in-out infinite; }
.readout.error .rd-led { background: var(--err); box-shadow: 0 0 6px var(--err); }
/* ─── Sidebar group divider ──────────────────────────────────────────── */
.dial-divider {
margin: 14px -22px 4px 0;
padding: 14px 12px 4px;
border-top: 1px solid var(--rule);
position: relative;
}
.dial-divider span {
font-family: var(--ff-mono);
font-size: 9px;
letter-spacing: 0.24em;
color: var(--ink-4);
text-transform: uppercase;
}
/* ─── Tabs ───────────────────────────────────────────────────────────── */
.tabs {
display: flex;
gap: 0;
border-bottom: 1px solid var(--rule);
margin-bottom: 22px;
}
.tab {
font: inherit;
font-family: var(--ff-sans);
font-weight: 500;
font-size: 13px;
color: var(--ink-3);
background: transparent;
border: 0;
border-bottom: 2px solid transparent;
padding: 10px 18px 12px;
cursor: pointer;
margin-bottom: -1px;
transition: color 0.12s, border-bottom-color 0.12s;
}
.tab:hover { color: var(--ink); }
.tab[data-active] {
color: var(--ink);
border-bottom-color: var(--hazard);
font-weight: 600;
}
.tab-count {
display: inline-block;
margin-left: 4px;
font-family: var(--ff-mono);
font-size: 10.5px;
color: var(--ink-4);
font-weight: 500;
}
.tab[data-active] .tab-count { color: var(--hazard); }
.tab-body { display: none; }
.tab-body[data-active] { display: block; }
/* ─── Cards grid (skills/plugins/bundles) ────────────────────────────── */
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(308px, 1fr));
gap: 12px;
}
.scard {
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 14px 16px 12px;
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
transition: border-color 0.14s;
}
.scard:hover { border-color: var(--ink-3); }
.scard-tag {
position: absolute;
top: 12px; right: 14px;
font-family: var(--ff-mono);
font-size: 9px;
font-weight: 600;
letter-spacing: 0.16em;
color: var(--ink-4);
text-transform: uppercase;
}
.scard.disabled .scard-tag,
.scard[data-status='disabled'] .scard-tag,
.scard[data-status='not enabled'] .scard-tag {
color: var(--ink-4);
}
.scard[data-status='enabled'] .scard-tag {
color: var(--ok);
}
.scard.user .scard-tag { color: var(--hazard); }
.scard-title {
font-family: var(--ff-sans);
font-weight: 600;
font-size: 15px;
letter-spacing: -0.01em;
line-height: 1.2;
color: var(--ink);
margin: 0;
padding-right: 60px;
}
.scard-sub {
font-family: var(--ff-mono);
font-size: 10px;
color: var(--ink-3);
letter-spacing: 0.04em;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.scard-sub > span { display: inline-flex; gap: 4px; align-items: baseline; }
.scard-sub strong {
color: var(--ink-2);
font-weight: 600;
}
.scard-desc {
font-size: 12.5px;
color: var(--ink-2);
margin: 0;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.scard-chips {
display: flex;
gap: 4px;
flex-wrap: wrap;
margin-top: 2px;
}
.chip {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
background: var(--paper-2);
border: 1px solid var(--rule-soft);
font-family: var(--ff-mono);
font-size: 9.5px;
letter-spacing: 0.08em;
color: var(--ink-3);
text-transform: lowercase;
}
.chip.hook { background: var(--hazard-soft); border-color: var(--hazard-line); color: var(--hazard-deep); }
.chip.platform { background: rgba(45, 93, 128, 0.10); border-color: rgba(45, 93, 128, 0.30); color: var(--info); }
.chip.cat { background: var(--paper-3); }
.chip.lang { background: var(--paper-2); }
.scard-actions {
margin-top: auto;
display: flex;
gap: 6px;
padding-top: 8px;
border-top: 1px solid var(--rule-soft);
}
.scard-actions .grow { flex: 1; }
.scard-stars {
font-family: var(--ff-mono);
font-size: 11px;
color: var(--ink-3);
}
.scard-stars::before {
content: "★";
color: var(--hazard);
margin-right: 4px;
}
/* ─── Stat grid (curator dashboard) ──────────────────────────────────── */
.stat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
}
.stat-tile {
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 18px 20px;
position: relative;
}
.stat-tile::before {
content: "";
position: absolute;
top: -1px; left: -1px;
width: 36px; height: 2px;
background: var(--hazard);
}
.stat-label {
font-family: var(--ff-mono);
font-size: 9.5px;
font-weight: 600;
letter-spacing: 0.18em;
color: var(--ink-3);
text-transform: uppercase;
}
.stat-value {
font-family: var(--ff-display);
font-weight: 700;
font-size: 44px;
line-height: 1;
letter-spacing: 0.02em;
color: var(--ink);
margin-top: 8px;
}
.stat-sub {
font-family: var(--ff-mono);
font-size: 10px;
color: var(--ink-3);
margin-top: 6px;
letter-spacing: 0.04em;
}
.stat-tile.ok .stat-value { color: var(--ok); }
.stat-tile.warn .stat-value { color: var(--hazard-deep); }
/* ─── Key/value list ─────────────────────────────────────────────────── */
.kv-list {
margin: 0;
display: grid;
grid-template-columns: auto 1fr;
gap: 8px 18px;
font-family: var(--ff-mono);
font-size: 12px;
}
.kv-list dt {
color: var(--ink-3);
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 10px;
padding-top: 2px;
}
.kv-list dd {
margin: 0;
color: var(--ink);
}
/* ─── Empty-pane prompt ──────────────────────────────────────────────── */
.empty-pane {
background: var(--paper-card);
border: 1px dashed var(--rule);
border-radius: var(--radius);
padding: 32px 24px;
text-align: center;
color: var(--ink-3);
font-family: var(--ff-mono);
font-size: 11px;
letter-spacing: 0.06em;
grid-column: 1 / -1;
}
.empty-pane code {
background: var(--paper-2);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--rule-soft);
font-size: 10.5px;
color: var(--ink-2);
}
/* ─── Editor (memory) ────────────────────────────────────────────────── */
.editor {
width: 100%;
min-height: 200px;
max-height: 50vh;
background: var(--paper-card);
color: var(--ink);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 14px 16px;
font-family: var(--ff-mono);
font-size: 12px;
line-height: 1.6;
resize: vertical;
}
.editor:focus { outline: none; border-color: var(--ink); box-shadow: inset 0 -2px 0 var(--hazard); }
/* Squeeze long sidebar — let it scroll & shorten dial padding so 18 items fit */
.dial-item { padding-top: 9px; padding-bottom: 9px; }
.dial-num { font-size: 22px; }
/* ─── Scrollbar ──────────────────────────────────────────────────────── */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background: var(--paper-4);
border-radius: 0;
border: 2px solid transparent;
background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); background-clip: padding-box; }
::selection { background: var(--hazard-soft); color: var(--ink); }
/* ─── Responsive — narrow viewport collapses the nav numerals ────────── */
@media (max-width: 1100px) {
:root { --nav-w: 200px; }
.folio-num { font-size: 56px; }
.folio-title { font-size: 36px; }
.main { padding: 28px 32px 40px; }
}
@media (max-width: 880px) {
:root { --nav-w: 64px; }
.masthead-title { font-size: 22px; }
.masthead-sub, .masthead-meta, .masthead-stamp, .nav-foot { display: none; }
.dial-item { grid-template-columns: 1fr; gap: 0; padding-left: 6px; }
.dial-name { display: none; }
.dial-num { font-size: 20px; }
}
/* ─── API User Table ──────────────────────────────────────────────────── */
.api-user-table {
width: 100%;
border-collapse: collapse;
background: var(--paper-card);
border: 1px solid var(--rule);
border-radius: var(--radius);
overflow: hidden;
margin-top: 22px;
}
.api-user-table th, .api-user-table td {
padding: 9px 14px;
font-size: 12.5px;
text-align: left;
border-bottom: 1px solid var(--rule-soft);
vertical-align: middle;
}
.api-user-table th {
background: var(--paper-2);
color: var(--ink-3);
font-family: var(--ff-mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 10px;
}
.api-user-table tr:last-child td { border-bottom: 0; }
.api-user-table tr:hover td { background: var(--paper-2); }
.api-user-name {
font-weight: 500;
font-size: 13px;
}
.cell-sub {
font-size: 11px;
color: var(--ink-3);
}
.key-mask {
font-family: var(--ff-mono);
font-size: 11.5px;
color: var(--ink-2);
letter-spacing: 0.04em;
}
.action-cell {
white-space: nowrap;
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 6px 14px;
}
.empty-cell {
color: var(--ink-3);
font-style: italic;
text-align: center;
padding: 24px;
}
/* ─── Badges ─────────────────────────────────────────────────────────── */
.badge {
display: inline-block;
padding: 2px 8px;
font-size: 9.5px;
border-radius: 0;
background: transparent;
border: 1px solid var(--rule);
font-family: var(--ff-mono);
letter-spacing: 0.12em;
color: var(--ink-3);
text-transform: uppercase;
}
.badge-active {
border-color: #6ab04c;
color: #6ab04c;
}
.badge-revoked {
border-color: var(--err);
color: var(--err);
}
.badge-deleted {
border-color: var(--ink-3);
color: var(--ink-3);
opacity: 0.6;
}
.badge-pre {
border-color: #7c6fcd;
color: #7c6fcd;
}
.badge-post {
border-color: #0097a7;
color: #0097a7;
}
/* ─── Dialog ─────────────────────────────────────────────────────────── */
dialog {
background: var(--paper-card);
color: var(--ink);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 28px 32px;
min-width: 340px;
max-width: 480px;
width: 100%;
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.55);
}
dialog h2 {
margin: 0 0 20px;
font-family: var(--ff-sans);
font-weight: 300;
font-size: 24px;
letter-spacing: -0.02em;
}
.dialog-field {
display: flex;
flex-direction: column;
gap: 5px;
margin-bottom: 14px;
}
.dialog-field label {
font-family: var(--ff-mono);
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--ink-3);
}
.dialog-field-check {
flex-direction: row;
align-items: center;
}
.dialog-field-check label {
font-family: var(--ff-sans);
font-size: 13px;
text-transform: none;
letter-spacing: 0;
color: var(--ink);
font-weight: 400;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.field-hint {
font-weight: 400;
text-transform: none;
letter-spacing: 0;
color: var(--ink-3);
}
.expires-presets {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-top: 4px;
}
.dialog-error {
font-family: var(--ff-mono);
font-size: 11px;
color: var(--hazard);
padding: 8px 10px;
background: var(--hazard-soft);
border: 1px solid var(--hazard-line);
border-radius: var(--radius);
margin-bottom: 4px;
display: none;
}
.dialog-error:not(:empty) {
display: block;
}
.dialog-actions {
display: flex;
gap: 8px;
margin-top: 20px;
justify-content: flex-end;
}
.reveal-body {
display: flex;
flex-direction: column;
gap: 12px;
}
.reveal-warning {
font-family: var(--ff-mono);
font-size: 11px;
color: var(--hazard);
text-transform: uppercase;
letter-spacing: 0.1em;
margin: 0;
}
.reveal-key-wrap {
background: var(--paper-2);
border: 1px solid var(--rule);
border-radius: var(--radius);
padding: 12px 16px;
overflow-x: auto;
}
.reveal-key-wrap code {
font-family: var(--ff-mono);
font-size: 13px;
word-break: break-all;
color: var(--ink);
}
/* ─── Responsive — narrow viewport stacks API table ─────────────────── */
@media (max-width: 600px) {
.api-user-table, .api-user-table thead, .api-user-table tbody,
.api-user-table th, .api-user-table td, .api-user-table tr {
display: block;
}
.api-user-table thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
.api-user-table tr {
border: 1px solid var(--rule);
border-radius: var(--radius);
margin-bottom: 10px;
background: var(--paper-card);
}
.api-user-table td {
border-bottom: 1px solid var(--rule-soft);
position: relative;
padding-left: 50%;
}
.api-user-table td::before {
position: absolute;
top: 9px;
left: 14px;
width: calc(50% - 20px);
white-space: nowrap;
font-family: var(--ff-mono);
font-size: 9.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--ink-3);
content: attr(data-label);
}
.action-cell {
padding-left: 14px;
}
}