189 lines
6.3 KiB
CSS
189 lines
6.3 KiB
CSS
/* The local UI's whole stylesheet. Plain, literal, no framework — the same
|
|
voice as the product's copy. Served from the binary, never from a CDN. */
|
|
|
|
:root {
|
|
--bg: #16181d;
|
|
--panel: #1e2128;
|
|
--line: #2c313b;
|
|
--ink: #e6e8ec;
|
|
--dim: #9aa1ad;
|
|
--ok: #4ea96b;
|
|
--warn: #d79a3c;
|
|
--bad: #d1584f;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
margin: 0;
|
|
padding: 2rem 1.5rem 4rem;
|
|
background: var(--bg);
|
|
color: var(--ink);
|
|
font: 15px/1.55 -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
}
|
|
|
|
main, header, footer { max-width: 46rem; margin: 0 auto; }
|
|
|
|
header { margin-bottom: 1.5rem; }
|
|
h1 { margin: 0; font-size: 1.35rem; font-weight: 600; letter-spacing: -0.01em; }
|
|
.sub { margin: 0.15rem 0 0; color: var(--dim); font-size: 0.85rem; }
|
|
|
|
.state {
|
|
padding: 1rem 1.1rem;
|
|
border: 1px solid var(--line);
|
|
border-left-width: 3px;
|
|
border-radius: 6px;
|
|
background: var(--panel);
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
.state.ok { border-left-color: var(--ok); }
|
|
.state.warn { border-left-color: var(--warn); }
|
|
.state.bad { border-left-color: var(--bad); }
|
|
.state h2 { margin: 0; font-size: 1rem; font-weight: 600; }
|
|
.state p { margin: 0.45rem 0 0; color: var(--dim); font-size: 0.87rem; }
|
|
|
|
/* ── waiting for you ───────────────────────────────────────────────────────
|
|
The only thing on this page that is time-critical, so it is the only thing
|
|
allowed to shout. */
|
|
|
|
h3.urgent { color: var(--warn); font-size: 0.85rem; margin-top: 0; }
|
|
|
|
.flash {
|
|
padding: 0.7rem 1rem;
|
|
border-radius: 6px;
|
|
margin-bottom: 1rem;
|
|
font-size: 0.88rem;
|
|
border: 1px solid var(--line);
|
|
}
|
|
.flash.ok { border-color: var(--ok); color: var(--ok); }
|
|
.flash.warn { border-color: var(--warn); color: var(--warn); }
|
|
.flash.bad { border-color: var(--bad); color: var(--bad); }
|
|
|
|
.approval {
|
|
border: 1px solid var(--warn);
|
|
border-radius: 6px;
|
|
background: var(--panel);
|
|
padding: 1rem 1.1rem;
|
|
margin-bottom: 0.8rem;
|
|
}
|
|
.approval-head { font-weight: 600; }
|
|
.approval-head .id {
|
|
color: var(--dim); font-weight: 400;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: 0.8rem;
|
|
}
|
|
.approval-line { margin: 0.45rem 0 0; font-size: 0.88rem; white-space: pre-wrap; }
|
|
.approval.unreadable { border-color: var(--bad); }
|
|
.approval.unreadable .approval-line strong { color: var(--bad); }
|
|
|
|
.approval-acts { display: flex; gap: 0.6rem; margin-top: 0.9rem; flex-wrap: wrap; }
|
|
/* Deny is written first in the markup so it leads for keyboard and screen
|
|
readers; on a describable request the visual order goes back to Approve
|
|
first, where it belongs. */
|
|
.approval-acts:not(.reversed) { flex-direction: row-reverse; justify-content: flex-end; }
|
|
.approval-acts form { display: flex; gap: 0.4rem; margin: 0; }
|
|
.approval-foot { margin: 0.7rem 0 0; color: var(--dim); font-size: 0.78rem; }
|
|
|
|
input.code {
|
|
width: 9rem;
|
|
padding: 0.45rem 0.6rem;
|
|
border: 1px solid var(--line);
|
|
border-radius: 5px;
|
|
background: var(--bg);
|
|
color: var(--ink);
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: 0.95rem;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
input.code:focus { outline: 2px solid var(--warn); outline-offset: 1px; }
|
|
|
|
button {
|
|
padding: 0.45rem 1rem;
|
|
border-radius: 5px;
|
|
border: 1px solid var(--line);
|
|
background: var(--line);
|
|
color: var(--ink);
|
|
font: inherit;
|
|
font-size: 0.88rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
button.go { background: var(--ok); border-color: var(--ok); color: #0d1410; }
|
|
button.no { background: transparent; color: var(--dim); }
|
|
button:hover { filter: brightness(1.12); }
|
|
|
|
/* ── what has happened here ────────────────────────────────────────────────
|
|
The same card as .host, deliberately NOT the .approval treatment: an approval
|
|
is a question the page is holding open, and these are answers to questions
|
|
nobody asked. A recent one — or a live refusal — takes the bad border,
|
|
because the host killing your programs is the one thing on this page you did
|
|
not do and were never told about. The border is the whole difference; an
|
|
event is never hidden once it stops being recent, it only stops shouting. */
|
|
|
|
.event {
|
|
border: 1px solid var(--line);
|
|
border-radius: 6px;
|
|
background: var(--panel);
|
|
padding: 0.85rem 1.1rem;
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
.event.recent, .event.refused { border-color: var(--bad); }
|
|
.event-when { color: var(--dim); font-size: 0.78rem; }
|
|
.event-line { margin: 0.35rem 0 0; font-size: 0.88rem; }
|
|
.event-foot { margin: 0.65rem 0 0; color: var(--dim); font-size: 0.78rem; }
|
|
|
|
.host {
|
|
border: 1px solid var(--line);
|
|
border-radius: 6px;
|
|
background: var(--panel);
|
|
padding: 0.85rem 1.1rem;
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
.host-name { font-weight: 600; word-break: break-all; }
|
|
.host-facts {
|
|
margin: 0.5rem 0 0;
|
|
display: grid;
|
|
grid-template-columns: 8.5rem 1fr;
|
|
gap: 0.2rem 0.75rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
.host-facts dt { color: var(--dim); }
|
|
.host-facts dd { margin: 0; word-break: break-all; }
|
|
|
|
.tag {
|
|
display: inline-block;
|
|
margin-left: 0.5rem;
|
|
padding: 0.05rem 0.45rem;
|
|
border-radius: 999px;
|
|
font-size: 0.72rem;
|
|
font-weight: 600;
|
|
vertical-align: 0.08em;
|
|
}
|
|
.tag.ok { background: rgba(78,169,107,0.16); color: var(--ok); }
|
|
.tag.warn { background: rgba(215,154,60,0.16); color: var(--warn); }
|
|
.tag.bad { background: rgba(209,88,79,0.16); color: var(--bad); }
|
|
|
|
h3 { margin: 1.6rem 0 0.6rem; font-size: 0.8rem; font-weight: 600;
|
|
color: var(--dim); text-transform: none; letter-spacing: 0; }
|
|
|
|
/* Was an inline style="margin-top:1rem" until review pointed out the page's own
|
|
CSP (`default-src 'self'`, ui.go) covers style-src and therefore blocks it —
|
|
the policy was silently winning against the markup. A class costs nothing and
|
|
keeps the CSP with no 'unsafe-inline' escape hatch in it. */
|
|
.checked-at { margin-top: 1rem; }
|
|
|
|
footer { margin-top: 2.5rem; color: var(--dim); font-size: 0.8rem; }
|
|
footer a { color: var(--dim); }
|
|
|
|
.note {
|
|
margin-top: 0.6rem;
|
|
padding: 0.7rem 0.9rem;
|
|
border: 1px dashed var(--line);
|
|
border-radius: 6px;
|
|
color: var(--dim);
|
|
font-size: 0.84rem;
|
|
}
|
|
|
|
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: 0.85em; color: var(--ink); }
|