stage-11: cyber pass + per-page help

Cyber feel, kept restrained: a faint cyan grid on the app background, HUD
corner-brackets on every panel, a soft glow behind page titles. Each cockpit
view gains a collapsible help block — how to use it, what you're seeing, and
why it matters — alongside the one-line purpose intro.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
m17hr1l
2026-05-18 23:21:52 +02:00
parent 434b072698
commit ce375211f3
6 changed files with 76 additions and 3 deletions

View File

@@ -17,7 +17,12 @@
body {
margin: 0;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
background: var(--bg);
background-color: var(--bg);
/* faint cyber grid */
background-image:
linear-gradient(rgba(30, 200, 255, 0.035) 1px, transparent 1px),
linear-gradient(90deg, rgba(30, 200, 255, 0.035) 1px, transparent 1px);
background-size: 44px 44px;
color: var(--text);
font-size: 14px;
line-height: 1.45;
@@ -31,7 +36,27 @@ h1, h2, h3,
.brand-sub, .nav a, .family-tag, .family-hub, .bot-name { font-family: var(--font-display); }
/* one-line purpose statement under a page title */
.page-intro { color: var(--muted); font-size: 13px; line-height: 1.55; margin: 4px 0 18px; max-width: 74ch; }
.page-intro { color: var(--muted); font-size: 13px; line-height: 1.55; margin: 4px 0 10px; max-width: 74ch; }
/* collapsible per-page help — how to use it, what you're seeing, why it matters */
.page-help { margin: 0 0 18px; max-width: 80ch; }
.page-help summary {
cursor: pointer; color: var(--accent); font-size: 12px;
font-family: var(--font-display); letter-spacing: 0.5px;
list-style: none; user-select: none; display: inline-block;
}
.page-help summary::-webkit-details-marker { display: none; }
.page-help summary::before { content: "▸ "; }
.page-help[open] summary::before { content: "▾ "; }
.page-help summary:hover { text-shadow: 0 0 10px var(--accent-glow); }
.help-body {
margin-top: 10px; padding: 12px 14px;
background: var(--panel-2); border: 1px solid var(--border);
border-left: 2px solid var(--accent); border-radius: 4px;
}
.help-body p { margin: 0 0 8px; font-size: 13px; line-height: 1.6; color: var(--muted); }
.help-body p:last-child { margin-bottom: 0; }
.help-body b { color: var(--accent); font-family: var(--font-display); font-weight: 600; }
.topbar {
display: flex; justify-content: space-between; align-items: center;
@@ -72,13 +97,21 @@ h1, h2, h3,
.footer { text-align: center; color: var(--muted); padding: 24px; font-size: 12px; }
.panel {
position: relative;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
padding: 20px 24px;
}
/* HUD corner brackets — targeting-frame cyber look */
.panel::before, .panel::after {
content: ""; position: absolute; width: 15px; height: 15px;
border: 2px solid var(--accent); pointer-events: none;
}
.panel::before { top: -1px; left: -1px; border-width: 2px 0 0 2px; border-radius: 6px 0 0 0; }
.panel::after { bottom: -1px; right: -1px; border-width: 0 2px 2px 0; border-radius: 0 0 6px 0; }
.panel-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 16px; }
.panel-head h1 { margin: 0; font-size: 18px; }
.panel-head h1 { margin: 0; font-size: 18px; text-shadow: 0 0 18px var(--accent-glow); }
.count { color: var(--muted); font-size: 12px; }
.empty { color: var(--muted); }
.back { color: var(--muted); font-size: 12px; }

View File

@@ -9,6 +9,14 @@
</div>
<p class="summary-lead">{{ case.summary }}</p>
<p class="page-intro">The full record for one case — how Classifyline rated it, what Scoutline observed, the evidence Sealine encrypted, where Routeline may send it, and every ledger entry it produced.</p>
<details class="page-help">
<summary>how to use this view</summary>
<div class="help-body">
<p><b>How to use.</b> Read the cards top-to-bottom — classification, observables, sealed package, routes, ledger. Hit <b>▶ case journey</b> for the animated walk-through.</p>
<p><b>What you're seeing.</b> Every worker line's output for this one case: how it was rated, what was observed, what was encrypted and to whom, where it would route, and the audit rows it produced.</p>
<p><b>Why it matters.</b> Nothing sensitive leaves psyc without a human seeing the full reasoning chain — this page is that chain, for one case.</p>
</div>
</details>
<div class="grid">
<div class="card">

View File

@@ -7,6 +7,14 @@
<span class="count">{{ total }} case{{ '' if total == 1 else 's' }}</span>
</div>
<p class="page-intro">Every threat case psyc is tracking — ingested from URLhaus, CISA KEV, and Feodo Tracker, then classified by severity and TLP. The live queue of what the platform currently knows about; click any case to follow it through the pipeline.</p>
<details class="page-help">
<summary>how to use this view</summary>
<div class="help-body">
<p><b>How to use.</b> Scan the severity and TLP badges to triage; click any case ID to open its full record and Worker Mesh journey. Run <code>psyc fetch-all</code> to pull fresh cases from the feeds.</p>
<p><b>What you're seeing.</b> Each row is one normalized Case object — ingested by Scoutline from URLhaus, CISA KEV, or Feodo Tracker, then rated by Classifyline.</p>
<p><b>Why it matters.</b> A defender needs one place that answers "what is happening right now" before deciding what to act on — this queue is that place.</p>
</div>
</details>
{% if not cases %}
<p class="empty">No cases yet. Run <code>psyc fetch urlhaus</code> to ingest.</p>
{% else %}

View File

@@ -40,6 +40,14 @@
</div>
<p class="summary-lead">{{ case.summary }}</p>
<p class="page-intro">Watch the case move through psyc's worker mesh — seven bots, each performing one pipeline stage and reporting its real result. The Classifier bot's severity verdict comes from the live fine-tuned model; hit ▶ replay to run it again.</p>
<details class="page-help">
<summary>how to use this view</summary>
<div class="help-body">
<p><b>How to use.</b> Let the case token travel the mesh — each bot wakes, performs its stage, and reports. Press <b>▶ replay</b> to run it again.</p>
<p><b>What you're seeing.</b> Seven worker bots, one per pipeline stage. Awake bots ran for this case; asleep bots are stages that didn't apply. The Classifier bot's verdict is generated live by the fine-tuned model.</p>
<p><b>Why it matters.</b> It makes the chain of reasoning legible — you can watch what the platform did and why, not just trust a final answer.</p>
</div>
</details>
<ol class="mesh playing" id="mesh">
<span class="case-token" aria-hidden="true"></span>

View File

@@ -7,6 +7,14 @@
<span class="count">{{ total }} entr{{ 'y' if total == 1 else 'ies' }}</span>
</div>
<p class="page-intro">The append-only audit trail. Every external submission and every policy-blocked route is recorded here — nothing leaves psyc without a row. This is the platform's proof of exactly what happened, when, and to whom.</p>
<details class="page-help">
<summary>how to use this view</summary>
<div class="help-body">
<p><b>How to use.</b> Read top-down, newest first. Each row is one external submission or one policy-blocked route. Open a case to see only its entries.</p>
<p><b>What you're seeing.</b> timestamp · case · destination · payload hash · submitter · TLP · outcome — written by Ledgerline the moment Courier acts.</p>
<p><b>Why it matters.</b> Defensive operations must be provable. The ledger is append-only — psyc can show exactly what it sent, where, and what came back.</p>
</div>
</details>
{% if not entries %}
<p class="empty">No ledger entries yet. Run <code>psyc submit &lt;case_id&gt;</code> or <code>psyc demo</code>.</p>
{% else %}

View File

@@ -7,6 +7,14 @@
<span class="count">{{ datasets|length }} dataset{{ '' if datasets|length == 1 else 's' }}</span>
</div>
<p class="page-intro">Trainline turns reviewed cases into LoRA training data and tracks the fine-tuned adapters built from it — the JSONL datasets below, and the trained adapters with their loss curves further down. The adapter behind the Classifier bot is trained here.</p>
<details class="page-help">
<summary>how to use this view</summary>
<div class="help-body">
<p><b>How to use.</b> <code>psyc train-build-all</code> builds the datasets below; the <code>Dockerfile.train</code> workflow fine-tunes an adapter; both then appear here.</p>
<p><b>What you're seeing.</b> JSONL datasets with example counts, and trained QLoRA adapters with their base model, hyperparameters, and per-step loss curve.</p>
<p><b>Why it matters.</b> The adapter behind the Classifier bot is built here — this page is the provenance of the model that's actually in operation.</p>
</div>
</details>
{% if not datasets %}
<p class="empty">No datasets yet. Run <code>psyc train-build-all</code>.</p>
{% else %}