From 5d196efe866f4120af759b58c2f993da63d54d3b Mon Sep 17 00:00:00 2001 From: ostefani Date: Mon, 9 Nov 2026 07:16:56 -0800 Subject: [PATCH 01/12] core: add UI path aliases. --- docs/index.html | 29 +++++++++++++++++++++++------ docs/landing.css | 16 +++++++++++----- ui/jsconfig.json | 17 +++++++++++++++++ ui/vite.config.js | 15 +++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 ui/jsconfig.json diff --git a/docs/index.html b/docs/index.html index fa562ab..1731303 100644 --- a/docs/index.html +++ b/docs/index.html @@ -97,7 +97,12 @@

Follow a report.

Storage
Local SQLite
Evidence
Original signed report
Identity
Scanner-reported
-

03 / Optional AI

Explain the change.

Local rules set the priority floor. If configured, AI can explain the change using redacted context and may suggest a one-level increase up to high. A final leak check guards provider requests. If the model fails, the bridge stores a marked fallback.

+
+

03 / Optional AI

+

Explain the change.

+

Local rules set the priority floor. If configured, AI can explain the change using redacted context and may suggest a one-level increase up to high. A final leak check guards provider requests. If the model fails, the bridge stores a marked fallback.

+

When enabled, the AI investigator can use read-only tools to look up retained scanner reports about host history, services, and similar changes. The briefing can cite supporting evidence and show where lookups were incomplete.

+
Example finding
Scanner reported a host appearance
Suggested check
Confirm against your asset inventory
Model context
Redacted by default
@@ -112,8 +117,8 @@

Follow a report.

02 / Review workflow

-

From one report
to the bigger picture.

-

The local dashboard brings together scanner evidence, priority reasoning, and operator decisions.

+

From report to decision.

+

Review what the scanner reported, see why it was prioritized, and record your decision in the local dashboard.

@@ -121,9 +126,21 @@

From one report
to the bigger picture.

Briefing detail / Synthetic demonstration data
-
01

Review related changes together

Qualifying appearance or disappearance briefings from the same source and target can form one incident with links to its members.

-
02

Turn repeat decisions into policy

By default, three matching, eligible “expected” decisions can produce a rule proposal for operator approval.

-
03

See where coverage is uncertain

The overview pairs an Act, Review, or Quiet verdict with per-source coverage. Liveness stays unknown until heartbeat capability is established.

+
+ +

Review related changes

+

WatcherBridge groups qualifying appearance or disappearance reports into incidents. Optional AI summaries for closed incidents show how many briefings were sampled or omitted, label hypotheses, and can cite member briefings.

+
+
+ +

Approve review rules

+

By default, three matching, eligible “expected” decisions can trigger a rule proposal. An operator decides whether to approve it.

+
+
+ +

Check scanner coverage

+

See the Act, Review, or Quiet verdict beside each source’s coverage. A source’s liveness stays unknown until it sends a heartbeat.

+
diff --git a/docs/landing.css b/docs/landing.css index de5dd5a..e92c302 100644 --- a/docs/landing.css +++ b/docs/landing.css @@ -70,8 +70,8 @@ nav a:hover, .text-link:hover { text-decoration: underline; text-underline-offse .architecture, .review { padding-block: clamp(54px, 8vw, 102px); } .architecture { background: #e6ede1; border-block: 1px solid var(--line); } -.section-heading { display: grid; grid-template-columns: 1.18fr 1fr; align-items: end; gap: 18px 40px; margin-block: 13px 32px; } -.section-heading > p { max-width: 41ch; color: var(--muted); font-size: 1.06rem; } +.section-heading { margin-block: 13px 32px; } +.section-heading > p { max-width: 60ch; margin-top: 12px; color: var(--muted); font-size: 1.06rem; } .lab { background: var(--dark); color: #edf7ee; padding: clamp(16px, 3vw, 36px); border-radius: 18px; } .lab-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 20px; } .lab .indicator { background: var(--lime); } @@ -96,11 +96,13 @@ nav a:hover, .text-link:hover { text-decoration: underline; text-underline-offse .readout dd { color: var(--lime); overflow-wrap: anywhere; } @keyframes reveal { from { opacity: .35; transform: translateY(5px); } to { opacity: 1; transform: none; } } -.review-grid { display: grid; grid-template-columns: 1.65fr 1fr; align-items: start; gap: clamp(24px, 4vw, 56px); } +.review-grid { display: grid; gap: clamp(28px, 4vw, 48px); } .product-shot img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 14px 32px #14282214; } .product-shot figcaption { margin-top: 12px; color: var(--muted); font: .72rem/1.5 var(--mono); } -.features article { display: grid; grid-template-columns: 30px 1fr; gap: 10px; padding-block: 19px; border-top: 1px solid var(--line); } +.features { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(16px, 2.5vw, 32px); } +.features article { padding-top: 16px; border-top: 1px solid var(--line); } .features article > span { color: #32654f; font: 700 .8rem/1.5 var(--mono); } +.features h3 { margin-top: 8px; line-height: 1.15; } .features p { margin-top: 8px; color: var(--muted); } .closing { padding-block: 54px; background: var(--dark); color: #edf7ee; } .closing-inner { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 24px; } @@ -110,12 +112,16 @@ nav a:hover, .text-link:hover { text-decoration: underline; text-underline-offse .site-footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; padding-block: 22px; color: var(--muted); font-size: .82rem; } @media (max-width: 820px) { - .hero, .section-heading, .review-grid { grid-template-columns: 1fr; } + .hero { grid-template-columns: 1fr; } .hero h1 { font-size: clamp(3.4rem, 10vw, 6rem); } .signal { width: min(100%, 590px); } .stage { grid-template-columns: 1fr; } } +@media (max-width: 720px) { + .features { grid-template-columns: 1fr; } +} + @media (max-width: 520px) { .wrap { width: calc(100% - 32px); } .site-header { align-items: flex-start; flex-direction: column; padding-block: 14px; gap: 12px; } diff --git a/ui/jsconfig.json b/ui/jsconfig.json new file mode 100644 index 0000000..edf766a --- /dev/null +++ b/ui/jsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "checkJs": false, + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ESNext", + "paths": { + "$lib/*": ["src/lib/*"], + "$components/*": ["src/components/*"], + "$containers/*": ["src/containers/*"], + "$api/*": ["src/api/*"], + "$styles/*": ["src/styles/*"] + } + }, + "include": ["src", "scripts", "vite.config.js", "eslint.config.js"] +} diff --git a/ui/vite.config.js b/ui/vite.config.js index 99434f2..d26b354 100644 --- a/ui/vite.config.js +++ b/ui/vite.config.js @@ -1,5 +1,11 @@ import { defineConfig } from 'vite'; import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { fileURLToPath } from 'node:url'; + +// Phase 0 folder split: stable import roots so components, containers, +// lib modules and the api folder land without rewriting specifiers. +// The target folders arrive in Phases 2-5; no source imports them yet. +const src = fileURLToPath(new URL('./src/', import.meta.url)); // Step 22 toolchain (Designv9 §8.1): JavaScript only, CSP-safe output. // Separate JS/CSS assets, no sourcemaps, no inlined data URIs, no @@ -41,6 +47,15 @@ export default defineConfig({ } } }, + resolve: { + alias: { + $lib: `${src}lib`, + $components: `${src}components`, + $containers: `${src}containers`, + $api: `${src}api`, + $styles: `${src}styles` + } + }, server: { port: 5173, strictPort: true, From 49e46b38499aa976557c36aa3189eb156b7e483f Mon Sep 17 00:00:00 2001 From: ostefani Date: Mon, 9 Nov 2026 07:29:39 -0800 Subject: [PATCH 02/12] core: split viewer CSS into tokens, base, utilities. --- internal/viewer/web/dist/assets/index.css | 2 +- internal/viewer/web/dist/assets/index.js | 2 +- ui/src/lib/Detail.svelte | 2 +- ui/src/lib/ReportView.svelte | 2 +- ui/src/main.js | 4 +- ui/src/{style.css => styles/base.css} | 64 +---------------------- ui/src/styles/tokens.css | 52 ++++++++++++++++++ ui/src/styles/utilities.css | 14 +++++ 8 files changed, 75 insertions(+), 67 deletions(-) rename ui/src/{style.css => styles/base.css} (90%) create mode 100644 ui/src/styles/tokens.css create mode 100644 ui/src/styles/utilities.css diff --git a/internal/viewer/web/dist/assets/index.css b/internal/viewer/web/dist/assets/index.css index 141c84a..400e9f3 100644 --- a/internal/viewer/web/dist/assets/index.css +++ b/internal/viewer/web/dist/assets/index.css @@ -1 +1 @@ -.decision-card.svelte-38cs4v{background:var(--surface);border:1px solid var(--border);overflow-wrap:anywhere;border-radius:16px;padding:26px}.decision-card.svelte-38cs4v h3:where(.svelte-38cs4v){letter-spacing:-.4px;margin-bottom:10px;font-size:20px}.decision-status.svelte-38cs4v{margin-bottom:20px}.decision-card.svelte-38cs4v .decision-status:where(.svelte-38cs4v) .pill:where(.svelte-38cs4v){padding:5px 11px;font-size:12px}.decision-card.svelte-38cs4v [data-review=unreviewed]:where(.svelte-38cs4v),.decision-card.svelte-38cs4v [data-review=snooze]:where(.svelte-38cs4v){color:var(--warning);border-color:var(--warning-border);background:var(--warning-bg)}.decision-card.svelte-38cs4v [data-review=expected]:where(.svelte-38cs4v){color:var(--success);border-color:var(--success-border);background:var(--success-bg)}.decision-card.svelte-38cs4v [data-review=not_expected]:where(.svelte-38cs4v){color:var(--danger);border-color:var(--danger-border);background:var(--danger-bg)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v){border:1px solid var(--border);border-radius:11px;gap:0;min-width:0;margin:0 0 12px;padding:6px 0;display:grid}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label:where(.svelte-38cs4v){cursor:pointer;grid-template-columns:20px minmax(0,1fr);align-items:center;gap:2px 12px;min-height:66px;margin:0;padding:12px 16px;display:grid}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label:where(.svelte-38cs4v):hover{background:var(--subtle)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label.selected:where(.svelte-38cs4v){background:var(--accent-soft)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) input:where(.svelte-38cs4v){width:18px;height:18px;accent-color:var(--accent);grid-row:1/span 2;margin:0;padding:0}.decision-label.svelte-38cs4v{font-weight:600}.decision-hint.svelte-38cs4v{color:var(--muted);grid-column:2;font-size:12px}.decision-card.svelte-38cs4v button[type=submit]:where(.svelte-38cs4v){width:100%;color:var(--surface);background:var(--text);border-color:var(--text);margin-top:8px;font-weight:700}.decision-card.svelte-38cs4v button[type=submit]:where(.svelte-38cs4v):disabled{opacity:.45}.decision-card.svelte-38cs4v .history-heading:where(.svelte-38cs4v){border-top:1px solid var(--border);text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin:24px 0 12px;padding-top:20px;font-size:11px}.empty-history.svelte-38cs4v{color:var(--muted);margin-bottom:0;font-size:13px}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v){padding-left:0;list-style:none}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) li:where(.svelte-38cs4v){flex-wrap:wrap;align-items:center;gap:8px;display:flex}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) time:where(.svelte-38cs4v){color:var(--muted);font-size:11px}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) .text:where(.svelte-38cs4v){flex-basis:100%}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) button:where(.svelte-38cs4v){min-height:32px;padding:4px 8px;font-size:12px}.wb-clipboard-buffer{opacity:0;pointer-events:none;position:fixed}.copy-btn.svelte-ba0p1p{min-height:32px;color:var(--accent);background:var(--accent-soft);padding:4px 10px;font-size:12px}.report-card.svelte-43cneo{border:1px solid var(--border);background:var(--surface);overflow-wrap:anywhere;border-radius:11px;margin:12px 0 0;padding:0 16px}summary.svelte-43cneo{padding:16px 0}.report-heading.svelte-43cneo{vertical-align:top;flex-wrap:wrap;align-items:center;gap:8px;max-width:100%;display:inline-flex}.report-id.svelte-43cneo{font-weight:600}.report-time.svelte-43cneo{color:var(--muted);margin:7px 0 0 16px;font-size:12px;font-weight:400;display:block}.report-body.svelte-43cneo{border-top:1px solid var(--border);padding:16px 0 6px}.report-view.svelte-43cneo{border-top:1px solid var(--border);margin:16px 0;padding:16px 0 0}.report-view.svelte-43cneo h4:where(.svelte-43cneo){font-size:14px}.report-view.svelte-43cneo .report-identity:where(.svelte-43cneo){grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr));gap:14px;margin:0 0 16px;display:grid}.report-identity.svelte-43cneo dt:where(.svelte-43cneo){text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px;font-size:11px}.report-identity.svelte-43cneo dd:where(.svelte-43cneo){margin:0}.confidence-note.svelte-43cneo{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.attributes.svelte-43cneo{border:0;padding:0}.attributes.svelte-43cneo summary:where(.svelte-43cneo){color:var(--accent);font-size:12px}.profile-chip.svelte-43cneo{border:1px solid var(--border);background:var(--accent-soft);border-radius:9px;flex-wrap:wrap;gap:5px 12px;padding:10px 12px;font-size:13px;text-decoration:none;display:flex}.mono.svelte-43cneo{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}li.svelte-6r3uui{border-top:1px solid var(--border);padding:12px 0;list-style:none}.tool-name.svelte-6r3uui{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:600}p.svelte-6r3uui{margin:8px 0}.dossier.svelte-90l7ri{max-width:1400px;margin:0 auto;container-type:inline-size}.dossier-back.svelte-90l7ri{margin:0 0 20px}.dossier-back.svelte-90l7ri a:where(.svelte-90l7ri){text-decoration:none}.dossier-layout.svelte-90l7ri{grid-template-columns:minmax(0,1fr) minmax(300px,.48fr);align-items:start;gap:20px 24px;display:grid}.dossier-layout.svelte-90l7ri>:where(.svelte-90l7ri):not(.dossier-side){grid-column:1;min-width:0}.dossier-card.svelte-90l7ri{border:1px solid var(--border);background:var(--surface);overflow-wrap:anywhere;border-radius:16px;margin:0;padding:26px}.dossier-side.svelte-90l7ri{border-radius:16px;grid-area:1/2/span 7;max-height:calc(100dvh - 40px);position:sticky;top:20px;overflow:auto}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri){flex-wrap:wrap;align-items:flex-start;margin-bottom:16px}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri) h2:where(.svelte-90l7ri){letter-spacing:-.7px;flex:260px;font-size:clamp(23px,2.1vw,30px);line-height:1.22}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri) .priority:where(.svelte-90l7ri){text-transform:uppercase;letter-spacing:.06em;padding:5px 12px;font-weight:700}.dossier.svelte-90l7ri .briefing-description:where(.svelte-90l7ri){margin-bottom:24px;font-size:16px;line-height:1.65}.dossier.svelte-90l7ri .identity-grid:where(.svelte-90l7ri){border-top:1px solid var(--border);grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 28px;margin:0;padding-top:22px;display:grid}.identity-grid.svelte-90l7ri>div:where(.svelte-90l7ri){min-width:0}.identity-grid.svelte-90l7ri dt:where(.svelte-90l7ri){text-transform:uppercase;letter-spacing:.08em;margin-bottom:7px;font-size:11px}.identity-grid.svelte-90l7ri dd:where(.svelte-90l7ri){flex-wrap:wrap;align-items:center;gap:6px 10px;margin:0;font-size:15px;font-weight:600;display:flex}.mono.svelte-90l7ri{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.identity-group.svelte-90l7ri{grid-column:1/-1}.identity-group.svelte-90l7ri p:where(.svelte-90l7ri){margin:5px 0 0}.evidence-index.svelte-90l7ri{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:4px 12px;margin-top:24px;padding-top:14px;font-size:13px;display:flex}.snapshot-note.svelte-90l7ri{border:1px solid var(--accent);color:var(--accent);background:var(--accent-soft);border-radius:12px;gap:12px;padding:15px 18px;display:flex}.snapshot-note.svelte-90l7ri p:where(.svelte-90l7ri){margin:0}.info-mark.svelte-90l7ri{border:1.5px solid;border-radius:50%;flex:none;place-items:center;width:19px;height:19px;margin-top:2px;font-size:12px;font-weight:700;display:inline-grid}.dossier.svelte-90l7ri h3:where(.svelte-90l7ri){letter-spacing:-.3px;margin-bottom:12px;font-size:18px}.section-heading.svelte-90l7ri{align-items:flex-start;gap:12px;display:flex}.section-heading.svelte-90l7ri h3:where(.svelte-90l7ri){margin-bottom:5px}.section-heading.svelte-90l7ri .section-note:where(.svelte-90l7ri){margin:0}.spread.svelte-90l7ri{flex-wrap:wrap;justify-content:space-between;margin-bottom:12px}.section-icon.svelte-90l7ri{background:var(--accent-soft);width:34px;height:34px;color:var(--accent);border-radius:9px;flex:none;place-items:center;font-size:23px;display:grid}.recommended-action.svelte-90l7ri{border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:9px;margin:18px 0;padding:16px;font-size:15px;line-height:1.7}.copy-row.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.copy-chip.svelte-90l7ri{border:1px solid var(--border);background:var(--subtle);overflow-wrap:anywhere;border-radius:8px;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding:5px 8px 5px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;display:inline-flex}.priority-rule.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.priority-rule.svelte-90l7ri code:where(.svelte-90l7ri){background:var(--subtle);border:1px solid var(--border);border-radius:6px;padding:4px 8px}.priority-comparison.svelte-90l7ri,.context-links.svelte-90l7ri{border:0;border-top:1px solid var(--border);border-radius:0;margin:16px 0 0;padding:0}.priority-comparison.svelte-90l7ri summary:where(.svelte-90l7ri),.context-links.svelte-90l7ri summary:where(.svelte-90l7ri){color:var(--accent);font-size:13px}.hypothesis.svelte-90l7ri,.context-text.svelte-90l7ri{line-height:1.7}.context-limit.svelte-90l7ri{border:1px solid var(--warning-border);background:var(--warning-bg);color:var(--warning);border-radius:9px;padding:12px 14px;font-size:13px}.lookup-receipts.svelte-90l7ri{margin:0;padding:0;list-style:none}.lookup-receipts.svelte-90l7ri li:where(.svelte-90l7ri){border-top:1px solid var(--border);align-items:flex-start;gap:12px;padding:12px 0;display:flex}.receipt-steps.svelte-90l7ri{flex-wrap:wrap;gap:4px 16px;display:flex}.lookup-receipts.svelte-90l7ri p:where(.svelte-90l7ri){margin:0;font-size:13px}.receipt-mark.svelte-90l7ri{color:var(--muted);font-weight:700}.text-link.svelte-90l7ri{color:var(--accent);text-align:left;background:0 0;border:0;min-height:36px;padding:6px 0;font-size:13px}.text-link.svelte-90l7ri:hover{background:0 0;text-decoration:underline}.citation-row.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:8px;margin-top:16px;display:flex}.citation.svelte-90l7ri{color:var(--accent);background:var(--accent-soft);min-height:32px;padding:4px 9px;font-size:12px}.tech-block.svelte-90l7ri{padding:0 24px}.tech-block.svelte-90l7ri>summary:where(.svelte-90l7ri){padding:20px 0;font-size:15px}.tech-block.svelte-90l7ri>summary:where(.svelte-90l7ri) span:where(.svelte-90l7ri){margin-left:10px;font-weight:400}.tech-body.svelte-90l7ri{border-top:1px solid var(--border);padding:18px 0 8px}.trace-list.svelte-90l7ri{margin:0 0 20px;padding:0}.embedded.svelte-90l7ri .dossier-layout:where(.svelte-90l7ri){flex-direction:column;gap:16px;display:flex}.embedded.svelte-90l7ri .dossier-layout:where(.svelte-90l7ri)>:where(.svelte-90l7ri){width:100%}.embedded.svelte-90l7ri .dossier-side:where(.svelte-90l7ri){order:1;max-height:none;position:static;overflow:visible}.embedded.svelte-90l7ri .recommended-card:where(.svelte-90l7ri){order:0}.embedded.svelte-90l7ri .dossier-card:where(.svelte-90l7ri):not(.briefing-summary,.recommended-card),.embedded.svelte-90l7ri .tech-block:where(.svelte-90l7ri){order:2}.embedded.svelte-90l7ri .dossier-card:where(.svelte-90l7ri){padding:20px}@container (width<=840px){.dossier-layout.svelte-90l7ri{flex-direction:column;display:flex}.dossier-layout.svelte-90l7ri>:where(.svelte-90l7ri){width:100%}.dossier-side.svelte-90l7ri{order:1;max-height:none;position:static;overflow:visible}.dossier-card.svelte-90l7ri:not(.briefing-summary,.recommended-card),.tech-block.svelte-90l7ri{order:2}}@container (width<=500px){.dossier.svelte-90l7ri .identity-grid:where(.svelte-90l7ri){grid-template-columns:minmax(0,1fr);gap:18px}.dossier-card.svelte-90l7ri{padding:20px}.tech-block.svelte-90l7ri{padding:0 20px}}.app-header.svelte-1n46o8q{background:var(--surface);border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:center;gap:12px 28px;padding:12px 28px;display:flex}.brand.svelte-1n46o8q{color:var(--text);letter-spacing:-.5px;align-items:center;gap:10px;font-size:18px;font-weight:700;text-decoration:none;display:inline-flex}.brand-mark.svelte-1n46o8q{background:var(--text);width:30px;height:30px;color:var(--surface);border-radius:8px;place-items:center;font-size:17px;display:grid}nav.svelte-1n46o8q{flex-wrap:wrap;gap:4px;display:flex}nav.svelte-1n46o8q a:where(.svelte-1n46o8q),nav.svelte-1n46o8q button:where(.svelte-1n46o8q){min-height:44px;color:var(--muted);background:0 0;border:0;border-radius:8px;align-items:center;padding:8px 14px;text-decoration:none;display:inline-flex}nav.svelte-1n46o8q a[aria-current=page]:where(.svelte-1n46o8q){background:var(--accent-soft);color:var(--accent);font-weight:650}main.svelte-1n46o8q{max-width:1500px;min-height:calc(100dvh - 160px);margin:0 auto;padding:28px}main.svelte-1n46o8q:focus{outline:none}.app-footer.svelte-1n46o8q{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;justify-content:space-between;gap:8px;padding:16px 28px;font-size:12px;display:flex}.skip-link.svelte-1n46o8q{z-index:10;background:var(--surface);padding:12px;position:fixed;top:-100px;left:16px}.skip-link.svelte-1n46o8q:focus{top:12px}section[aria-label=Keyboard\ shortcuts].svelte-1n46o8q{background:var(--surface);border:1px solid var(--border);border-radius:12px;margin:16px 28px;padding:20px}@media (width<=640px){.app-header.svelte-1n46o8q{gap:10px;padding:12px 16px}nav.svelte-1n46o8q{order:3;width:100%}nav.svelte-1n46o8q a:where(.svelte-1n46o8q),nav.svelte-1n46o8q button:where(.svelte-1n46o8q){padding:8px 10px}main.svelte-1n46o8q,.app-footer.svelte-1n46o8q{padding:16px}}:root{color:var(--text);background:var(--canvas);--canvas:#f4f5f7;--surface:#fff;--subtle:#f8f9fb;--text:#14171c;--muted:#535d6b;--border:#d9dde3;--accent:#1b4f9c;--accent-soft:#e8eef7;--success:#225b38;--success-bg:#e8f3ec;--success-border:#9ec4aa;--warning:#6b4700;--warning-bg:#fff4d6;--warning-border:#d9b94f;--danger:#9e302c;--danger-bg:#fcecea;--danger-border:#dea49d;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;font:14px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}*{box-sizing:border-box}body{margin:0}a{color:var(--accent);text-underline-offset:3px}a:hover{text-decoration:underline}button,input,select,textarea{font:inherit;color:inherit;border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:9px 12px}button,select{min-height:44px}button{cursor:pointer;font-weight:550}button:hover{background:var(--accent-soft);border-color:var(--accent)}button:disabled{cursor:default;opacity:.6}input,select,textarea{max-width:100%}input:not([type=checkbox],[type=radio]),textarea{min-height:44px}textarea{resize:vertical;width:100%;min-height:96px;display:block}:focus-visible{outline:3px solid var(--accent);outline-offset:3px}h1,h2,h3,h4,p,dl,ul,ol{margin-top:0}h2{letter-spacing:-.6px;overflow-wrap:anywhere;margin-bottom:12px;font-size:26px;line-height:1.25}h3{margin-bottom:12px;font-size:16px;font-weight:650}h4{margin-bottom:12px;font-size:14px}p{margin-bottom:14px}section,article{min-width:0}section+section{margin-top:24px}code,pre,kbd{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}pre{white-space:pre-wrap;background:var(--subtle);border-radius:8px;padding:12px}kbd{border:1px solid var(--border);background:var(--surface);border-bottom-width:2px;border-radius:4px;padding:1px 5px}time{font-variant-numeric:tabular-nums}[role=alert],.coverage-note,[role=note]{border:1px solid var(--warning-border);background:var(--warning-bg);color:var(--warning);overflow-wrap:anywhere;border-radius:8px;padding:12px 16px}[role=status]:not(.conn){color:var(--muted)}.eyebrow{text-transform:uppercase;letter-spacing:.08em;color:var(--muted);font-size:11px;font-weight:650}.section-note,.help{color:var(--muted);font-size:13px}.pill{border:1px solid var(--border);background:var(--subtle);color:var(--muted);vertical-align:middle;border-radius:999px;align-items:center;gap:4px;padding:2px 9px;font-size:12px;display:inline-flex}.pill[data-state=ok],.priority[data-priority=low]{color:var(--success);background:var(--success-bg);border-color:var(--success-border)}.pill[data-state=degraded],.priority[data-priority=medium]{color:var(--warning);background:var(--warning-bg);border-color:var(--warning-border)}.pill[data-state=blind],.priority[data-priority=high],.priority[data-priority=critical]{color:var(--danger);background:var(--danger-bg);border-color:var(--danger-border)}.priority{text-transform:uppercase;letter-spacing:.03em;border-radius:5px;font-weight:650}.conn{color:var(--muted);align-items:center;gap:7px;margin:0 0 0 auto;font-size:12px;display:flex}.dot{background:var(--muted);border-radius:50%;flex-shrink:0;width:8px;height:8px}.conn[data-state=live] .dot{background:var(--success)}.conn[data-state=retrying] .dot{background:var(--warning)}.verdict{border:1px solid var(--border);background:var(--surface);border-radius:12px;margin-bottom:24px;padding:22px 24px}.verdict[data-verdict=review]{background:var(--warning-bg);border-color:var(--warning-border)}.verdict[data-verdict=act]{background:var(--danger-bg);border-color:var(--danger-border)}.verdict[data-verdict=quiet]{border-color:var(--success-border)}.verdict-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:8px 0 16px;display:flex}.verdict h2{margin:0;font-size:30px}.verdict-scale{border:1px solid var(--border);background:var(--surface);border-radius:999px;margin:0;padding:0;list-style:none;display:flex;overflow:hidden}.verdict-scale li{text-transform:capitalize;color:var(--muted);padding:6px 16px;font-size:12px}.verdict-scale [aria-current=true]{background:var(--text);color:var(--surface);font-weight:650}.pills{flex-wrap:wrap;gap:8px;margin:0 0 12px;padding:0;list-style:none;display:flex}.context{color:var(--muted);margin:0;font-size:12px}.reasons{font-size:13px}.review-workspace{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);align-items:start;gap:24px;display:grid}.review-list,.detail-pane{scrollbar-gutter:stable;max-height:calc(100dvh - 120px);padding:3px;overflow:auto}.review-list>section{margin-bottom:24px}.attention{gap:8px;margin:0;padding:0;list-style:none;display:grid}.attention-row{background:var(--surface);border:1px solid var(--border);text-align:left;width:100%;color:var(--text);border-radius:10px;align-items:flex-start;gap:12px;padding:16px;font-weight:400;text-decoration:none;display:flex}.attention-row:hover,.host-list a:hover{background:var(--subtle);border-color:var(--accent);text-decoration:none}.attention-row[aria-current=true]{background:var(--accent-soft);border-color:var(--accent);box-shadow:inset 3px 0 var(--accent)}.attention>li>button:not(.attention-row){margin:8px 0}.row-main{overflow-wrap:anywhere;flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.row-main code{color:var(--muted);font-size:11px}.row-meta{color:var(--muted);text-align:right;flex-direction:column;align-items:flex-end;gap:6px;font-size:11px;display:flex}.detail-pane{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;position:sticky;top:20px}.pane-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;font-size:12px;display:flex}.pane-heading .eyebrow{margin-right:auto}.empty-detail{text-align:center;max-width:340px;min-height:310px;color:var(--muted);flex-direction:column;justify-content:center;align-items:center;margin:auto;display:flex}.empty-detail h2{color:var(--text);font-size:20px}.empty-mark{border:1px solid var(--border);background:var(--subtle);border-radius:12px;place-items:center;width:48px;height:48px;margin-bottom:20px;font-size:24px;display:grid}.digest{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:0;display:grid}.digest>div{border:1px solid var(--border);background:var(--surface);border-radius:10px;padding:14px}.digest dt{color:var(--muted);font-size:12px;font-weight:500}.digest dd{margin:8px 0 0;font-size:26px;font-weight:600}.expected{color:var(--muted);font-size:11px;font-weight:400;display:block}.sources{margin:0;padding:0;list-style:none}.sources>li{background:var(--surface);border:1px solid var(--border);border-radius:10px;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-bottom:8px;padding:14px 16px;font-size:12px;display:flex}.sources>li>span:not(.pill){color:var(--muted)}.host-list{gap:8px;margin:20px 0;padding:0;list-style:none;display:grid}.host-list a{border:1px solid var(--border);background:var(--surface);color:var(--text);border-radius:10px;align-items:center;gap:16px;padding:18px 20px;text-decoration:none;display:flex}.host-list .row-main>span{color:var(--muted);font-size:12px}.host-icon{color:var(--accent);background:var(--accent-soft);border-radius:8px;padding:8px 12px}main>article{max-width:1050px;margin:0 auto}article>section,article>dl,main>section[aria-label=Health]>section{background:var(--surface);border:1px solid var(--border);border-radius:12px;margin-bottom:20px;padding:20px 24px}.detail-pane article>section{border:0;border-bottom:1px solid var(--border);border-radius:0;margin:0 0 16px;padding:0 0 16px}.detail-pane article>section>:last-child{margin-bottom:0}.briefing-heading{justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.briefing-heading h2{letter-spacing:-.3px;margin:0;font-size:20px}.briefing-description{max-width:75ch;font-size:15px;line-height:1.6}.briefing-meta{color:var(--muted);overflow-wrap:anywhere;flex-wrap:wrap;align-items:baseline;gap:8px 16px;font-size:12px;display:flex}.briefing-meta>span{min-width:0}.briefing-host{color:var(--text)}.briefing .stepper{border:1px solid var(--border);background:var(--subtle);border-radius:8px;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;margin-bottom:10px;display:grid}.briefing .stepper li{color:var(--muted);background:0 0;border:0;border-radius:0;padding:10px 12px}.briefing .stepper li+li{border-left:1px solid var(--border)}.briefing .stepper strong{color:var(--text);margin-top:3px;font-size:14px}.priority-note span{display:block}article h2{font-size:23px}article p,article li{overflow-wrap:anywhere}dl:not(.digest,.health-metrics){grid-template-columns:minmax(110px,.35fr) minmax(0,1fr);gap:10px 20px;display:grid}dt{color:var(--muted);font-size:13px;font-weight:600}dd{overflow-wrap:anywhere;margin:0}details{border:1px solid var(--border);background:var(--surface);border-radius:10px;margin-bottom:10px;padding:0 18px}summary{cursor:pointer;min-height:48px;padding:13px 0;font-weight:600}details>dl{padding:12px 0}details>section{border-top:1px solid var(--border);padding:16px 0}.health-metrics{grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-bottom:0;display:grid}.health-metrics>div{background:var(--subtle);border:1px solid var(--border);border-radius:8px;padding:16px}.health-metrics dd{margin-top:10px;font-size:26px}.health-metrics small{color:var(--muted);font-size:13px}.stepper{flex-wrap:wrap;gap:8px;padding:0;list-style:none;display:flex}.stepper li{background:var(--subtle);border:1px solid var(--border);border-radius:8px;flex:130px;padding:12px;font-size:12px}.stepper strong{font-size:15px;display:block}fieldset{border:1px solid var(--border);border-radius:10px;margin:12px 0;padding:12px 16px}label{margin:10px 0;font-size:13px;display:block}label input:not([type=checkbox],[type=radio]),label select,label textarea{margin-top:4px}.trail{padding:0;list-style:none}.trail li{border-top:1px solid var(--border);padding:12px 0;font-size:13px}.trail .text{overflow-wrap:anywhere}.trail .until{color:var(--muted);font-size:12px}@media (width<=1050px){.review-workspace{grid-template-columns:minmax(0,1fr)}.review-list,.detail-pane{max-height:none;overflow:visible}.detail-pane{position:static}.health-metrics{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width<=640px){.briefing .stepper{grid-template-columns:minmax(0,1fr)}.briefing .stepper li{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.briefing .stepper li+li{border-left:0;border-top:1px solid var(--border)}h2{font-size:23px}.verdict{padding:18px}.verdict h2{font-size:26px}.digest{grid-template-columns:repeat(2,minmax(0,1fr))}.attention-row,.host-list a{flex-wrap:wrap;gap:10px;padding:14px}.row-meta{text-align:left;flex-direction:row;justify-content:space-between;align-items:center;width:100%}.host-icon{display:none}.detail-pane,article>section,article>dl{padding:16px}dl:not(.digest,.health-metrics){grid-template-columns:minmax(0,1fr);gap:4px}dd{margin-bottom:10px}.health-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (prefers-color-scheme:dark){:root{--canvas:#15191f;--surface:#1e242d;--subtle:#252c36;--text:#edf0f5;--muted:#b4bfce;--border:#414b5a;--accent:#9dc2ff;--accent-soft:#293c58;--success:#abd7b8;--success-bg:#213b2b;--success-border:#4e7a5d;--warning:#f1d38e;--warning-bg:#3d321e;--warning-border:#8c7237;--danger:#f3b3ad;--danger-bg:#422b2b;--danger-border:#9e625b}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}.sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.review-workspace[data-empty=true]{grid-template-columns:minmax(0,1fr);max-width:900px;margin:0 auto} +.decision-card.svelte-38cs4v{background:var(--surface);border:1px solid var(--border);overflow-wrap:anywhere;border-radius:16px;padding:26px}.decision-card.svelte-38cs4v h3:where(.svelte-38cs4v){letter-spacing:-.4px;margin-bottom:10px;font-size:20px}.decision-status.svelte-38cs4v{margin-bottom:20px}.decision-card.svelte-38cs4v .decision-status:where(.svelte-38cs4v) .pill:where(.svelte-38cs4v){padding:5px 11px;font-size:12px}.decision-card.svelte-38cs4v [data-review=unreviewed]:where(.svelte-38cs4v),.decision-card.svelte-38cs4v [data-review=snooze]:where(.svelte-38cs4v){color:var(--warning);border-color:var(--warning-border);background:var(--warning-bg)}.decision-card.svelte-38cs4v [data-review=expected]:where(.svelte-38cs4v){color:var(--success);border-color:var(--success-border);background:var(--success-bg)}.decision-card.svelte-38cs4v [data-review=not_expected]:where(.svelte-38cs4v){color:var(--danger);border-color:var(--danger-border);background:var(--danger-bg)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v){border:1px solid var(--border);border-radius:11px;gap:0;min-width:0;margin:0 0 12px;padding:6px 0;display:grid}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label:where(.svelte-38cs4v){cursor:pointer;grid-template-columns:20px minmax(0,1fr);align-items:center;gap:2px 12px;min-height:66px;margin:0;padding:12px 16px;display:grid}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label:where(.svelte-38cs4v):hover{background:var(--subtle)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) label.selected:where(.svelte-38cs4v){background:var(--accent-soft)}.decision-card.svelte-38cs4v fieldset:where(.svelte-38cs4v) input:where(.svelte-38cs4v){width:18px;height:18px;accent-color:var(--accent);grid-row:1/span 2;margin:0;padding:0}.decision-label.svelte-38cs4v{font-weight:600}.decision-hint.svelte-38cs4v{color:var(--muted);grid-column:2;font-size:12px}.decision-card.svelte-38cs4v button[type=submit]:where(.svelte-38cs4v){width:100%;color:var(--surface);background:var(--text);border-color:var(--text);margin-top:8px;font-weight:700}.decision-card.svelte-38cs4v button[type=submit]:where(.svelte-38cs4v):disabled{opacity:.45}.decision-card.svelte-38cs4v .history-heading:where(.svelte-38cs4v){border-top:1px solid var(--border);text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin:24px 0 12px;padding-top:20px;font-size:11px}.empty-history.svelte-38cs4v{color:var(--muted);margin-bottom:0;font-size:13px}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v){padding-left:0;list-style:none}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) li:where(.svelte-38cs4v){flex-wrap:wrap;align-items:center;gap:8px;display:flex}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) time:where(.svelte-38cs4v){color:var(--muted);font-size:11px}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) .text:where(.svelte-38cs4v){flex-basis:100%}.decision-card.svelte-38cs4v .trail:where(.svelte-38cs4v) button:where(.svelte-38cs4v){min-height:32px;padding:4px 8px;font-size:12px}.wb-clipboard-buffer{opacity:0;pointer-events:none;position:fixed}.copy-btn.svelte-ba0p1p{min-height:32px;color:var(--accent);background:var(--accent-soft);padding:4px 10px;font-size:12px}.report-card.svelte-43cneo{border:1px solid var(--border);background:var(--surface);overflow-wrap:anywhere;border-radius:11px;margin:12px 0 0;padding:0 16px}summary.svelte-43cneo{padding:16px 0}.report-heading.svelte-43cneo{vertical-align:top;flex-wrap:wrap;align-items:center;gap:8px;max-width:100%;display:inline-flex}.report-id.svelte-43cneo{font-weight:600}.report-time.svelte-43cneo{color:var(--muted);margin:7px 0 0 16px;font-size:12px;font-weight:400;display:block}.report-body.svelte-43cneo{border-top:1px solid var(--border);padding:16px 0 6px}.report-view.svelte-43cneo{border-top:1px solid var(--border);margin:16px 0;padding:16px 0 0}.report-view.svelte-43cneo h4:where(.svelte-43cneo){font-size:14px}.report-view.svelte-43cneo .report-identity:where(.svelte-43cneo){grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr));gap:14px;margin:0 0 16px;display:grid}.report-identity.svelte-43cneo dt:where(.svelte-43cneo){text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px;font-size:11px}.report-identity.svelte-43cneo dd:where(.svelte-43cneo){margin:0}.confidence-note.svelte-43cneo{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.attributes.svelte-43cneo{border:0;padding:0}.attributes.svelte-43cneo summary:where(.svelte-43cneo){color:var(--accent);font-size:12px}.profile-chip.svelte-43cneo{border:1px solid var(--border);background:var(--accent-soft);border-radius:9px;flex-wrap:wrap;gap:5px 12px;padding:10px 12px;font-size:13px;text-decoration:none;display:flex}li.svelte-6r3uui{border-top:1px solid var(--border);padding:12px 0;list-style:none}.tool-name.svelte-6r3uui{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:600}p.svelte-6r3uui{margin:8px 0}.dossier.svelte-90l7ri{max-width:1400px;margin:0 auto;container-type:inline-size}.dossier-back.svelte-90l7ri{margin:0 0 20px}.dossier-back.svelte-90l7ri a:where(.svelte-90l7ri){text-decoration:none}.dossier-layout.svelte-90l7ri{grid-template-columns:minmax(0,1fr) minmax(300px,.48fr);align-items:start;gap:20px 24px;display:grid}.dossier-layout.svelte-90l7ri>:where(.svelte-90l7ri):not(.dossier-side){grid-column:1;min-width:0}.dossier-card.svelte-90l7ri{border:1px solid var(--border);background:var(--surface);overflow-wrap:anywhere;border-radius:16px;margin:0;padding:26px}.dossier-side.svelte-90l7ri{border-radius:16px;grid-area:1/2/span 7;max-height:calc(100dvh - 40px);position:sticky;top:20px;overflow:auto}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri){flex-wrap:wrap;align-items:flex-start;margin-bottom:16px}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri) h2:where(.svelte-90l7ri){letter-spacing:-.7px;flex:260px;font-size:clamp(23px,2.1vw,30px);line-height:1.22}.dossier.svelte-90l7ri .briefing-heading:where(.svelte-90l7ri) .priority:where(.svelte-90l7ri){text-transform:uppercase;letter-spacing:.06em;padding:5px 12px;font-weight:700}.dossier.svelte-90l7ri .briefing-description:where(.svelte-90l7ri){margin-bottom:24px;font-size:16px;line-height:1.65}.dossier.svelte-90l7ri .identity-grid:where(.svelte-90l7ri){border-top:1px solid var(--border);grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 28px;margin:0;padding-top:22px;display:grid}.identity-grid.svelte-90l7ri>div:where(.svelte-90l7ri){min-width:0}.identity-grid.svelte-90l7ri dt:where(.svelte-90l7ri){text-transform:uppercase;letter-spacing:.08em;margin-bottom:7px;font-size:11px}.identity-grid.svelte-90l7ri dd:where(.svelte-90l7ri){flex-wrap:wrap;align-items:center;gap:6px 10px;margin:0;font-size:15px;font-weight:600;display:flex}.identity-group.svelte-90l7ri{grid-column:1/-1}.identity-group.svelte-90l7ri p:where(.svelte-90l7ri){margin:5px 0 0}.evidence-index.svelte-90l7ri{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:4px 12px;margin-top:24px;padding-top:14px;font-size:13px;display:flex}.snapshot-note.svelte-90l7ri{border:1px solid var(--accent);color:var(--accent);background:var(--accent-soft);border-radius:12px;gap:12px;padding:15px 18px;display:flex}.snapshot-note.svelte-90l7ri p:where(.svelte-90l7ri){margin:0}.info-mark.svelte-90l7ri{border:1.5px solid;border-radius:50%;flex:none;place-items:center;width:19px;height:19px;margin-top:2px;font-size:12px;font-weight:700;display:inline-grid}.dossier.svelte-90l7ri h3:where(.svelte-90l7ri){letter-spacing:-.3px;margin-bottom:12px;font-size:18px}.section-heading.svelte-90l7ri{align-items:flex-start;gap:12px;display:flex}.section-heading.svelte-90l7ri h3:where(.svelte-90l7ri){margin-bottom:5px}.section-heading.svelte-90l7ri .section-note:where(.svelte-90l7ri){margin:0}.spread.svelte-90l7ri{flex-wrap:wrap;justify-content:space-between;margin-bottom:12px}.section-icon.svelte-90l7ri{background:var(--accent-soft);width:34px;height:34px;color:var(--accent);border-radius:9px;flex:none;place-items:center;font-size:23px;display:grid}.recommended-action.svelte-90l7ri{border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:9px;margin:18px 0;padding:16px;font-size:15px;line-height:1.7}.copy-row.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.copy-chip.svelte-90l7ri{border:1px solid var(--border);background:var(--subtle);overflow-wrap:anywhere;border-radius:8px;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding:5px 8px 5px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;display:inline-flex}.priority-rule.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.priority-rule.svelte-90l7ri code:where(.svelte-90l7ri){background:var(--subtle);border:1px solid var(--border);border-radius:6px;padding:4px 8px}.priority-comparison.svelte-90l7ri,.context-links.svelte-90l7ri{border:0;border-top:1px solid var(--border);border-radius:0;margin:16px 0 0;padding:0}.priority-comparison.svelte-90l7ri summary:where(.svelte-90l7ri),.context-links.svelte-90l7ri summary:where(.svelte-90l7ri){color:var(--accent);font-size:13px}.hypothesis.svelte-90l7ri,.context-text.svelte-90l7ri{line-height:1.7}.context-limit.svelte-90l7ri{border:1px solid var(--warning-border);background:var(--warning-bg);color:var(--warning);border-radius:9px;padding:12px 14px;font-size:13px}.lookup-receipts.svelte-90l7ri{margin:0;padding:0;list-style:none}.lookup-receipts.svelte-90l7ri li:where(.svelte-90l7ri){border-top:1px solid var(--border);align-items:flex-start;gap:12px;padding:12px 0;display:flex}.receipt-steps.svelte-90l7ri{flex-wrap:wrap;gap:4px 16px;display:flex}.lookup-receipts.svelte-90l7ri p:where(.svelte-90l7ri){margin:0;font-size:13px}.receipt-mark.svelte-90l7ri{color:var(--muted);font-weight:700}.text-link.svelte-90l7ri{color:var(--accent);text-align:left;background:0 0;border:0;min-height:36px;padding:6px 0;font-size:13px}.text-link.svelte-90l7ri:hover{background:0 0;text-decoration:underline}.citation-row.svelte-90l7ri{flex-wrap:wrap;align-items:center;gap:8px;margin-top:16px;display:flex}.citation.svelte-90l7ri{color:var(--accent);background:var(--accent-soft);min-height:32px;padding:4px 9px;font-size:12px}.tech-block.svelte-90l7ri{padding:0 24px}.tech-block.svelte-90l7ri>summary:where(.svelte-90l7ri){padding:20px 0;font-size:15px}.tech-block.svelte-90l7ri>summary:where(.svelte-90l7ri) span:where(.svelte-90l7ri){margin-left:10px;font-weight:400}.tech-body.svelte-90l7ri{border-top:1px solid var(--border);padding:18px 0 8px}.trace-list.svelte-90l7ri{margin:0 0 20px;padding:0}.embedded.svelte-90l7ri .dossier-layout:where(.svelte-90l7ri){flex-direction:column;gap:16px;display:flex}.embedded.svelte-90l7ri .dossier-layout:where(.svelte-90l7ri)>:where(.svelte-90l7ri){width:100%}.embedded.svelte-90l7ri .dossier-side:where(.svelte-90l7ri){order:1;max-height:none;position:static;overflow:visible}.embedded.svelte-90l7ri .recommended-card:where(.svelte-90l7ri){order:0}.embedded.svelte-90l7ri .dossier-card:where(.svelte-90l7ri):not(.briefing-summary,.recommended-card),.embedded.svelte-90l7ri .tech-block:where(.svelte-90l7ri){order:2}.embedded.svelte-90l7ri .dossier-card:where(.svelte-90l7ri){padding:20px}@container (width<=840px){.dossier-layout.svelte-90l7ri{flex-direction:column;display:flex}.dossier-layout.svelte-90l7ri>:where(.svelte-90l7ri){width:100%}.dossier-side.svelte-90l7ri{order:1;max-height:none;position:static;overflow:visible}.dossier-card.svelte-90l7ri:not(.briefing-summary,.recommended-card),.tech-block.svelte-90l7ri{order:2}}@container (width<=500px){.dossier.svelte-90l7ri .identity-grid:where(.svelte-90l7ri){grid-template-columns:minmax(0,1fr);gap:18px}.dossier-card.svelte-90l7ri{padding:20px}.tech-block.svelte-90l7ri{padding:0 20px}}.app-header.svelte-1n46o8q{background:var(--surface);border-bottom:1px solid var(--border);flex-wrap:wrap;align-items:center;gap:12px 28px;padding:12px 28px;display:flex}.brand.svelte-1n46o8q{color:var(--text);letter-spacing:-.5px;align-items:center;gap:10px;font-size:18px;font-weight:700;text-decoration:none;display:inline-flex}.brand-mark.svelte-1n46o8q{background:var(--text);width:30px;height:30px;color:var(--surface);border-radius:8px;place-items:center;font-size:17px;display:grid}nav.svelte-1n46o8q{flex-wrap:wrap;gap:4px;display:flex}nav.svelte-1n46o8q a:where(.svelte-1n46o8q),nav.svelte-1n46o8q button:where(.svelte-1n46o8q){min-height:44px;color:var(--muted);background:0 0;border:0;border-radius:8px;align-items:center;padding:8px 14px;text-decoration:none;display:inline-flex}nav.svelte-1n46o8q a[aria-current=page]:where(.svelte-1n46o8q){background:var(--accent-soft);color:var(--accent);font-weight:650}main.svelte-1n46o8q{max-width:1500px;min-height:calc(100dvh - 160px);margin:0 auto;padding:28px}main.svelte-1n46o8q:focus{outline:none}.app-footer.svelte-1n46o8q{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;justify-content:space-between;gap:8px;padding:16px 28px;font-size:12px;display:flex}.skip-link.svelte-1n46o8q{z-index:10;background:var(--surface);padding:12px;position:fixed;top:-100px;left:16px}.skip-link.svelte-1n46o8q:focus{top:12px}section[aria-label=Keyboard\ shortcuts].svelte-1n46o8q{background:var(--surface);border:1px solid var(--border);border-radius:12px;margin:16px 28px;padding:20px}@media (width<=640px){.app-header.svelte-1n46o8q{gap:10px;padding:12px 16px}nav.svelte-1n46o8q{order:3;width:100%}nav.svelte-1n46o8q a:where(.svelte-1n46o8q),nav.svelte-1n46o8q button:where(.svelte-1n46o8q){padding:8px 10px}main.svelte-1n46o8q,.app-footer.svelte-1n46o8q{padding:16px}}:root{color:var(--text);background:var(--canvas);--canvas:#f4f5f7;--surface:#fff;--subtle:#f8f9fb;--text:#14171c;--muted:#535d6b;--border:#d9dde3;--accent:#1b4f9c;--accent-soft:#e8eef7;--success:#225b38;--success-bg:#e8f3ec;--success-border:#9ec4aa;--warning:#6b4700;--warning-bg:#fff4d6;--warning-border:#d9b94f;--danger:#9e302c;--danger-bg:#fcecea;--danger-border:#dea49d;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;font:14px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;--canvas:#15191f;--surface:#1e242d;--subtle:#252c36;--text:#edf0f5;--muted:#b4bfce;--border:#414b5a;--accent:#9dc2ff;--accent-soft:#293c58;--success:#abd7b8;--success-bg:#213b2b;--success-border:#4e7a5d;--warning:#f1d38e;--warning-bg:#3d321e;--warning-border:#8c7237;--danger:#f3b3ad;--danger-bg:#422b2b;--danger-border:#9e625b}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}*{box-sizing:border-box}body{margin:0}a{color:var(--accent);text-underline-offset:3px}a:hover{text-decoration:underline}button,input,select,textarea{font:inherit;color:inherit;border:1px solid var(--border);background:var(--surface);border-radius:8px;padding:9px 12px}button,select{min-height:44px}button{cursor:pointer;font-weight:550}button:hover{background:var(--accent-soft);border-color:var(--accent)}button:disabled{cursor:default;opacity:.6}input,select,textarea{max-width:100%}input:not([type=checkbox],[type=radio]),textarea{min-height:44px}textarea{resize:vertical;width:100%;min-height:96px;display:block}:focus-visible{outline:3px solid var(--accent);outline-offset:3px}h1,h2,h3,h4,p,dl,ul,ol{margin-top:0}h2{letter-spacing:-.6px;overflow-wrap:anywhere;margin-bottom:12px;font-size:26px;line-height:1.25}h3{margin-bottom:12px;font-size:16px;font-weight:650}h4{margin-bottom:12px;font-size:14px}p{margin-bottom:14px}section,article{min-width:0}section+section{margin-top:24px}code,pre,kbd{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}pre{white-space:pre-wrap;background:var(--subtle);border-radius:8px;padding:12px}kbd{border:1px solid var(--border);background:var(--surface);border-bottom-width:2px;border-radius:4px;padding:1px 5px}time{font-variant-numeric:tabular-nums}[role=alert],.coverage-note,[role=note]{border:1px solid var(--warning-border);background:var(--warning-bg);color:var(--warning);overflow-wrap:anywhere;border-radius:8px;padding:12px 16px}[role=status]:not(.conn){color:var(--muted)}.eyebrow{text-transform:uppercase;letter-spacing:.08em;color:var(--muted);font-size:11px;font-weight:650}.section-note,.help{color:var(--muted);font-size:13px}.pill{border:1px solid var(--border);background:var(--subtle);color:var(--muted);vertical-align:middle;border-radius:999px;align-items:center;gap:4px;padding:2px 9px;font-size:12px;display:inline-flex}.pill[data-state=ok],.priority[data-priority=low]{color:var(--success);background:var(--success-bg);border-color:var(--success-border)}.pill[data-state=degraded],.priority[data-priority=medium]{color:var(--warning);background:var(--warning-bg);border-color:var(--warning-border)}.pill[data-state=blind],.priority[data-priority=high],.priority[data-priority=critical]{color:var(--danger);background:var(--danger-bg);border-color:var(--danger-border)}.priority{text-transform:uppercase;letter-spacing:.03em;border-radius:5px;font-weight:650}.conn{color:var(--muted);align-items:center;gap:7px;margin:0 0 0 auto;font-size:12px;display:flex}.dot{background:var(--muted);border-radius:50%;flex-shrink:0;width:8px;height:8px}.conn[data-state=live] .dot{background:var(--success)}.conn[data-state=retrying] .dot{background:var(--warning)}.verdict{border:1px solid var(--border);background:var(--surface);border-radius:12px;margin-bottom:24px;padding:22px 24px}.verdict[data-verdict=review]{background:var(--warning-bg);border-color:var(--warning-border)}.verdict[data-verdict=act]{background:var(--danger-bg);border-color:var(--danger-border)}.verdict[data-verdict=quiet]{border-color:var(--success-border)}.verdict-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:8px 0 16px;display:flex}.verdict h2{margin:0;font-size:30px}.verdict-scale{border:1px solid var(--border);background:var(--surface);border-radius:999px;margin:0;padding:0;list-style:none;display:flex;overflow:hidden}.verdict-scale li{text-transform:capitalize;color:var(--muted);padding:6px 16px;font-size:12px}.verdict-scale [aria-current=true]{background:var(--text);color:var(--surface);font-weight:650}.pills{flex-wrap:wrap;gap:8px;margin:0 0 12px;padding:0;list-style:none;display:flex}.context{color:var(--muted);margin:0;font-size:12px}.reasons{font-size:13px}.review-workspace{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);align-items:start;gap:24px;display:grid}.review-list,.detail-pane{scrollbar-gutter:stable;max-height:calc(100dvh - 120px);padding:3px;overflow:auto}.review-list>section{margin-bottom:24px}.attention{gap:8px;margin:0;padding:0;list-style:none;display:grid}.attention-row{background:var(--surface);border:1px solid var(--border);text-align:left;width:100%;color:var(--text);border-radius:10px;align-items:flex-start;gap:12px;padding:16px;font-weight:400;text-decoration:none;display:flex}.attention-row:hover,.host-list a:hover{background:var(--subtle);border-color:var(--accent);text-decoration:none}.attention-row[aria-current=true]{background:var(--accent-soft);border-color:var(--accent);box-shadow:inset 3px 0 var(--accent)}.attention>li>button:not(.attention-row){margin:8px 0}.row-main{overflow-wrap:anywhere;flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.row-main code{color:var(--muted);font-size:11px}.row-meta{color:var(--muted);text-align:right;flex-direction:column;align-items:flex-end;gap:6px;font-size:11px;display:flex}.detail-pane{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;position:sticky;top:20px}.pane-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;font-size:12px;display:flex}.pane-heading .eyebrow{margin-right:auto}.empty-detail{text-align:center;max-width:340px;min-height:310px;color:var(--muted);flex-direction:column;justify-content:center;align-items:center;margin:auto;display:flex}.empty-detail h2{color:var(--text);font-size:20px}.empty-mark{border:1px solid var(--border);background:var(--subtle);border-radius:12px;place-items:center;width:48px;height:48px;margin-bottom:20px;font-size:24px;display:grid}.digest{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:0;display:grid}.digest>div{border:1px solid var(--border);background:var(--surface);border-radius:10px;padding:14px}.digest dt{color:var(--muted);font-size:12px;font-weight:500}.digest dd{margin:8px 0 0;font-size:26px;font-weight:600}.expected{color:var(--muted);font-size:11px;font-weight:400;display:block}.sources{margin:0;padding:0;list-style:none}.sources>li{background:var(--surface);border:1px solid var(--border);border-radius:10px;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-bottom:8px;padding:14px 16px;font-size:12px;display:flex}.sources>li>span:not(.pill){color:var(--muted)}.host-list{gap:8px;margin:20px 0;padding:0;list-style:none;display:grid}.host-list a{border:1px solid var(--border);background:var(--surface);color:var(--text);border-radius:10px;align-items:center;gap:16px;padding:18px 20px;text-decoration:none;display:flex}.host-list .row-main>span{color:var(--muted);font-size:12px}.host-icon{color:var(--accent);background:var(--accent-soft);border-radius:8px;padding:8px 12px}main>article{max-width:1050px;margin:0 auto}article>section,article>dl,main>section[aria-label=Health]>section{background:var(--surface);border:1px solid var(--border);border-radius:12px;margin-bottom:20px;padding:20px 24px}.detail-pane article>section{border:0;border-bottom:1px solid var(--border);border-radius:0;margin:0 0 16px;padding:0 0 16px}.detail-pane article>section>:last-child{margin-bottom:0}.briefing-heading{justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.briefing-heading h2{letter-spacing:-.3px;margin:0;font-size:20px}.briefing-description{max-width:75ch;font-size:15px;line-height:1.6}.briefing-meta{color:var(--muted);overflow-wrap:anywhere;flex-wrap:wrap;align-items:baseline;gap:8px 16px;font-size:12px;display:flex}.briefing-meta>span{min-width:0}.briefing-host{color:var(--text)}.briefing .stepper{border:1px solid var(--border);background:var(--subtle);border-radius:8px;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;margin-bottom:10px;display:grid}.briefing .stepper li{color:var(--muted);background:0 0;border:0;border-radius:0;padding:10px 12px}.briefing .stepper li+li{border-left:1px solid var(--border)}.briefing .stepper strong{color:var(--text);margin-top:3px;font-size:14px}.priority-note span{display:block}article h2{font-size:23px}article p,article li{overflow-wrap:anywhere}dl:not(.digest,.health-metrics){grid-template-columns:minmax(110px,.35fr) minmax(0,1fr);gap:10px 20px;display:grid}dt{color:var(--muted);font-size:13px;font-weight:600}dd{overflow-wrap:anywhere;margin:0}details{border:1px solid var(--border);background:var(--surface);border-radius:10px;margin-bottom:10px;padding:0 18px}summary{cursor:pointer;min-height:48px;padding:13px 0;font-weight:600}details>dl{padding:12px 0}details>section{border-top:1px solid var(--border);padding:16px 0}.health-metrics{grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-bottom:0;display:grid}.health-metrics>div{background:var(--subtle);border:1px solid var(--border);border-radius:8px;padding:16px}.health-metrics dd{margin-top:10px;font-size:26px}.health-metrics small{color:var(--muted);font-size:13px}.stepper{flex-wrap:wrap;gap:8px;padding:0;list-style:none;display:flex}.stepper li{background:var(--subtle);border:1px solid var(--border);border-radius:8px;flex:130px;padding:12px;font-size:12px}.stepper strong{font-size:15px;display:block}fieldset{border:1px solid var(--border);border-radius:10px;margin:12px 0;padding:12px 16px}label{margin:10px 0;font-size:13px;display:block}label input:not([type=checkbox],[type=radio]),label select,label textarea{margin-top:4px}.trail{padding:0;list-style:none}.trail li{border-top:1px solid var(--border);padding:12px 0;font-size:13px}.trail .text{overflow-wrap:anywhere}.trail .until{color:var(--muted);font-size:12px}@media (width<=1050px){.review-workspace{grid-template-columns:minmax(0,1fr)}.review-list,.detail-pane{max-height:none;overflow:visible}.detail-pane{position:static}.health-metrics{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width<=640px){.briefing .stepper{grid-template-columns:minmax(0,1fr)}.briefing .stepper li{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.briefing .stepper li+li{border-left:0;border-top:1px solid var(--border)}h2{font-size:23px}.verdict{padding:18px}.verdict h2{font-size:26px}.digest{grid-template-columns:repeat(2,minmax(0,1fr))}.attention-row,.host-list a{flex-wrap:wrap;gap:10px;padding:14px}.row-meta{text-align:left;flex-direction:row;justify-content:space-between;align-items:center;width:100%}.host-icon{display:none}.detail-pane,article>section,article>dl{padding:16px}dl:not(.digest,.health-metrics){grid-template-columns:minmax(0,1fr);gap:4px}dd{margin-bottom:10px}.health-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}.review-workspace[data-empty=true]{grid-template-columns:minmax(0,1fr);max-width:900px;margin:0 auto}.sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px} diff --git a/internal/viewer/web/dist/assets/index.js b/internal/viewer/web/dist/assets/index.js index e06d0d5..c7d5594 100644 --- a/internal/viewer/web/dist/assets/index.js +++ b/internal/viewer/web/dist/assets/index.js @@ -1,5 +1,5 @@ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible,d=()=>{};function f(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}function m(e,t){if(Array.isArray(e))return e;if(t===void 0||!(Symbol.iterator in e))return Array.from(e);let n=[];for(let r of e)if(n.push(r),n.length===t)break;return n}var h=1024,g=2048,_=4096,v=8192,y=16384,b=32768,x=1<<25,S=65536,C=1<<19,w=1<<20,T=1<<25,ee=1<<21,te=1<<22,E=1<<23,D=Symbol(`$state`),ne=Symbol(`component`),re=Symbol(`legacy props`),ie=Symbol(``),O=Symbol(`attributes`),ae=Symbol(`class`),oe=Symbol(`style`),se=Symbol(`text`),ce=Symbol(`form reset`),le=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},ue=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`),de={},k=Symbol(`uninitialized`),fe=`http://www.w3.org/1999/xhtml`;function pe(){console.warn(`https://svelte.dev/e/derived_inert`)}function me(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function he(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var A=!1;function ge(e){A=e}var j;function _e(e){if(e===null)throw me(),de;return j=e}function ve(){return _e(Zt(j))}function M(e){if(A){if(Zt(j)!==null)throw me(),de;j=e}}function ye(e=1){if(A){for(var t=e,n=j;t--;)n=Zt(n);j=n}}function be(e=!0){for(var t=0,n=j;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=Zt(n);e&&n.remove(),n=i}}function xe(e){if(!e||e.nodeType!==8)throw me(),de;return e.data}function Se(e){return e===this.v}function Ce(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function we(e){return!Ce(e,this.v)}function Te(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ee(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function De(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Oe(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ke(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Ae(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function je(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Me(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ne(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Pe(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Fe(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ie(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var Le=null;function Re(e){Le=e}function ze(e,t=!1,n){Le={p:Le,i:!1,c:null,e:null,s:e,x:null,r:W,l:null}}function Be(e){var t=Le,n=t.e;if(n!==null){t.e=null;for(var r of n)dn(r)}return e!==void 0&&(t.x=e),t.i=!0,Le=t.p,Ve(e)}function Ve(e={}){return i(e,ne,{value:!0}),e}function He(){return!0}var Ue=[];function We(){var e=Ue;Ue=[],f(e)}function Ge(e){if(Ue.length===0&&!vt){var t=Ue;queueMicrotask(()=>{t===Ue&&We()})}Ue.push(e)}function Ke(){for(;Ue.length>0;)We()}var qe=~(g|_|h);function N(e,t){e.f=e.f&qe|t}function Je(e){e.f&512||e.deps===null?N(e,h):N(e,_)}function Ye(e,t){e.f&2048?t.set(e,g):e.f&4096&&t.get(e)!==2048&&t.set(e,_),N(e,h)}function Xe(e){A&&Xt(e)!==null&&Qt(e)}var Ze=!1;function Qe(){Ze||(Ze=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[ce]?.()})},{capture:!0}))}function $e(e){var t=U,n=W;Nn(null),Pn(null);try{return e()}finally{Nn(t),Pn(n)}}function et(e,t,n,r=n){e.addEventListener(t,()=>$e(n));let i=e[ce];e[ce]=i?()=>{i(),r(!0)}:()=>r(!0),Qe()}function tt(e,t,n,r){let i=He()?at:ct;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=W,c=nt(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){rn(e,s)}rt()}}var d=it();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>st(e))).then(u).catch(e=>rn(e,s)).finally(d)}l?l.then(()=>{s.f&16384?d():(c(),f(),rt())}):f()}function nt(){var e=W,t=U,n=Le,r=F;return function(i=!0){Pn(e),Nn(t),Re(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function rt(e=!0){Pn(null),Nn(null),Re(null),e&&F?.deactivate()}function it(){var e=W,t=e.b,n=F,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function at(e){var t=2|g;return W!==null&&(W.f|=C),{ctx:Le,deps:null,effects:null,equals:Se,f:t,fn:e,reactions:null,rv:0,v:k,wv:0,parent:W,ac:null}}var ot=Symbol(`obsolete`);function st(e,t,n){let r=W;r===null&&Ee();var i=void 0,a=Ft(k),o=!U,s=new Set;return mn(()=>{var t=W,n=p();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==le&&n.reject(e)}).finally(rt)}catch(e){n.reject(e),rt()}var c=F;if(o){if(t.f&32768)var l=it();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(ot);else for(let e of s.values())e.reject(ot);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==ot&&(c.activate(),t?(a.f|=E,zt(a,t)):(a.f&8388608&&(a.f^=E),zt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),ln(()=>{for(let e of s)e.reject(ot)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function P(e){let t=at(e);return In(t),t}function ct(e){let t=at(e);return t.equals=we,t}function lt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(le),t.ac=null}),t.fn!==null&&(t.teardown=d),Zn(t,0),yn(t))}function pt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&Qn(t)}var mt=null,F=null,ht=null,gt=null,_t=null,vt=!1,yt=!1,bt=null,xt=null,St=0,Ct=1,wt=class e{id=Ct++;started=!1;linked=!0;prev=null;next=null;async_deriveds=new Map;current=new Map;previous=new Map;#e=new Set;#t=new Set;#n=0;#r=new Map;#i=null;#a=[];#o=[];#s=new Map;#c=new Map;#l=new Set;is_fork=!1;#u=!1;constructor(){mt===null?mt=this:(mt.next=this,this.prev=mt),mt=this}#d(){if(this.is_fork)return!0;for(let e of this.#r.keys())if(!this.#f(e))return!0;return!1}#f(e){for(var t=e;t!==null;t=t.parent)if(this.#c.has(t))return!0;return!1}skip_effect(e){this.#c.has(e)||this.#c.set(e,{d:[],m:[]}),this.#l.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#c.get(e);if(n){this.#c.delete(e);for(var r of n.d)N(r,g),t(r);for(r of n.m)N(r,_),t(r)}this.#l.add(e)}#p(){var e=[];for(let i of this.#a)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=h}}n||e.push(t)}return this.#a=[],e}#m(){this.started=!0;for(let[e,t]of this.#s)N(e,t),this.schedule(e);this.apply();for(var t=bt=[],n=[],r=xt=[];this.#a.length>0;){St++>1e3&&(this.#b(),Et());for(let e of this.#p())try{this.#h(e,t,n)}catch(t){throw jt(e),this.#d()||this.discard(),t}}if(F=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(bt=null,xt=null,this.#d()){this.#y(n),this.#y(t);for(let[e,t]of this.#c)At(e,t);r.length>0&&F.#m();return}let a=this.#g();if(a)this.#y(n),this.#y(t),a.#v(this);else{this.#s.clear();for(let e of this.#e)e(this);this.#e.clear(),ht=this,Ot(n),Ot(t),ht=null,this.#i?.resolve();var o=F;this.#n===0&&(this.#a.length===0||o!==null)&&this.#b(),o!==null&&(Nt.clear(),o.#m())}}#h(e,t,n){e.f^=h;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#c.has(r))&&r.fn!==null){a?r.f^=h:i&4?t.push(r):Kn(r)&&(i&16&&this.add_dirty_reaction(r,_),Qn(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#g(){for(var e=this.prev;e!==null;e=e.prev)if(!e.is_fork&&this.#_(e))return e;return null}#_(e){for(let[t,[,n]]of this.current)if(!n&&e.current.has(t))return!0;return!1}#v(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_reactions(e.#s);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#s.delete(i),N(i,g),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#b(),F=this,this.#m()}#y(e){for(var t=0;t{this.#u=!1,this.linked&&this.flush()}))}transfer_reactions(e){for(let[t,n]of e)this.add_dirty_reaction(t,n);e.clear()}add_dirty_reaction(e,t){(t===2048||this.#s.get(e)!==2048)&&this.#s.set(e,t)}oncommit(e){this.#e.add(e)}ondiscard(e){this.#t.add(e)}settled(){return(this.#i??=p()).promise}static ensure(){if(F===null){let t=F=new e;!yt&&!vt&&Ge(()=>{t.started||t.flush()})}return F}apply(){gt=null}schedule(e){_t=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)?e.b.defer_effect(e):this.#a.push(e)}#b(){if(this.linked){var e=this.prev,t=this.next;e===null||(e.next=t),t===null?mt=e:t.prev=e,this.linked=!1}}};function Tt(e){var t=vt,n=ht;ht=null,vt=!0;try{var r;for(e&&(Tt(),r=e());;){if(Ke(),F===null)return r;F.flush()}}finally{vt=t,ht=n}}function Et(){try{je()}catch(e){rn(e,_t)}}var Dt=null;function Ot(e){var t=e.length;if(t!==0){for(var n=0;n0)){Nt.clear();for(let e of Dt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Dt.has(n)&&(Dt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||Qn(n)}}Dt.clear()}}Dt=null}}function kt(e){F.schedule(e)}function At(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),N(e,h);for(var n=e.first;n!==null;)At(n,t),n=n.next}}function jt(e){N(e,h);for(var t=e.first;t!==null;)jt(t),t=t.next}var Mt=new Set,Nt=new Map,Pt=!1;function Ft(e,t){return{f:0,v:e,reactions:null,equals:Se,rv:0,wv:0}}function I(e,t){let n=Ft(e,t);return In(n),n}function It(e,t=!1,n=!0){let r=Ft(e);return t||(r.equals=we),r}function L(e,t,n=!1){return U!==null&&(!Mn||U.f&131072)&&He()&&U.f&4325394&&(Fn===null||!Fn.has(e))&&Fe(),zt(e,n?Ut(t):t,xt)}var Lt=null,Rt=0;function zt(e,t,n=null){if(!e.equals(t)){An?Nt.set(e,t):Nt.has(e)||Nt.set(e,e.v);var r=wt.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&ut(t),gt===null&&Je(t)}e.wv=Gn(),Lt=null,Rt=0,Ht(e,g,n),Lt=null,He()&&W!==null&&W.f&1024&&!(W.f&96)&&(zn===null?Bn([e]):zn.push(e)),!r.is_fork&&Mt.size>0&&!Pt&&Bt()}return t}function Bt(){Pt=!1;for(let e of Mt){e.f&1024&&N(e,_);let t;try{t=Kn(e)}catch{t=!0}t&&Qn(e)}Mt.clear()}function Vt(e){L(e,e.v+1)}function Ht(e,t,n){var r=e.reactions;if(r!==null){var i=He(),a=r.length;if(Rt+=a,Rt>1e5&&Lt===null&&(Lt=new Set),Lt!==null){if(Lt.has(e))return;Lt.add(e)}for(var o=0;o{if(Un===d)return e();var t=U,n=Un;Nn(null),Wn(d);var r=e();return Nn(t),Wn(n),r};return i&&r.set(`length`,I(t.length,u)),new Proxy(t,{defineProperty(e,t,n){`value`in n&&n.configurable!==!1&&n.enumerable!==!1&&n.writable!==!1||Ne();var i=r.get(t);return i===void 0?f(()=>{var e=I(n.value,u);return r.set(t,e),e}):L(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>I(k,u));r.set(t,e),Vt(o)}}else L(n,k),Vt(o);return!0},get(e,n,i){if(n===D)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>I(Ut(s?e[n]:k),u)),r.set(n,o)),o!==void 0){var c=G(o);return c===k?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var n=Reflect.getOwnPropertyDescriptor(e,t),i=r.get(t);if(i!==void 0){var a=G(i);if(a===k)return;if(n&&`value`in n)n.value=a;else return{enumerable:!0,configurable:!0,value:a,writable:!0}}return n},has(e,t){if(t===D)return!0;var n=r.get(t),i=n!==void 0&&n.v!==k||Reflect.has(e,t);return(n!==void 0||(W!==null||U!==null)&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>I(i?Ut(e[t]):k,u)),r.set(t,n)),G(n)===k)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;dI(k,u)),r.set(d+``,p)):L(p,k)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>I(void 0,u)),L(c,Ut(n)),r.set(t,c));else{l=c.v!==k;var m=f(()=>Ut(n));L(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&L(g,_+1)}Vt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==k});for(var[n,i]of r)i.v!==k&&!(n in e)&&t.push(n);return t},setPrototypeOf(){Pe()}})}var Wt,Gt,Kt,qt;function Jt(){if(Wt===void 0){Wt=window,Gt=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;Kt=a(t,`firstChild`).get,qt=a(t,`nextSibling`).get,u(e)&&(e[ae]=void 0,e[O]=null,e[oe]=void 0,e.__e=void 0),u(n)&&(n[se]=void 0)}}function Yt(e=``){return document.createTextNode(e)}function Xt(e){return Kt.call(e)}function Zt(e){return qt.call(e)}function R(e,t){if(!A)return Xt(e);var n=Xt(j);if(n===null)n=j.appendChild(Yt());else if(t&&n.nodeType!==3){var r=Yt();return n?.before(r),_e(r),r}return t&&tn(n),_e(n),n}function z(e,t=!1){if(!A){var n=Xt(e);return n instanceof Comment&&n.data===``?Zt(n):n}if(t){if(j?.nodeType!==3){var r=Yt();return j?.before(r),_e(r),r}tn(j)}return j}function B(e,t=!1){if(!A)return Xt(e);var n=R(e,t);return M(e),n}function V(e,t=1,n=!1){let r=A?j:e;for(var i;t--;)i=r,r=Zt(r);if(!A)return r;if(n){if(r?.nodeType!==3){var a=Yt();return r===null?i?.after(a):r.before(a),_e(a),a}tn(r)}return _e(r),r}function Qt(e){e.textContent=``}function $t(){return!1}function en(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function tn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function nn(e){var t=W;if(t===null)return U.f|=E,e;if(!(t.f&32768)&&!(t.f&4))throw e;rn(e,t)}function rn(e,t){if(e===de)throw e;if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function an(e){W===null&&(U===null&&Ae(e),ke()),An&&Oe(e)}function on(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function sn(e,t){var n=W;n!==null&&n.f&8192&&(e|=v);var r={ctx:Le,deps:null,nodes:null,f:e|g|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};F?.register_created_effect(r);var i=r;if(e&4)bt===null?wt.ensure().schedule(r):bt.push(r);else if(t!==null){try{Qn(r)}catch(e){throw xn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=S))}if(i!==null&&(i.parent=n,n!==null&&on(i,n),U!==null&&U.f&2&&!(e&64))){var a=U;(a.effects??=[]).push(i)}return r}function cn(){return U!==null&&!Mn}function ln(e){let t=sn(8,null);return N(t,h),t.teardown=e,t}function un(e){an(`$effect`);var t=W.f;if(!U&&t&32&&Le!==null&&!Le.i){var n=Le;(n.e??=[]).push(e)}else return dn(e)}function dn(e){return sn(4|w,e)}function fn(e){wt.ensure();let t=sn(64|C,e);return(e={})=>new Promise(n=>{e.outro?wn(t,()=>{xn(t),n(void 0)}):(xn(t),n(void 0))})}function pn(e){return sn(4,e)}function mn(e){return sn(te|C,e)}function hn(e,t=0){return sn(8|t,e)}function H(e,t=[],n=[],r=[]){tt(r,t,n,t=>{sn(8,()=>{e(...t.map(G))})})}function gn(e,t=0){return sn(16|t,e)}function _n(e){return sn(32|C,e)}function vn(e){var t=e.teardown;if(t!==null){let n=An,r=U;jn(!0),Nn(null);try{t.call(null)}catch(t){rn(t,e.parent)}finally{jn(n),Nn(r)}}}function yn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&$e(()=>{e.abort(le)});var r=n.next;n.f&64?n.parent=null:xn(n,t),n=r}}function bn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||xn(t),t=n}}function xn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Sn(e.nodes.start,e.nodes.end),n=!0),e.f|=x,yn(e,t&&!n),Zn(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();vn(e),e.f^=x,e.f|=y;var i=e.parent;i!==null&&i.first!==null&&Cn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Sn(e,t){for(;e!==null;){var n=e===t?null:Zt(e);e.remove(),e=n}}function Cn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function wn(e,t,n=!0){var r=[];e.f|=256,Tn(e,r,!0);var i=()=>{n&&xn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Tn(e,t,n){if(!(e.f&8192)){e.f^=v;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Tn(i,t,o?n:!1)}i=a}}}function En(e){e.f&=-257,Dn(e,!0)}function Dn(e,t){if(!(e.f&256)&&e.f&8192){e.f^=v,e.f&1024||(N(e,g),wt.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Dn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function On(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:Zt(n);t.append(n),n=i}}var kn=null,An=!1;function jn(e){An=e}var U=null,Mn=!1;function Nn(e){U=e}var W=null;function Pn(e){W=e}var Fn=null;function In(e){U!==null&&(U.f&2097152||U.f&2)&&(Fn??=new Set).add(e)}var Ln=null,Rn=0,zn=null;function Bn(e){zn=e}var Vn=1,Hn=0,Un=Hn;function Wn(e){Un=e}function Gn(){return++Vn}function Kn(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&>===null&&N(e,h)}return!1}function qn(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Fn!==null&&Fn.has(e)))for(var i=0;i{e.ac.abort(le)}),e.ac=null);try{e.f|=ee;var u=e.fn,d=u();e.f|=b;var f=Yn(e);if(He()&&zn!==null&&!Mn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=Rn+Ln.length,r=0;r{s.ac.abort(le),s.ac=null,N(s,g)}),ft(s),Zn(s,0)}}function Zn(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Ge(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function lr(e,t,n,r,i){var a={capture:r,passive:i},o=cr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&ln(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function K(e,t,n){(t[ar]??={})[e]=n}function ur(e){for(var t=0;t{fr=!1,dr=null}));var s=0,c=dr===e&&e[ar];if(c){var l=a.indexOf(c);if(l!==-1&&(t===document||t===window)){e[ar]=t;return}var u=a.indexOf(t);if(u===-1)return;l<=u&&(s=l)}if(o=a[s]||e.target,o!==t){i(e,`currentTarget`,{configurable:!0,get(){return o||n}});var d=U,f=W;Nn(null),Pn(null);try{for(var p,m=[];o!==null&&o!==t;){try{var h=o[ar]?.[r];h==null||o.disabled&&e.target!==o||h.call(o,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;s++,o=s{throw e});throw p}}finally{e[ar]=t,delete e.currentTarget,Nn(d),Pn(f)}}}var mr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function hr(e){return mr?.createHTML(e)??e}function gr(e){var t=en(`template`);return t.innerHTML=hr(e.replaceAll(``,``)),t.content}function _r(e,t){var n=W;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function q(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(A)return _r(j,null),j;i===void 0&&(i=gr(a?e:``+e),n||(i=Xt(i)));var t=r||Gt?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=Xt(t),s=t.lastChild;_r(o,s)}else _r(t,t);return t}}function vr(e=``){if(!A){var t=Yt(e+``);return _r(t,t),t}var n=j;return n.nodeType===3?tn(n):(n.before(n=Yt()),_e(n)),_r(n,n),n}function yr(){if(A)return _r(j,null),j;var e=document.createDocumentFragment(),t=document.createComment(``),n=Yt();return e.append(t,n),_r(t,n),e}function J(e,t){if(A){var n=W;n.f&32768&&n.nodes.end!==null||(n.nodes.end=j),ve()}else e!==null&&e.before(t)}function br(e){let t=0,n=Ft(0),r;return()=>{cn()&&(G(n),hn(()=>(t===0&&(r=nr(()=>e(()=>Vt(n)))),t+=1,()=>{Ge(()=>{--t,t===0&&(r?.(),r=void 0,Vt(n))})})))}}var xr=S|C;function Sr(e,t,n,r){new Cr(e,t,n,r)}var Cr=class{parent;is_pending=!1;transform_error;#e;#t=A?j:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Map;#p=null;#m=br(()=>(this.#p=Ft(this.#l),()=>{this.#p=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=W;t.b=this,t.f|=128,n(e)},this.parent=W.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=gn(()=>{if(A){let e=xe(this.#t);ve();let t=e===`[!`;if(e.startsWith(`[?`)){let t=JSON.parse(e.slice(2));this.#g(t)}else t?this.#y():this.#h()}else this.#b()},xr),A&&(this.#e=j)}#h(){try{this.#a=_n(()=>this.#r(this.#e))}catch(e){this.error(e)}}#g(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#_(e);Ge(r),t&&(this.#s=_n(()=>{t(this.#e,()=>e,()=>n)}))}#_(e){var t=!1,n=!1;let r=()=>{this.#v()||(t?he():(t=!0,n&&Ie(),this.#s!==null&&wn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})))};return{reset:r,invoke_onerror:()=>{if(!this.#v())try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){rn(e,this.#i&&this.#i.parent)}}}}#v(){return(this.#i.f&(y|x))!==0}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=_n(()=>e(this.#e)),Ge(()=>{if(!this.#v()){var e=this.#c=document.createDocumentFragment(),t=Yt(),n=!1;e.append(t),this.#a=this.#S(()=>{try{return _n(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){rn(e,this.#i.parent)}return null}}),this.#a===null?(this.#c=null,n&&this.#x(F)):this.#u===0&&(this.#e.before(e),this.#c=null,wn(this.#o,()=>{this.#o=null}),this.#x(F))}}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=_n(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();On(this.#a,e);let t=this.#n.pending;this.#o=_n(()=>t(this.#e))}else this.#x(F)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_reactions(this.#f)}defer_effect(e){Ye(e,this.#f)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=W,n=U,r=Le;Pn(this.#i),Nn(this.#i),Re(this.#i.ctx);try{return wt.ensure(),e()}finally{Pn(t),Nn(n),Re(r)}}#C(e,t){this.has_pending_snippet()?(this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&wn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))):this.parent&&this.parent.#C(e,t)}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!this.#p||this.#d||(this.#d=!0,Ge(()=>{this.#d=!1,this.#p&&zt(this.#p,this.#l)}))}get_effect_pending(){return this.#m(),G(this.#p)}error(e){if(e===de||!this.#n.onerror&&!this.#n.failed)throw e;F?.is_fork?(this.#a&&F.skip_effect(this.#a),this.#o&&F.skip_effect(this.#o),this.#s&&F.skip_effect(this.#s),F.oncommit(()=>{this.#v()||this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(xn(this.#a),null),this.#o&&=(xn(this.#o),null),this.#s&&=(xn(this.#s),null),A&&(_e(this.#t),ye(),_e(be()));let t=this.#n.failed,n=e=>{if(this.#v())return;let{reset:n,invoke_onerror:r}=this.#_(e);r(),t&&!this.#v()&&(this.#s=this.#S(()=>{try{return _n(()=>{var r=W;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return rn(e,this.#i.parent),null}}))};Ge(()=>{if(!this.#v()){var t;try{t=this.transform_error(e)}catch(e){rn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>rn(e,this.#i&&this.#i.parent)):n(t)}})}};function Y(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[se]??=e.nodeValue)&&(e[se]=n,e.nodeValue=`${n}`)}function wr(e,t){return Er(e,t)}var Tr=new Map;function Er(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){Jt();var l=void 0,u=fn(()=>{var s=n??t.appendChild(Yt());Sr(s,{pending:()=>{}},t=>{ze({});var n=Le;if(o&&(n.c=o),a&&(i.$$events=a),A&&_r(t,null),l=e(t,i)||Ve(),A&&(W.nodes.end=j,j===null||j.nodeType!==8||j.data!==`]`))throw me(),de;Be()},c);var u=new Set,d=e=>{for(var n=0;n{for(var e of u)for(let n of[t,document]){var r=Tr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,pr),r.delete(e),r.size===0&&Tr.delete(n)):r.set(e,i)}sr.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return Dr.set(l,u),l}var Dr=new WeakMap,Or=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)En(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(En(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(xn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();On(r,t),t.append(Yt()),this.#n.set(e,{effect:r,fragment:t})}else xn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),wn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(xn(n.effect),this.#n.delete(e))};ensure(e,t){var n=F,r=$t();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=Yt();i.append(a),this.#n.set(e,{effect:_n(()=>t(a)),fragment:i})}else this.#t.set(e,_n(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else A&&(this.anchor=j),this.#a(n)}};function X(e,t,n=!1){var r;A&&(r=j,ve());var i=new Or(e),a=n?S:0;function o(e,t){if(A){var n=xe(r);if(e!==parseInt(n.substring(1))){var a=be();_e(a),i.anchor=a,ge(!1),i.ensure(e,t),ge(!0);return}}i.ensure(e,t)}gn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var kr=Symbol(`NaN`);function Ar(e,t,n){A&&ve();var r=new Or(e),i=!He();gn(()=>{var e=t();e!==e&&(e=kr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function jr(e,t){return t}function Mr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Nr(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=i.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;Qt(d),d.append(u),e.items.clear()}Nr(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Nr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var t=i();return e(t)?t:t==null?[]:r(t)}),p=new Map,m=!0;function h(e){if(!(_.effect.f&16384)){_.pending.delete(e);var t=G(f);_.fallback=d,Ir(_,t,c,n,a),d!==null&&(t.length===0?d.f&33554432?(d.f^=T,Rr(d,null,c)):En(d):wn(d,()=>{d=null}))}}function g(e){_.pending.delete(e)}var _={effect:gn(()=>{var e=G(f),t=e.length;let r=!1;A&&xe(c)===`[!`!=(t===0)&&(c=be(),_e(c),ge(!1),r=!0);for(var u=new Set,_=F,v=$t(),y=0;ys(c)):(d=_n(()=>s(Pr??=Yt())),d.f|=T)),t>u.size&&De(``,``,``),A&&t>0&&_e(be()),!m){if(p.set(_,u),v){for(let[e,t]of l)u.has(e)||_.skip_effect(t.e);_.oncommit(h),_.ondiscard(g)}else h(_)}r&&ge(!0),G(f)}),flags:n,items:l,pending:p,outrogroups:null,fallback:d};m=!1,A&&(c=j)}function Fr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Ir(e,t,n,i,a){var o=!!(i&8),s=t.length,c=e.items,l=Fr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v0){var te=i&4&&s===0?n:null;if(o){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Lr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Ft(n):It(n,!1,!1):null,l=o&2?Ft(i):null;return{v:c,i:l,e:_n(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Rr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=Zt(r);if(a.before(r),r===i)return;r=o}}function zr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}var Br=[...` -\r\f\xA0\v`];function Vr(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Br.includes(r[o-1]))&&(s===r.length||Br.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Hr(e,t,n,r,i,a){var o=e[ae];if(A||o!==n||o===void 0){var s=Vr(n,r,a);A&&s===e.getAttribute(`class`)||(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[ae]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}var Ur=Symbol(`is custom element`),Wr=Symbol(`is html`),Gr=ue?`link`:`LINK`,Kr=ue?`progress`:`PROGRESS`;function qr(e){if(A){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[ce]=n,Ge(n),Qe()}}function Jr(e,t){var n=Xr(e);n.value!==(n.value=t??void 0)&&(e.value!==t||t===0&&e.nodeName===Kr)&&(e.value=t??``)}function Yr(e,t){var n=Xr(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=Xr(e);if(!(A&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===Gr))){var a=i[t];a!==(i[t]=n)&&(t===`loading`&&(e[ie]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Qr(e).has(t)?e[t]=n:(!A||typeof n!=`number`&&typeof n!=`boolean`||a!==String(n))&&e.setAttribute(t,n))}}function Xr(e){return e[O]??={[Ur]:e.nodeName.includes(`-`),[Wr]:e.namespaceURI===fe}}var Zr=new Map;function Qr(e){var t=e.getAttribute(`is`)||e.nodeName,n=Zr.get(t);if(n)return n;Zr.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.add(s);i=l(i)}return n}function $r(e,t,n=t){var r=new WeakSet;et(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=ti(e)?ni(a):a,n(a),F!==null&&r.add(F),await $n(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(A&&e.defaultValue!==e.value||nr(t)==null&&e.value)&&(n(ti(e)?ni(e.value):e.value),F!==null&&r.add(F)),hn(()=>{var n=t();if(e===document.activeElement){var i=F;if(r.has(i))return}ti(e)&&n===ni(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function ei(e,t,n=t){et(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(A&&e.defaultChecked!==e.checked||nr(t)==null)&&n(e.checked),hn(()=>{e.checked=!!t()})}function ti(e){var t=e.type;return t===`number`||t===`range`}function ni(e){return e===``?null:+e}function ri(e,t){return e===t||e?.[D]===t}function ii(e=Ve(),t,n,r){var i=Le.r,a=W;return pn(()=>{var o,s;return hn(()=>{o=s,s=r?.()||[],nr(()=>{ri(n(...s),e)||(t(e,...s),o&&ri(n(...o),e)&&t(null,...o))})}),()=>{let r=a;for(;r!==i&&r.parent!==null&&r.parent.f&33554432;)r=r.parent;let o=()=>{s&&ri(n(...s),e)&&t(null,...s)},c=r.teardown;r.teardown=()=>{o(),c?.()}}}),e}var ai=!1;function oi(e){var t=ai;try{return ai=!1,[e(),ai]}finally{ai=t}}function $(e,t,n,r){var i=!0,o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=at(r),G(u)):(l&&(l=!1,c=s?nr(r):r),c);let f;if(o){var p=D in e||re in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=oi(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Me(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?at:ct)(()=>(v=!1,g()));o&&G(y);var b=W;return(function(e,t){if(arguments.length>0){let n=t?G(y):i&&o?Ut(e):e;return L(y,n),v=!0,c!==void 0&&(c=n),e}return An&&v||b.f&16384?y.v:G(y)})}function si(e){Le===null&&Te(`onMount`),un(()=>{let t=nr(e);if(typeof t==`function`)return t})}function ci(e){Le===null&&Te(`onDestroy`),si(()=>()=>nr(e))}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);async function li(){let e=await fetch(`/api/overview`);if(!e.ok)throw Error(`overview ${e.status}`);return e.json()}async function ui(e,t){let n=await fetch(e);if(!n.ok)throw Error(`${t} ${n.status}`);return n.json()}function di(e){return ui(`/api/briefing?id=${encodeURIComponent(e)}`,`briefing`)}async function fi(e){let t={subject_kind:e.subjectKind,subject_id:e.subjectId,kind:e.kind,request_id:e.requestId};e.until!==void 0&&(t.until=e.until),e.text!==void 0&&(t.text=e.text),e.includeMembers!==void 0&&(t.include_members=e.includeMembers);let n=await fetch(`/api/decisions`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(`decision ${n.status}: ${await n.text()}`);return n.json()}function pi(e,t,n={}){let r=new URLSearchParams({subject_kind:e,subject_id:t});return n.limit!==void 0&&r.set(`limit`,String(n.limit)),n.before!==void 0&&r.set(`before`,n.before),ui(`/api/decisions?${r.toString()}`,`decisions`)}async function mi(e,t){let n=await fetch(`/api/decisions/${encodeURIComponent(e)}/undo`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({request_id:t})});if(!n.ok)throw Error(`undo ${n.status}: ${await n.text()}`);return n.json()}function hi(e,t){let n=new URLSearchParams({after:String(t),limit:`200`});return ui(`/api/decisions/${encodeURIComponent(e)}/members?${n}`,`member decisions`)}function gi(e=``){return ui(`/api/devices${e===``?``:`?after=${encodeURIComponent(e)}`}`,`devices`)}function _i(e,t=``){let n=t===``?``:`?history_after=${encodeURIComponent(t)}`;return ui(`/api/devices/${encodeURIComponent(e)}${n}`,`device`)}function vi(){return ui(`/api/health`,`health`)}function yi(e){return ui(`/api/incidents/${encodeURIComponent(e)}`,`incident`)}function bi(){let e=0,t=0;return{start(){return e+=1,{id:e,stream:t}},streamed(){t+=1},invalidate(){e+=1},current(n){return n.id===e&&n.stream===t}}}function xi(e){let t=document.activeElement,n=t?.closest?.(`[data-rows]`);if(!n)return!1;let r=[...n.querySelectorAll(`[data-row]`)];if(r.length===0)return!1;let i=r.indexOf(t);return i===-1&&(i=e===1?-1:0),(r[i+e]??(e===1?r[0]:r[r.length-1])).focus(),!0}function Si(e){let t=e.target;if(t===null||t.isContentEditable)return!0;let n=t.tagName;return n===`INPUT`||n===`TEXTAREA`||n===`SELECT`}function Ci(e){let t=new Date(e??``);return Number.isNaN(t.getTime())?{local:`unknown`,utc:`unknown`}:{local:t.toLocaleString(),utc:t.toISOString()}}function wi(e){switch(e){case`rules`:return`Deterministic rules`;case`model`:return`Model suggestion`;case`agent`:return`Investigator agent`;case`fallback`:return`Deterministic fallback`;case`legacy`:return`Earlier version (source unknown)`;default:return`Unknown source`}}function Ti(e){switch(e){case`reported_device`:return`Reported device`;case`address`:return`Address context — not a device identity`;case`event`:return`Unresolved events — not a device identity`;case`legacy`:return`Legacy profile`;default:return`Unknown class`}}function Ei(e){return{weak_appearance:`Weak sightings`,weak_disappearance:`Weak disappearances`,port_closed:`Ports closed`,hostname_change:`Hostname changes`,os_refinement:`OS refinements`,other:`Other changes`}[e]??e}function Di(e,t=[],n=[]){if(e!==`other`)return Ei(e);let r=new Map([[`low`,0],[`medium`,1],[`high`,2]]),i=new Map;for(let e of t)e.view_role===`previous`&&e.event_id&&i.set(e.event_id,r.get(e.attributes?.identity_confidence));for(let e of t){if(e.view_role!==`current`)continue;let t=i.get(e.event_id),n=r.get(e.attributes?.identity_confidence);if(t!==void 0&&n!==void 0&&ne.event_type));if(a.size===1){if(a.has(`host.appeared`))return`New host reported`;if(a.has(`host.disappeared`))return`Host disappearance reported`;if(a.has(`host.changed`))return`Reported host details changed`}return a.size>1?`Reported network changes`:`Reported details changed`}function Oi(e){let t=/^(\d+)h0m0s$/.exec(e);return t?`${t[1]} hours`:e}function ki(e=[]){let t=(e??[]).map(e=>new Date(e.occurred_at??``)).filter(e=>!Number.isNaN(e.getTime())).sort((e,t)=>e-t);if(t.length===0)return null;let n=t.length!==e.length,r=Ci(t[0].toISOString());if(t.length===1)return{...r,partial:n};let i=Ci(t[t.length-1].toISOString());return{local:`${r.local} – ${i.local}`,utc:`${r.utc} – ${i.utc}`,partial:n}}function Ai(e=[]){if(e.length!==1)return null;let t=(e??[]).filter(e=>e.view_role===`host`||e.view_role===`current`),n=new Set(t.map(e=>e.event_id));if(t.length!==1||n.size!==1)return null;let r=t[0].attributes??{},i=e=>typeof e==`string`&&e!==``?e:null;return{event_id:t[0].event_id,view_role:t[0].view_role,hostname:i(r.hostname),ip:i(r.ip),mac:i(r.mac),randomized:typeof r.randomized==`boolean`?r.randomized:null,identity_confidence:i(r.identity_confidence)??`unknown`,identity_source:i(r.identity_source)??`unknown`}}function ji(e){return e===!0?`Randomized (scanner-reported)`:e===!1?`Not randomized (scanner-reported)`:`Randomization unknown`}function Mi(e,t){return{parentId:e,count:Array.isArray(t.generated_decision_ids)?t.generated_decision_ids.length:0,next:t.generated_next??0,members:[]}}function Ni(e){return`Undone. ${e.next?`At least ${e.count}`:String(e.count)} member decision${e.count===1?` was`:`s were`} generated; incident undo does not undo them. Review them on their briefing pages.`}async function Pi(e,t){let n=await t(e.parentId,e.next),r=n.next??0;if(r!==0&&r<=e.next)throw Error(`Member decision page did not advance. Try again.`);return{parentId:e.parentId,count:e.count+n.members.length,next:r,members:n.members}}var Fi=q(` `),Ii=q(``),Li=q(``),Ri=q(`

`),zi=q(``),Bi=q(``),Vi=q(`

`),Hi=q(`
  • `),Ui=q(`
      `),Wi=q(``),Gi=q(`

      `,1),Ki=q(`

      Recorded decisions

      `),qi=q(`

      No decisions recorded.

      `),Ji=q(`undone`),Yi=q(` `),Xi=q(` `),Zi=q(``),Qi=q(`
    • `),$i=q(``),ea=q(`

      `),ta=q(`
        `,1),na=q(`

        Choose a decision

        `);function ra(e,t){ze(t,!0);let n=$(t,`kinds`,19,()=>[]),r=$(t,`decisions`,19,()=>[]),i=$(t,`effective`,3,null),a=$(t,`preset`,3,null),o=$(t,`reload`,3,async()=>{}),s=$(t,`memberScope`,3,!1),c=$(t,`memberCount`,3,0),l=$(t,`kindHelp`,19,()=>({})),u=$(t,`requireChoice`,3,!1),d={expected:`No further action needed.`,not_expected:`Flag for follow-up: this briefing still needs a check.`,snooze:`Hide from attention until a time. Empty keeps the server default (24h).`,note:`Free-text note, kept with the audit (max 2000 characters).`,label:`Your label for this profile, never identity (max 256 characters).`},f=I(null),p=P(()=>u()?G(f):G(f)??n()[0]??`note`),m=I(``),h=I(``),g=I(!1),_=I(null),v=I(null),y=I(null),b=I(Ut(crypto.randomUUID())),x=I(!1),S=I(null),C=I(null),w=I(!1),T=I(null),ee=I(null),te=I(Ut({})),E=!0;ci(()=>{E=!1});let D=0,ne=P(()=>r().length===0?`empty`:`${r()[0].decision_id}:${r().length}`),re=I(Ut([])),ie=I(``),O=I(Ut([])),ae=I(!1),oe=I(!1),se=I(``),ce=I(null),le=I(``),ue=P(()=>G(ie)===G(ne)&&G(re).length>0?[...r(),...G(re)]:r()),de=P(()=>G(oe)&&G(se)===G(ne)),k=P(()=>G(le)===G(ne)?G(ce):null),fe=P(()=>G(p)===`note`||G(p)===`label`),pe=P(()=>G(p)===`label`?256:2e3),me=P(()=>G(p)!==null&&n().includes(G(p))&&(!G(fe)||G(h).trim()!==``));un(()=>{a()!==null&&n().includes(a().kind)&&(L(f,a().kind,!0),G(ee)?.focus())});function he(){return i()===null||i()===void 0?`No operator review`:i().kind===`snooze`?`Snoozed${i().until?` until ${i().until}`:``}`:i().kind===`expected`?`Reviewed: expected`:`Flagged: ${i().kind.replace(`_`,` `)}`}async function A(e){if(e.preventDefault(),!G(g)&&(!u()||G(me))&&G(p)!==null){L(g,!0),L(v,null);try{let e={subjectKind:t.subjectKind,subjectId:t.subjectId,kind:G(p),requestId:G(b)};if(G(p)===`snooze`&&G(m)!==``){let t=new Date(G(m));if(Number.isNaN(t.getTime()))throw Error(`snooze time is not a date`);e.until=t.toISOString()}G(fe)&&(e.text=G(h)),s()&&(e.includeMembers=G(x));let n=await fi(e);L(b,crypto.randomUUID(),!0),L(f,null),L(m,``),L(h,``),L(x,!1),L(S,null),L(C,null),L(T,null),L(re,[],!0),L(ie,``),L(O,[],!0),L(oe,!1),L(ce,null),D+=1,n.applied===!1?L(v,`Already recorded (${n.reason??`duplicate`}).`):Array.isArray(n.members)&&n.members.length>0&&L(S,`Recorded on the incident and ${n.members.length} member briefing${n.members.length===1?``:`s`}.`),await o()()}catch(e){L(v,e instanceof Error?e.message:String(e),!0)}finally{L(g,!1)}}}async function ge(e){if(G(_)===null){L(_,e,!0),L(y,null),L(S,null),L(C,null),L(T,null);try{G(te)[e]??=crypto.randomUUID();let t=await mi(e,G(te)[e]);((t.generated_decision_ids?.length??0)>0||t.generated_next)&&L(C,Mi(e,t),!0);let n={...G(te)};delete n[e],L(te,n,!0),L(re,[],!0),L(ie,``),L(O,[],!0),L(oe,!1),L(ce,null),D+=1,await o()()}catch(e){L(y,e instanceof Error?e.message:String(e),!0)}finally{L(_,null)}}}async function j(){if(G(w)||!G(C)?.next)return;let e=G(C);L(w,!0),L(T,null);try{let t=await Pi(e,hi);E&&G(C)===e&&L(C,t,!0)}catch(t){E&&G(C)===e&&L(T,t instanceof Error?t.message:String(t),!0)}finally{E&&L(w,!1)}}async function _e(){if(G(ae)||G(de)||G(ue).length===0)return;let e=D,n=G(ne),r=G(ue)[G(ue).length-1].cursor;L(ae,!0),L(y,null);try{let i=await pi(t.subjectKind,t.subjectId,{limit:50,before:r});if(!E||e!==D||n!==G(ne))return;if(i.length===0){L(oe,!0),L(se,n,!0);return}G(ie)===n&&G(re).length>0&&(G(O).push({rows:G(re),key:G(ie)}),G(O).length>4&&(G(O).shift(),L(ce,`Earlier pages dropped from this view; the full audit remains available through the API.`),L(le,n,!0))),L(re,i,!0),L(ie,n,!0),L(oe,i.length<50),L(se,n,!0)}catch(t){if(!E||e!==D||n!==G(ne))return;L(y,t instanceof Error?t.message:String(t),!0)}finally{E&&L(ae,!1)}}function ve(){for(;G(O).length>0&&G(O)[G(O).length-1].key!==G(ne);)G(O).pop();let e=G(O).pop();e===void 0?(L(re,[],!0),L(ie,``)):(L(re,e.rows,!0),L(ie,e.key,!0)),L(oe,!1),L(se,G(ne),!0)}var ye=na();let be;var xe=R(ye),Se=B(xe,!0),Ce=V(xe,2),we=R(Ce),Te=B(we);M(Ce);var Ee=V(Ce,2),De=R(Ee),Oe=R(De);let ke;Z(V(Oe,2),16,n,e=>e,(e,n)=>{var r=Ii();let i;var a=R(r);qr(a);var o=V(a,2),s=B(o,!0),c=V(o,2),l=e=>{var t=Fi(),r=B(t,!0);H(()=>Y(r,{expected:`no further action`,not_expected:`investigate`,snooze:`hide until later`,note:`annotation only`}[n])),J(e,t)};X(c,e=>{u()&&e(l)}),M(r),H(e=>{i=Hr(r,1,`svelte-38cs4v`,null,i,{selected:G(p)===n}),Q(a,`name`,`decision-kind-${t.subjectId}`),Jr(a,n),Yr(a,G(p)===n),Y(s,e)},[()=>n===`not_expected`?`Not expected`:n.charAt(0).toUpperCase()+n.slice(1)]),K(`change`,a,()=>L(f,n,!0)),J(e,r)}),M(De);var Ae=V(De,2),je=B(Ae,!0),Me=V(Ae,2),Ne=e=>{var t=Li(),n=R(t);qr(n);var r=V(n);M(t),H(()=>Y(r,` Also apply to the ${c()??``} member briefing${c()===1?``:`s`} (snapshot now; later joiners stay untouched)`)),ei(n,()=>G(x),e=>L(x,e)),J(e,t)};X(Me,e=>{s()&&e(Ne)});var Pe=V(Me,2),Fe=e=>{var t=Ri(),n=B(t,!0);H(()=>Y(n,G(S))),J(e,t)};X(Pe,e=>{G(S)!==null&&e(Fe)});var Ie=V(Pe,2),Le=e=>{var t=zi(),n=V(R(t));qr(n),M(t),$r(n,()=>G(m),e=>L(m,e)),J(e,t)};X(Ie,e=>{G(p)===`snooze`&&e(Le)});var Re=V(Ie,2),Ve=e=>{var t=Bi(),n=R(t),r=V(n);Xe(r),M(t),H(()=>{Y(n,`${G(p)===`label`?`Label`:`Note`} `),Q(r,`maxlength`,G(pe))}),$r(r,()=>G(h),e=>L(h,e)),J(e,t)};X(Re,e=>{G(fe)&&e(Ve)});var He=V(Re,2),Ue=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(v))),J(e,t)};X(He,e=>{G(v)!==null&&e(Ue)});var We=V(He,2),Ge=B(We,!0);ii(We,e=>L(ee,e),()=>G(ee)),M(Ee);var Ke=V(Ee,2),qe=e=>{var t=Gi(),n=z(t),r=B(n,!0),i=V(n,2),a=e=>{var t=Ui();Z(t,21,()=>G(C).members,e=>e.decision_id,(e,t)=>{var n=Hi(),r=R(n),i=B(r,!0),a=V(r);M(n),H((e,n)=>{Q(r,`href`,e),Y(i,G(t).subject_id),Y(a,` — ${n??``}`)},[()=>`#/briefing/${encodeURIComponent(G(t).subject_id)}`,()=>G(t).kind.replace(`_`,` `)]),J(e,n)}),M(t),J(e,t)};X(i,e=>{G(C).members.length>0&&e(a)});var o=V(i,2),s=e=>{var t=Wi(),n=B(t,!0);H(()=>{t.disabled=G(w),Y(n,G(w)?`Loading…`:G(T)?`Retry remaining member decisions`:`Show remaining member decisions`)}),K(`click`,t,j),J(e,t)};X(o,e=>{G(C).next&&e(s)});var c=V(o,2),l=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(T))),J(e,t)};X(c,e=>{G(T)!==null&&e(l)}),H(e=>Y(r,e),[()=>Ni(G(C))]),J(e,t)};X(Ke,e=>{G(C)!==null&&e(qe)});var N=V(Ke,2),Je=e=>{J(e,Ki())};X(N,e=>{u()&&e(Je)});var Ye=V(N,2),Ze=e=>{J(e,qi())},Qe=e=>{var t=ta(),n=z(t);Z(n,21,()=>G(ue),e=>e.decision_id,(e,t)=>{var n=Qi(),r=R(n),i=B(r,!0),a=V(r,2),o=e=>{J(e,Ji())};X(a,e=>{G(t).undone&&e(o)});var s=V(a,2),c=e=>{var n=Yi(),r=B(n,!0);H(()=>Y(r,G(t).text)),J(e,n)};X(s,e=>{G(t).text&&e(c)});var l=V(s,2),u=e=>{var n=Xi(),r=B(n);H(()=>Y(r,`until ${G(t).until??``}`)),J(e,n)};X(l,e=>{G(t).until&&e(u)});var d=V(l,2),f=B(d,!0),p=V(d,2),m=e=>{var n=Zi(),r=B(n,!0);H(()=>{n.disabled=G(_)!==null,Y(r,G(_)===G(t).decision_id?`Undoing…`:`Undo`)}),K(`click`,n,()=>ge(G(t).decision_id)),J(e,n)};X(p,e=>{!G(t).undone&&G(t).kind!==`undo`&&e(m)}),M(n),H((e,n)=>{Y(i,e),Q(d,`datetime`,G(t).created_at),Y(f,n)},[()=>G(t).kind.replace(`_`,` `),()=>new Date(G(t).created_at).toLocaleString()]),J(e,n)}),M(n);var r=V(n,2),i=e=>{var t=Wi(),n=B(t,!0);H(()=>{t.disabled=G(ae),Y(n,G(ae)?`Loading…`:`Show older decisions`)}),K(`click`,t,_e),J(e,t)};X(r,e=>{G(de)||e(i)});var a=V(r,2),o=e=>{var t=$i();H(()=>t.disabled=G(ae)),K(`click`,t,ve),J(e,t)};X(a,e=>{G(ie)===G(ne)&&G(re).length>0&&e(o)});var s=V(a,2),c=e=>{var t=ea(),n=B(t,!0);H(()=>Y(n,G(k))),J(e,t)};X(s,e=>{G(k)!==null&&e(c)}),J(e,t)};X(Ye,e=>{G(ue).length===0?e(Ze):e(Qe,-1)});var $e=V(Ye,2),et=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(y))),J(e,t)};X($e,e=>{G(y)!==null&&e(et)}),M(ye),H(e=>{be=Hr(ye,1,`svelte-38cs4v`,null,be,{"decision-card":u()}),Y(Se,u()?`Your decision`:`Operator decisions`),Q(we,`data-review`,i()?.kind??`unreviewed`),Y(Te,`Status: ${e??``}`),ke=Hr(Oe,1,``,null,ke,{"sr-only":u()}),Y(je,G(p)===null?`Choose a verdict to record it.`:l()[G(p)]??d[G(p)]??``),We.disabled=u()?G(g)||!G(me):G(g),Y(Ge,G(g)?`Recording…`:`Record decision`)},[()=>he()]),lr(`submit`,Ee,A),J(e,ye),Be()}ur([`change`,`click`]);var ia=q(` `),aa=q(` `,1);function oa(e,t){ze(t,!0);let n=$(t,`name`,3,`value`),r=I(`idle`),i=I(null),a=!0;ci(()=>{a=!1,G(i)!==null&&clearTimeout(G(i))});async function o(){G(i)!==null&&(clearTimeout(G(i)),L(i,null)),L(r,`idle`);try{if(navigator.clipboard?.writeText!==void 0)await navigator.clipboard.writeText(t.value);else{let e=document.createElement(`textarea`);e.value=t.value,e.className=`wb-clipboard-buffer`,document.body.appendChild(e);let n=document.activeElement,r=!1;try{e.select(),r=document.execCommand(`copy`)}finally{e.remove(),n instanceof HTMLElement&&n.focus({preventScroll:!0})}if(!r)throw Error(`copy rejected`)}if(!a)return;L(r,`copied`),L(i,setTimeout(()=>{L(r,`idle`),L(i,null)},1500),!0)}catch{a&&L(r,`failed`)}}var s=aa(),c=z(s),l=B(c,!0),u=V(c,2),d=e=>{var t=ia(),r=B(t);H(()=>Y(r,`Could not copy the ${n()??``}; select and copy it manually.`)),J(e,t)};X(u,e=>{G(r)===`failed`&&e(d)}),H(()=>{Q(c,`aria-label`,`Copy ${n()}`),Y(l,G(r)===`copied`?`Copied`:G(r)===`failed`?`Copy failed`:`Copy`)}),K(`click`,c,o),J(e,s),Be()}ur([`click`]);var sa=q(`

        No retained observation for this event. Its reference is retained, but a saved scanner view is unavailable.

        `),ca=q(`
        Reported vendor
        `),la=q(`
        Reported OS
        `),ua=q(`
        Reported device
        `),da=q(`

        Some reported fields were truncated at ingest.

        `),fa=q(`
      1. `),pa=q(`
          `),ma=q(`Open reported context → `),ha=q(`

          No linked profile is available for this saved view.

          `),ga=q(`

          Hostname
          IP address
          MAC address

          Saved report view · technical attributes
           

          `),_a=q(`
          Reported

          Reported scan target:
          Received

          `);function va(e,t){ze(t,!0);let n=$(t,`views`,19,()=>[]),r=$(t,`profileLinks`,19,()=>[]),i=P(()=>Ci(t.ref.occurred_at)),a=P(()=>Ci(t.ref.received_at)),o=e=>typeof e==`string`&&e.trim()!==``?e:`Not reported`;var s=_a(),c=R(s),l=R(c),u=R(l),d=B(u,!0),f=B(V(u),!0);M(l);var p=V(l,2),m=V(R(p)),h=B(m,!0);M(p),M(c);var g=V(c,2),_=R(g),v=V(R(_)),y=B(v,!0),b=V(v),x=e=>{J(e,vr(`(truncated)`))};X(b,e=>{t.ref.target_truncated&&e(x)});var S=V(b,3),C=B(S,!0);M(_);var w=V(_,2),T=e=>{J(e,sa())},ee=e=>{var t=yr();Z(z(t),17,n,e=>`${e.event_id}:${e.view_role}`,(e,t)=>{let n=P(()=>G(t).attributes??{});var i=ga(),a=R(i),s=B(a,!0),c=V(a,2),l=R(c),u=B(V(R(l)),!0);M(l);var d=V(l,2),f=B(V(R(d)),!0);M(d);var p=V(d,2),m=B(V(R(p)),!0);M(p);var h=V(p,2),g=e=>{var t=ca(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).vendor)),J(e,t)};X(h,e=>{G(n).vendor&&e(g)});var _=V(h,2),v=e=>{var t=la(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).os)),J(e,t)};X(_,e=>{G(n).os&&e(v)});var y=V(_,2),b=e=>{var t=ua(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).device)),J(e,t)};X(y,e=>{G(n).device&&e(b)}),M(c);var x=V(c,2),S=R(x),C=B(S),w=B(V(S));M(x);var T=V(x,2),ee=B(T,!0),te=V(T,2),E=e=>{J(e,da())};X(te,e=>{G(n).attributes_truncated&&e(E)});var D=V(te,2),ne=B(V(R(D)),!0);M(D);var re=V(D,2),ie=B(re),O=V(re,2),ae=e=>{var n=pa();Z(n,21,()=>G(t).ports,e=>`${e.proto}/${e.port}`,(e,t)=>{var n=fa(),r=B(n);H(()=>Y(r,`${G(t).proto??``}/${G(t).port??``} ${G(t).state??``}${G(t).service?` (${G(t).service})`:``}`)),J(e,n)}),M(n),J(e,n)};X(O,e=>{(G(t).ports??[]).length>0&&e(ae)});var oe=V(O,2),se=e=>{var n=ma(),i=B(V(R(n)));M(n),H((e,t,r)=>{Q(n,`href`,e),Y(i,`${t??``} · ${r??``}`)},[()=>`#/devices/${encodeURIComponent(G(t).profile_id)}`,()=>Ti(G(t).reference_class),()=>r().find(e=>e.profile_id===G(t).profile_id)?.role??G(t).view_role]),J(e,n)},ce=e=>{J(e,ha())};X(oe,e=>{G(t).profile_id?e(se):e(ce,-1)}),M(i),H((e,r,a,o,c)=>{Q(i,`aria-label`,`${G(t).view_role} view`),Y(s,G(t).view_role===`previous`?`Before the reported change`:G(t).view_role===`current`?`After the reported change`:`Reported host`),Y(u,e),Y(f,r),Y(m,a),Y(C,`Scanner-reported identity confidence: ${(G(n).identity_confidence||`unknown`)??``}`),Y(w,`Identity source: ${(G(n).identity_source||`unknown`)??``}`),Y(ee,o),Y(ne,c),Y(ie,`Snapshot covers ${G(t).ports_total??``} ports${G(t).ports_truncated?` (truncated)`:``}.`)},[()=>o(G(n).hostname),()=>o(G(n).ip),()=>o(G(n).mac),()=>ji(G(n).randomized),()=>JSON.stringify(G(n),null,2)]),J(e,i)}),J(e,t)};X(w,e=>{n().length===0?e(T):e(ee,-1)}),M(g),M(s),H(()=>{Q(s,`data-evidence`,`event:${t.ref.event_id}`),Q(s,`aria-label`,`Saved report ${t.ref.event_id}`),Y(d,t.ref.event_id),Y(f,t.ref.event_type),Q(m,`datetime`,t.ref.occurred_at),Q(m,`title`,G(i).utc),Y(h,G(i).local),Y(y,t.ref.target||`Not reported`),Q(S,`datetime`,t.ref.received_at),Q(S,`title`,G(a).utc),Y(C,G(a).local)}),J(e,s),Be()}var ya=q(` `),ba=q(`
        • Arguments and tokenized result
           
           
        • `);function xa(e,t){ze(t,!0);let n=P(()=>`step-${t.step.step}`),r=P(()=>t.step.summary??(t.step.outcome===`unknown_subject`?`The investigator could not resolve this lookup to a permitted reported subject; history remains unknown.`:t.step.status===`blocked`?`Redaction blocked this result; the lookup did not complete.`:t.step.status===`error`?`Tool execution failed; result unavailable.`:`No interpreted result is available.`));var i=ba(),a=R(i),o=B(a,!0),s=V(a,2),c=e=>{var n=ya(),r=B(n,!0);H(e=>Y(r,e),[()=>t.step.outcome.replaceAll(`_`,` `)]),J(e,n)};X(s,e=>{t.step.outcome&&e(c)});var l=V(s,2),u=B(l),d=V(l,2),f=B(d,!0),p=V(d,2),m=V(R(p),2),h=B(m),g=B(V(m,2));M(p),M(i),H(()=>{Q(i,`id`,G(n)),Q(i,`data-evidence`,`step:${t.step.step}`),Y(o,t.step.tool),Y(u,`step ${t.step.step??``} · ${t.step.status??``} · ${t.step.duration_ms??``} ms`),Y(f,G(r)),Y(h,`Args: ${t.step.args??``}`),Y(g,`Result: ${t.step.result??``}`)}),J(e,i),Be()}function Sa(e,t){if(!e)return!1;let n=Array.from(e.querySelectorAll(`[data-evidence]`)).find(e=>e.dataset.evidence===t);if(!n)return!1;for(let t=n;t&&t!==e;t=t.parentElement)t.tagName===`DETAILS`&&(t.open=!0);return n.scrollIntoView({block:`nearest`}),n.focus({preventScroll:!0}),!0}var Ca=q(`

          Briefing unavailable

          `),wa=q(`

          Loading briefing…

          `),Ta=q(`

          ← Back to Home

          `),Ea=q(`

          The saved view could not be refreshed. Your last loaded report is still shown.

          `),Da=q(`
          Reported host
          IP address
          MAC address
          `,1),Oa=q(`
          Reported identities
          See the individual report views below

          `),ka=q(`(truncated)`),Aa=q(` `),ja=q(``),Ma=q(``),Na=q(` `),Pa=q(`
          Copy for your investigation
          `),Fa=q(` `),Ia=q(`

          Suggestion adjusted to respect the baseline floor and one-step raise limit.

          `),La=q(`

          Deterministic fallback text: the model path did not produce this briefing.

          `),Ra=q(`

          No referenced events retained.

          `),za=q(`

          More referenced events exist than shown.

          `),Ba=q(`

          More observations exist than shown; some saved views are omitted.

          `),Va=q(`

          Possible explanation:

          `),Ha=q(`

          `),Ua=q(`

          Investigation has limits. Some lookups could not establish retained context. Missing lookup results do not prove this host has no history.

          `),Wa=q(``),Ga=q(`
        • `),Ka=q(`

          What the investigator could establish

            `,1),qa=q(``),Ja=q(`
            Cited evidence
            `),Ya=q(`
          • Open reported context
          • `),Xa=q(``),Za=q(`

            No profile links persisted for this briefing.

            `),Qa=q(`

            More profile links exist than shown.

            `),$a=q(`
              `),eo=q(`

              No agent trace is available for this briefing.

              `),to=q(`

              Model accounting

              `,1),no=q(`

              Reported scan target
              Analysis source

              Reported at the time shown. Network conditions may have changed since this report.

              Why this priority

              How the priority was computed
              1. Baseline floor
              2. Model suggestion
              3. Final priority

              Saved at report time

              Open a report to see what the scanner supplied, with its linked context.

              Technical details Tool calls, accounting and provenance

              Executed tool steps

              Provenance

              Briefing
              Group
              Grouping version
              Sequence
              Created
              Grouping anchor
              `);function ro(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=$(t,`embedded`,3,!1),i=$(t,`ontitle`,3,null),a=bi();ci(()=>a.invalidate());let o=I(null),s=I(null),c=I(null),l=I(null),u=0,d=P(()=>G(s)??n()),f=P(()=>G(d)===null?``:Di(G(d).kind,G(d).observations,G(d).referenced_events));un(()=>{if(G(d)!==null&&i()){let e=G(d).briefing_id,t=G(f);nr(()=>i()(e,t))}});async function p(){a.invalidate();let e=a.start();L(c,null);try{let n=await di(t.id);a.current(e)&&L(s,n,!0)}catch(t){a.current(e)&&L(c,t instanceof Error?t.message:String(t),!0)}}un(()=>{if(n()!==null)return;let e=t.id;a.invalidate();let r=a.start();L(c,null),di(e).then(e=>{a.current(r)&&L(s,e,!0)},e=>{a.current(r)&&L(c,e instanceof Error?e.message:String(e),!0)})});let m=P(()=>G(d)===null?null:Ci(G(d).created_at)),h=P(()=>G(d)===null?null:ki(G(d).referenced_events)),g=P(()=>G(d)===null?null:Ai(G(d).observations)),_=P(()=>G(d)!==null&&(G(d).clamped||G(d).baseline!==G(d).priority||(G(d).model_suggestion??G(d).baseline)!==G(d).priority)),v=P(()=>G(d)?.referenced_events.length===1?G(d).referenced_events[0]:null),y=P(()=>G(d)!==null&&!G(d).observations_truncated&&!G(d).referenced_truncated),b=P(()=>(G(d)?.traces??[]).filter(e=>[`unknown_subject`,`blocked`,`error`,`unavailable`,`truncated`].includes(e.outcome))),x=P(()=>{let e=[];for(let t of G(d)?.traces??[]){let n=t.summary||`step:${t.step}`,r=e.find(e=>e.key===n);r||(r={key:n,summary:t.summary,steps:[]},e.push(r)),r.steps.push(t.step)}return e});function S(e){Sa(G(o),e)}function C(e){return(G(d)?.observations??[]).filter(t=>t.event_id===e)}function w(e){if(Si(e)||e.metaKey||e.ctrlKey||e.altKey)return;let t={e:`expected`,s:`snooze`,x:`not_expected`}[e.key];t!==void 0&&G(d)!==null&&(u+=1,L(l,{kind:t,seq:u},!0))}si(()=>(window.addEventListener(`keydown`,w),()=>{window.removeEventListener(`keydown`,w)}));var T=yr(),ee=z(T),te=e=>{var t=Ca(),n=V(R(t),2),r=B(n),i=V(n,2);M(t),H(()=>Y(r,`The bridge did not return this briefing (${G(c)??``}).`)),K(`click`,i,p),J(e,t)},E=e=>{J(e,wa())},D=e=>{var t=no();let n;var i=R(t),a=e=>{J(e,Ta())};X(i,e=>{r()||e(a)});var s=V(i,2),u=e=>{var t=Ea(),n=V(R(t));M(t),K(`click`,n,p),J(e,t)};X(s,e=>{G(c)!==null&&e(u)});var w=V(s,2),T=R(w),ee=R(T),te=R(ee),E=B(te,!0),D=V(te,2),ne=B(D);M(ee);var re=V(ee,2),ie=B(re,!0),O=V(re,2),ae=R(O),oe=e=>{var t=Da(),n=z(t),r=B(V(R(n)),!0);M(n);var i=V(n,2),a=V(R(i)),o=R(a,!0),s=V(o),c=e=>{oa(e,{get value(){return G(g).ip},name:`IP address`})};X(s,e=>{G(g).ip!==null&&e(c)}),M(a),M(i);var l=V(i,2),u=V(R(l)),d=R(u,!0),f=V(d),p=e=>{oa(e,{get value(){return G(g).mac},name:`MAC address`})};X(f,e=>{G(g).mac!==null&&e(p)}),M(u),M(l),H(()=>{Y(r,G(g).hostname??`Not reported`),Y(o,G(g).ip??`Not reported`),Y(d,G(g).mac??`Not reported`)}),J(e,t)},se=e=>{var t=Oa(),n=V(R(t)),r=B(V(R(n)),!0);M(n),M(t),H(()=>Y(r,G(y)?`Grouped changes can describe different host views.`:`Only part of the retained evidence is shown.`)),J(e,t)};X(ae,e=>{G(g)!==null&&G(y)?e(oe):e(se,-1)});var ce=V(ae,2),le=V(R(ce)),ue=R(le,!0),de=V(ue),k=e=>{J(e,ka())};X(de,e=>{G(v)?.target_truncated&&e(k)}),M(le),M(ce);var fe=V(ce,2),pe=R(fe),me=B(pe,!0),he=V(pe),A=R(he),ge=e=>{var t=Aa(),n=B(t,!0);H(()=>{Q(t,`title`,G(h).utc),Y(n,G(h).local)}),J(e,t)},j=e=>{J(e,vr(`Not reported`))};X(A,e=>{G(h)===null?e(j,-1):e(ge)}),M(he),M(fe);var _e=V(fe,2),ve=B(V(R(_e)),!0);M(_e),M(O);var be=V(O,2),xe=R(be),Se=B(xe),Ce=V(xe,2),we=e=>{var t=ja();K(`click`,t,()=>S(`event:${G(v).event_id}`)),J(e,t)},Te=e=>{var t=Ma();K(`click`,t,()=>S(`reports`)),J(e,t)};X(Ce,e=>{G(v)?e(we):G(d).referenced_events.length>0&&e(Te,1)}),M(be),M(T);var Ee=V(T,4),De=V(R(Ee),2),Oe=B(De,!0),ke=V(De,2),Ae=e=>{var t=Pa(),n=V(R(t),2),r=e=>{var t=Na(),n=R(t);oa(V(n),{get value(){return G(g).ip},name:`IP address`}),M(t),H(()=>Y(n,`${G(g).ip??``} `)),J(e,t)};X(n,e=>{G(g).ip!==null&&e(r)});var i=V(n,2),a=e=>{var t=Na(),n=R(t);oa(V(n),{get value(){return G(g).mac},name:`MAC address`}),M(t),H(()=>Y(n,`${G(g).mac??``} `)),J(e,t)};X(i,e=>{G(g).mac!==null&&e(a)}),M(t),J(e,t)};X(ke,e=>{G(g)!==null&&G(y)&&(G(g).ip!==null||G(g).mac!==null)&&e(Ae)}),M(Ee);var je=V(Ee,2),Me=R(je);{let e=P(()=>G(d).decisions??[]),t=P(()=>G(d).effective_review??null);ra(Me,{subjectKind:`briefing`,get subjectId(){return G(d).briefing_id},kinds:[`expected`,`not_expected`,`snooze`,`note`],get decisions(){return G(e)},get effective(){return G(t)},get preset(){return G(l)},reload:p,requireChoice:!0})}M(je);var Ne=V(je,2),Pe=V(R(Ne),2),Fe=B(Pe,!0),Ie=V(Pe,2),Le=R(Ie),Re=B(Le),ze=V(Le),Be=e=>{var t=Fa(),n=B(t,!0);H(()=>Y(n,G(d).baseline_rule)),J(e,t)};X(ze,e=>{G(d).baseline_rule&&e(Be)}),M(Ie);var Ve=V(Ie,2),He=V(R(Ve),2),Ue=R(He),We=B(V(R(Ue)),!0);M(Ue);var Ge=V(Ue,2),Ke=B(V(R(Ge)),!0);M(Ge);var qe=V(Ge,2),N=B(V(R(qe)),!0);M(qe),M(He);var Je=V(He,2),Ye=e=>{J(e,Ia())};X(Je,e=>{G(d).clamped&&e(Ye)}),M(Ve);var Xe=V(Ve,2),Ze=e=>{J(e,La())};X(Xe,e=>{G(d).fallback_used&&e(Ze)}),M(Ne);var Qe=V(Ne,2),$e=R(Qe),et=B(R($e));ye(),M($e);var tt=V($e,4),nt=e=>{J(e,Ra())},rt=e=>{var t=yr();Z(z(t),17,()=>G(d).referenced_events,e=>e.event_id,(e,t)=>{{let n=P(()=>C(G(t).event_id));va(e,{get ref(){return G(t)},get views(){return G(n)},get profileLinks(){return G(d).profiles}})}}),J(e,t)};X(tt,e=>{G(d).referenced_events.length===0?e(nt):e(rt,-1)});var it=V(tt,2),at=e=>{J(e,za())};X(it,e=>{G(d).referenced_truncated&&e(at)});var ot=V(it,2),st=e=>{J(e,Ba())};X(ot,e=>{G(d).observations_truncated&&e(st)}),M(Qe);var ct=V(Qe,2),lt=R(ct),ut=B(lt,!0),dt=V(lt,2),ft=e=>{var t=Va(),n=V(R(t));M(t),H(()=>Y(n,` ${G(d).likely_explanation??``}`)),J(e,t)};X(dt,e=>{G(d).likely_explanation&&e(ft)});var pt=V(dt,2),mt=e=>{var t=Ha(),n=B(R(t));M(t),H(()=>Y(n,`Explanation confidence (model self-assessment): ${G(d).confidence??``}`)),J(e,t)};X(pt,e=>{G(d).confidence&&e(mt)});var F=V(pt,2),ht=e=>{J(e,Ua())};X(F,e=>{G(b).length>0&&e(ht)});var gt=V(F,2),_t=B(gt,!0),vt=V(gt,2),yt=e=>{var t=Ka(),n=V(z(t),2);Z(n,21,()=>G(x),jr,(e,t)=>{var n=Ga(),r=V(R(n)),i=R(r),a=B(i,!0),o=V(i);Z(o,20,()=>G(t).steps,e=>e,(e,t)=>{var n=Wa(),r=B(n);H(()=>Y(r,`View step ${t??``}`)),K(`click`,n,()=>S(`step:${t}`)),J(e,n)}),M(o),M(r),M(n),H(()=>Y(a,G(t).summary||`No interpreted result is available. Open the tool step to inspect the saved response.`)),J(e,n)}),M(n),J(e,t)};X(vt,e=>{G(d).traces?.length&&e(yt)});var bt=V(vt,2),xt=e=>{var t=Ja();Z(V(R(t),2),19,()=>G(d).evidence_refs,(e,t)=>`${e.event_id??e.tool_step}:${t}`,(e,t)=>{var n=qa(),r=B(n,!0);H(()=>Y(r,G(t).event_id?`Event ${G(t).event_id}`:`Tool step ${G(t).tool_step}`)),K(`click`,n,()=>S(G(t).event_id?`event:${G(t).event_id}`:`step:${G(t).tool_step}`)),J(e,n)}),M(t),J(e,t)};X(bt,e=>{G(d).evidence_refs?.length&&e(xt)});var St=V(bt,2),Ct=e=>{var t=Xa(),n=V(R(t));Z(n,21,()=>G(d).profiles,e=>`${e.profile_id}:${e.role}`,(e,t)=>{var n=Ya(),r=R(n),i=B(V(r,2));M(n),H(e=>{Q(r,`href`,e),Y(i,`${G(t).profile_id??``} (${G(t).role??``})`)},[()=>`#/devices/${encodeURIComponent(G(t).profile_id)}`]),J(e,n)}),M(n),M(t),J(e,t)},wt=e=>{J(e,Za())};X(St,e=>{G(d).profiles.length>0?e(Ct):e(wt,-1)});var Tt=V(St,2),Et=e=>{J(e,Qa())};X(Tt,e=>{G(d).profiles_truncated&&e(Et)}),M(ct);var Dt=V(ct,2),Ot=V(R(Dt),2),kt=V(R(Ot),2),At=e=>{var t=$a();Z(t,21,()=>G(d).traces,e=>e.step,(e,t)=>{xa(e,{get step(){return G(t)}})}),M(t),J(e,t)},jt=e=>{J(e,eo())};X(kt,e=>{G(d).traces?.length?e(At):e(jt,-1)});var Mt=V(kt,2),Nt=e=>{var t=to(),n=B(V(z(t)));H(()=>Y(n,`${G(d).input_tokens??`Unknown`??``} input tokens · ${G(d).output_tokens??`Unknown`??``} output tokens · ${G(d).cost_usd==null?`Cost unknown`:`$${G(d).cost_usd}`}`)),J(e,t)};X(Mt,e=>{(G(d).input_tokens!=null||G(d).output_tokens!=null||G(d).cost_usd!=null)&&e(Nt)});var Pt=V(Mt,3),Ft=V(R(Pt)),I=B(Ft,!0),It=V(Ft,2),Lt=B(It,!0),Rt=V(It,3),zt=B(Rt,!0),Bt=V(Rt,2),Vt=B(Bt,!0),Ht=V(Bt,3),Ut=R(Ht),Wt=B(Ut,!0);M(Ht);var Gt=B(V(Ht,3),!0);M(Pt),M(Ot),M(Dt),M(w),M(t),ii(t,e=>L(o,e),()=>G(o)),H(e=>{n=Hr(t,1,`briefing dossier svelte-90l7ri`,null,n,{embedded:r()}),Y(E,G(f)),Q(D,`data-priority`,G(d).priority),Q(D,`aria-label`,`Priority: ${G(d).priority}`),Y(ne,`${G(d).priority??``} priority`),Y(ie,G(d).what_changed),Y(ue,G(v)?.target||(G(d).referenced_events.length>1?`Shown per report below`:`Not reported`)),Y(me,G(h)?.partial||G(d).referenced_truncated?`Available report times`:G(d).referenced_events.length>1?`Report time range`:`Report time`),Y(ve,e),Y(Se,`${G(d).referenced_events.length??``}${G(d).referenced_truncated?`+`:``} source report${G(d).referenced_events.length===1?``:`s`}`),Y(Oe,G(d).recommended_check),Y(Fe,G(d).priority_reason),Q(Le,`data-priority`,G(d).priority),Y(Re,`Final priority: ${G(d).priority??``}`),Ve.open=G(_),Y(We,G(d).baseline),Y(Ke,G(d).model_suggestion??`No model suggestion — baseline stands`),Y(N,G(d).priority),Y(et,`Scanner snapshot${G(d).referenced_events.length===1?``:`s`}`),Y(ut,G(d).source===`agent`?`Investigator notes`:`Analysis and retained context`),Y(_t,G(d).context),Y(I,G(d).briefing_id),Y(Lt,G(d).group_id),Y(zt,G(d).grouping_version),Y(Vt,G(d).seq),Q(Ut,`datetime`,G(d).created_at),Q(Ut,`title`,G(m)?.utc),Y(Wt,G(m)?.local),Y(Gt,G(d).host)},[()=>wi(G(d).source)]),J(e,t)};X(ee,e=>{G(c)!==null&&G(d)===null?e(te):G(d)===null?e(E,1):e(D,-1)}),J(e,T),Be()}ur([`click`]);var io=q(`

              `),ao=q(`

              Loading incident…

              `),oo=q(`Closed `),so=q(`Open since `),co=q(`

              Some members share identity evidence; only distinct qualified references count toward the burst threshold.

              `),lo=q(`

              More members exist than shown.

              `),uo=q(`

              No member evidence retained.

              `),fo=q(`
            1. `),po=q(`
                `),mo=q(`

                `),ho=q(`

                Hypothesis:

                `),go=q(`
              1. `),_o=q(`

                Cited reports:

                  `,1),vo=q(`

                  Summary

                  `),yo=q(`

                  Scope

                  Window start: · Last member:

                  Evidence

                  `,1),bo=q(`

                  Back to overview

                  `,1);function xo(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=bi();ci(()=>{r.invalidate(),typeof window<`u`&&window.removeEventListener(`watcherbridge:incident`,c)});let i=I(null),a=I(null),o=P(()=>G(i)??n());async function s(){r.invalidate();let e=r.start();L(a,null);try{let n=await yi(t.id);r.current(e)&&L(i,n,!0)}catch(t){r.current(e)&&L(a,t instanceof Error?t.message:String(t),!0)}}function c(){s()}si(()=>{n()===null&&s(),window.addEventListener(`watcherbridge:incident`,c)});let l={burst_appearance:`Burst appearance`,burst_disappearance:`Burst disappearance`},u={expected:`Mark reviewed: this incident needs no further action. Members stay untouched unless included below.`,not_expected:`Flag for follow-up: this incident still needs a check.`,snooze:`Hide until a time. Empty keeps the server default (24h).`,note:`Free-text note, kept with the audit (max 2000 characters).`};function d(e){return`${`${e.member_count} member report${e.member_count===1?``:`s`}`} · ${`${e.qualified_refs} qualified upstream reference${e.qualified_refs===1?``:`s`}`}`}var f=bo(),p=V(z(f),2),m=e=>{var t=io(),n=R(t),r=V(n);M(t),H(()=>Y(n,`Refresh failed: ${G(a)??``} `)),K(`click`,r,c),J(e,t)};X(p,e=>{G(a)!==null&&e(m)});var h=V(p,2),g=e=>{J(e,ao())},_=e=>{let t=P(()=>G(o).incident),n=P(()=>Ci(G(t).opened_at)),r=P(()=>Ci(G(t).closed_at)),i=P(()=>Ci(G(t).window_start)),a=P(()=>Ci(G(t).last_member_at));var c=yo(),f=z(c),p=B(f,!0),m=V(f,2),h=R(m),g=B(h,!0),_=V(h,2),v=B(_),y=V(_,2),b=e=>{var n=oo(),i=V(R(n)),a=B(i,!0);M(n),H(()=>{Q(i,`datetime`,G(t).closed_at),Q(i,`title`,G(r).utc),Y(a,G(r).local)}),J(e,n)},x=e=>{var r=so(),i=V(R(r)),a=B(i,!0);M(r),H(()=>{Q(i,`datetime`,G(t).opened_at),Q(i,`title`,G(n).utc),Y(a,G(n).local)}),J(e,r)};X(y,e=>{G(t).status===`closed`&&G(t).closed_at?e(b):e(x,-1)}),M(m);var S=V(m,2),C=V(R(S),2),w=B(C),T=V(C,2),ee=B(T),te=V(T,2),E=V(R(te)),D=B(E,!0),ne=V(E,2),re=B(ne,!0);M(te),M(S);var ie=V(S,2),O=V(R(ie),2),ae=B(O,!0),oe=V(O,2),se=e=>{J(e,co())};X(oe,e=>{G(t).qualified_refs{J(e,lo())};X(ce,e=>{G(o).truncated&&e(le)});var ue=V(ce,2),de=e=>{J(e,uo())},k=e=>{var t=po();Z(t,21,()=>G(o).members,e=>e.briefing_id,(e,t)=>{let n=P(()=>Ci(G(t).created_at));var r=fo(),i=R(r),a=B(i,!0),o=V(i,2),s=B(o,!0),c=V(o,2),l=B(c,!0),u=B(V(c,2));M(r),H(()=>{Q(i,`href`,`#/briefing/${G(t).briefing_id}`),Y(a,G(t).briefing_id),Y(s,G(t).priority),Q(c,`datetime`,G(t).created_at),Q(c,`title`,G(n).utc),Y(l,G(n).local),Y(u,`${G(t).refs.length??``} qualified reference${G(t).refs.length===1?``:`s`}`)}),J(e,r)}),M(t),J(e,t)};X(ue,e=>{G(o).members.length===0?e(de):e(k,-1)}),M(ie);var fe=V(ie,2),pe=e=>{let t=P(()=>G(o).summary);var n=vo(),r=V(R(n),2),i=B(R(r));M(r);var a=V(r,2),s=e=>{var n=mo(),r=B(n,!0);H(()=>Y(r,G(t).summary)),J(e,n)};X(a,e=>{G(t).summary&&e(s)});var c=V(a,2),l=e=>{var n=ho(),r=V(R(n));M(n),H(()=>Y(r,` ${G(t).hypothesis??``}`)),J(e,n)};X(c,e=>{G(t).hypothesis&&e(l)});var u=V(c,2),d=e=>{var n=mo(),r=B(n);H(()=>Y(r,`Display confidence: ${G(t).confidence??``}`)),J(e,n)};X(u,e=>{G(t).confidence&&e(d)});var f=V(u,2),p=B(f),m=V(f,2),h=e=>{var n=mo(),r=B(n);H(()=>Y(r,`Priority explanation: ${G(t).reason??``}`)),J(e,n)};X(m,e=>{G(t).reason&&e(h)});var g=V(m,2),_=B(g),v=V(g,2),y=e=>{var n=_o(),r=V(z(n),2);Z(r,21,()=>G(t).citations,e=>e.member,(e,t)=>{var n=go(),r=R(n),i=V(r),a=B(i,!0);M(n),H(()=>{Y(r,`${G(t).member??``}: `),Q(i,`href`,`#/briefing/${G(t).briefing_id}`),Y(a,G(t).briefing_id)}),J(e,n)}),M(r),J(e,n)};X(v,e=>{G(t).citations.length&&e(y)});var b=V(v,2),x=e=>{var n=mo(),r=B(n,!0);H(()=>Y(r,G(t).error)),J(e,n)};X(b,e=>{G(t).error&&e(x)}),M(n),H(()=>{Y(i,`Summary status: ${G(t).status??``}`),Y(p,`Priority floor: ${G(t).floor??``}. Resolved priority: ${G(t).final_priority??``}${G(t).suggestion?` (suggested ${G(t).suggestion}${G(t).clamped?`, clamped`:``})`:` (no model raise)`}`),Y(_,`Evidence coverage: ${G(t).sampled??``} sampled member reports; ${G(t).omitted??``} omitted. The sample may not represent every member.`)}),J(e,n)};X(fe,e=>{G(o).summary&&e(pe)});var me=V(fe,2);{let e=P(()=>G(o).decisions??[]),n=P(()=>G(o).effective_review??null);ra(me,{subjectKind:`incident`,get subjectId(){return G(t).incident_id},kinds:[`expected`,`not_expected`,`snooze`,`note`],get decisions(){return G(e)},get effective(){return G(n)},memberScope:!0,get memberCount(){return G(t).member_count},get kindHelp(){return u},reload:s})}H(e=>{Y(p,l[G(t).kind]??G(t).kind),Y(g,G(t).status),Y(v,`Floor: ${G(t).floor??``}`),Y(w,`Source: ${G(t).source_scope??``}`),Y(ee,`Target: ${G(t).target??``}`),Q(E,`datetime`,G(t).window_start),Q(E,`title`,G(i).utc),Y(D,G(i).local),Q(ne,`datetime`,G(t).last_member_at),Q(ne,`title`,G(a).utc),Y(re,G(a).local),Y(ae,e)},[()=>d(G(t))]),J(e,c)};X(h,e=>{G(o)===null?e(g):e(_,-1)}),J(e,f),Be()}ur([`click`]);var So=q(`
                • `),Co=q(`
                    `),wo=q(`, last +\r\f\xA0\v`];function Vr(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Br.includes(r[o-1]))&&(s===r.length||Br.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Hr(e,t,n,r,i,a){var o=e[ae];if(A||o!==n||o===void 0){var s=Vr(n,r,a);A&&s===e.getAttribute(`class`)||(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[ae]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}var Ur=Symbol(`is custom element`),Wr=Symbol(`is html`),Gr=ue?`link`:`LINK`,Kr=ue?`progress`:`PROGRESS`;function qr(e){if(A){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[ce]=n,Ge(n),Qe()}}function Jr(e,t){var n=Xr(e);n.value!==(n.value=t??void 0)&&(e.value!==t||t===0&&e.nodeName===Kr)&&(e.value=t??``)}function Yr(e,t){var n=Xr(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=Xr(e);if(!(A&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===Gr))){var a=i[t];a!==(i[t]=n)&&(t===`loading`&&(e[ie]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Qr(e).has(t)?e[t]=n:(!A||typeof n!=`number`&&typeof n!=`boolean`||a!==String(n))&&e.setAttribute(t,n))}}function Xr(e){return e[O]??={[Ur]:e.nodeName.includes(`-`),[Wr]:e.namespaceURI===fe}}var Zr=new Map;function Qr(e){var t=e.getAttribute(`is`)||e.nodeName,n=Zr.get(t);if(n)return n;Zr.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.add(s);i=l(i)}return n}function $r(e,t,n=t){var r=new WeakSet;et(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=ti(e)?ni(a):a,n(a),F!==null&&r.add(F),await $n(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(A&&e.defaultValue!==e.value||nr(t)==null&&e.value)&&(n(ti(e)?ni(e.value):e.value),F!==null&&r.add(F)),hn(()=>{var n=t();if(e===document.activeElement){var i=F;if(r.has(i))return}ti(e)&&n===ni(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}function ei(e,t,n=t){et(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(A&&e.defaultChecked!==e.checked||nr(t)==null)&&n(e.checked),hn(()=>{e.checked=!!t()})}function ti(e){var t=e.type;return t===`number`||t===`range`}function ni(e){return e===``?null:+e}function ri(e,t){return e===t||e?.[D]===t}function ii(e=Ve(),t,n,r){var i=Le.r,a=W;return pn(()=>{var o,s;return hn(()=>{o=s,s=r?.()||[],nr(()=>{ri(n(...s),e)||(t(e,...s),o&&ri(n(...o),e)&&t(null,...o))})}),()=>{let r=a;for(;r!==i&&r.parent!==null&&r.parent.f&33554432;)r=r.parent;let o=()=>{s&&ri(n(...s),e)&&t(null,...s)},c=r.teardown;r.teardown=()=>{o(),c?.()}}}),e}var ai=!1;function oi(e){var t=ai;try{return ai=!1,[e(),ai]}finally{ai=t}}function $(e,t,n,r){var i=!0,o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=at(r),G(u)):(l&&(l=!1,c=s?nr(r):r),c);let f;if(o){var p=D in e||re in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=oi(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Me(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?at:ct)(()=>(v=!1,g()));o&&G(y);var b=W;return(function(e,t){if(arguments.length>0){let n=t?G(y):i&&o?Ut(e):e;return L(y,n),v=!0,c!==void 0&&(c=n),e}return An&&v||b.f&16384?y.v:G(y)})}function si(e){Le===null&&Te(`onMount`),un(()=>{let t=nr(e);if(typeof t==`function`)return t})}function ci(e){Le===null&&Te(`onDestroy`),si(()=>()=>nr(e))}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);async function li(){let e=await fetch(`/api/overview`);if(!e.ok)throw Error(`overview ${e.status}`);return e.json()}async function ui(e,t){let n=await fetch(e);if(!n.ok)throw Error(`${t} ${n.status}`);return n.json()}function di(e){return ui(`/api/briefing?id=${encodeURIComponent(e)}`,`briefing`)}async function fi(e){let t={subject_kind:e.subjectKind,subject_id:e.subjectId,kind:e.kind,request_id:e.requestId};e.until!==void 0&&(t.until=e.until),e.text!==void 0&&(t.text=e.text),e.includeMembers!==void 0&&(t.include_members=e.includeMembers);let n=await fetch(`/api/decisions`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(`decision ${n.status}: ${await n.text()}`);return n.json()}function pi(e,t,n={}){let r=new URLSearchParams({subject_kind:e,subject_id:t});return n.limit!==void 0&&r.set(`limit`,String(n.limit)),n.before!==void 0&&r.set(`before`,n.before),ui(`/api/decisions?${r.toString()}`,`decisions`)}async function mi(e,t){let n=await fetch(`/api/decisions/${encodeURIComponent(e)}/undo`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({request_id:t})});if(!n.ok)throw Error(`undo ${n.status}: ${await n.text()}`);return n.json()}function hi(e,t){let n=new URLSearchParams({after:String(t),limit:`200`});return ui(`/api/decisions/${encodeURIComponent(e)}/members?${n}`,`member decisions`)}function gi(e=``){return ui(`/api/devices${e===``?``:`?after=${encodeURIComponent(e)}`}`,`devices`)}function _i(e,t=``){let n=t===``?``:`?history_after=${encodeURIComponent(t)}`;return ui(`/api/devices/${encodeURIComponent(e)}${n}`,`device`)}function vi(){return ui(`/api/health`,`health`)}function yi(e){return ui(`/api/incidents/${encodeURIComponent(e)}`,`incident`)}function bi(){let e=0,t=0;return{start(){return e+=1,{id:e,stream:t}},streamed(){t+=1},invalidate(){e+=1},current(n){return n.id===e&&n.stream===t}}}function xi(e){let t=document.activeElement,n=t?.closest?.(`[data-rows]`);if(!n)return!1;let r=[...n.querySelectorAll(`[data-row]`)];if(r.length===0)return!1;let i=r.indexOf(t);return i===-1&&(i=e===1?-1:0),(r[i+e]??(e===1?r[0]:r[r.length-1])).focus(),!0}function Si(e){let t=e.target;if(t===null||t.isContentEditable)return!0;let n=t.tagName;return n===`INPUT`||n===`TEXTAREA`||n===`SELECT`}function Ci(e){let t=new Date(e??``);return Number.isNaN(t.getTime())?{local:`unknown`,utc:`unknown`}:{local:t.toLocaleString(),utc:t.toISOString()}}function wi(e){switch(e){case`rules`:return`Deterministic rules`;case`model`:return`Model suggestion`;case`agent`:return`Investigator agent`;case`fallback`:return`Deterministic fallback`;case`legacy`:return`Earlier version (source unknown)`;default:return`Unknown source`}}function Ti(e){switch(e){case`reported_device`:return`Reported device`;case`address`:return`Address context — not a device identity`;case`event`:return`Unresolved events — not a device identity`;case`legacy`:return`Legacy profile`;default:return`Unknown class`}}function Ei(e){return{weak_appearance:`Weak sightings`,weak_disappearance:`Weak disappearances`,port_closed:`Ports closed`,hostname_change:`Hostname changes`,os_refinement:`OS refinements`,other:`Other changes`}[e]??e}function Di(e,t=[],n=[]){if(e!==`other`)return Ei(e);let r=new Map([[`low`,0],[`medium`,1],[`high`,2]]),i=new Map;for(let e of t)e.view_role===`previous`&&e.event_id&&i.set(e.event_id,r.get(e.attributes?.identity_confidence));for(let e of t){if(e.view_role!==`current`)continue;let t=i.get(e.event_id),n=r.get(e.attributes?.identity_confidence);if(t!==void 0&&n!==void 0&&ne.event_type));if(a.size===1){if(a.has(`host.appeared`))return`New host reported`;if(a.has(`host.disappeared`))return`Host disappearance reported`;if(a.has(`host.changed`))return`Reported host details changed`}return a.size>1?`Reported network changes`:`Reported details changed`}function Oi(e){let t=/^(\d+)h0m0s$/.exec(e);return t?`${t[1]} hours`:e}function ki(e=[]){let t=(e??[]).map(e=>new Date(e.occurred_at??``)).filter(e=>!Number.isNaN(e.getTime())).sort((e,t)=>e-t);if(t.length===0)return null;let n=t.length!==e.length,r=Ci(t[0].toISOString());if(t.length===1)return{...r,partial:n};let i=Ci(t[t.length-1].toISOString());return{local:`${r.local} – ${i.local}`,utc:`${r.utc} – ${i.utc}`,partial:n}}function Ai(e=[]){if(e.length!==1)return null;let t=(e??[]).filter(e=>e.view_role===`host`||e.view_role===`current`),n=new Set(t.map(e=>e.event_id));if(t.length!==1||n.size!==1)return null;let r=t[0].attributes??{},i=e=>typeof e==`string`&&e!==``?e:null;return{event_id:t[0].event_id,view_role:t[0].view_role,hostname:i(r.hostname),ip:i(r.ip),mac:i(r.mac),randomized:typeof r.randomized==`boolean`?r.randomized:null,identity_confidence:i(r.identity_confidence)??`unknown`,identity_source:i(r.identity_source)??`unknown`}}function ji(e){return e===!0?`Randomized (scanner-reported)`:e===!1?`Not randomized (scanner-reported)`:`Randomization unknown`}function Mi(e,t){return{parentId:e,count:Array.isArray(t.generated_decision_ids)?t.generated_decision_ids.length:0,next:t.generated_next??0,members:[]}}function Ni(e){return`Undone. ${e.next?`At least ${e.count}`:String(e.count)} member decision${e.count===1?` was`:`s were`} generated; incident undo does not undo them. Review them on their briefing pages.`}async function Pi(e,t){let n=await t(e.parentId,e.next),r=n.next??0;if(r!==0&&r<=e.next)throw Error(`Member decision page did not advance. Try again.`);return{parentId:e.parentId,count:e.count+n.members.length,next:r,members:n.members}}var Fi=q(` `),Ii=q(``),Li=q(``),Ri=q(`

                    `),zi=q(``),Bi=q(``),Vi=q(`

                    `),Hi=q(`
                  • `),Ui=q(`
                      `),Wi=q(``),Gi=q(`

                      `,1),Ki=q(`

                      Recorded decisions

                      `),qi=q(`

                      No decisions recorded.

                      `),Ji=q(`undone`),Yi=q(` `),Xi=q(` `),Zi=q(``),Qi=q(`
                    • `),$i=q(``),ea=q(`

                      `),ta=q(`
                        `,1),na=q(`

                        Choose a decision

                        `);function ra(e,t){ze(t,!0);let n=$(t,`kinds`,19,()=>[]),r=$(t,`decisions`,19,()=>[]),i=$(t,`effective`,3,null),a=$(t,`preset`,3,null),o=$(t,`reload`,3,async()=>{}),s=$(t,`memberScope`,3,!1),c=$(t,`memberCount`,3,0),l=$(t,`kindHelp`,19,()=>({})),u=$(t,`requireChoice`,3,!1),d={expected:`No further action needed.`,not_expected:`Flag for follow-up: this briefing still needs a check.`,snooze:`Hide from attention until a time. Empty keeps the server default (24h).`,note:`Free-text note, kept with the audit (max 2000 characters).`,label:`Your label for this profile, never identity (max 256 characters).`},f=I(null),p=P(()=>u()?G(f):G(f)??n()[0]??`note`),m=I(``),h=I(``),g=I(!1),_=I(null),v=I(null),y=I(null),b=I(Ut(crypto.randomUUID())),x=I(!1),S=I(null),C=I(null),w=I(!1),T=I(null),ee=I(null),te=I(Ut({})),E=!0;ci(()=>{E=!1});let D=0,ne=P(()=>r().length===0?`empty`:`${r()[0].decision_id}:${r().length}`),re=I(Ut([])),ie=I(``),O=I(Ut([])),ae=I(!1),oe=I(!1),se=I(``),ce=I(null),le=I(``),ue=P(()=>G(ie)===G(ne)&&G(re).length>0?[...r(),...G(re)]:r()),de=P(()=>G(oe)&&G(se)===G(ne)),k=P(()=>G(le)===G(ne)?G(ce):null),fe=P(()=>G(p)===`note`||G(p)===`label`),pe=P(()=>G(p)===`label`?256:2e3),me=P(()=>G(p)!==null&&n().includes(G(p))&&(!G(fe)||G(h).trim()!==``));un(()=>{a()!==null&&n().includes(a().kind)&&(L(f,a().kind,!0),G(ee)?.focus())});function he(){return i()===null||i()===void 0?`No operator review`:i().kind===`snooze`?`Snoozed${i().until?` until ${i().until}`:``}`:i().kind===`expected`?`Reviewed: expected`:`Flagged: ${i().kind.replace(`_`,` `)}`}async function A(e){if(e.preventDefault(),!G(g)&&(!u()||G(me))&&G(p)!==null){L(g,!0),L(v,null);try{let e={subjectKind:t.subjectKind,subjectId:t.subjectId,kind:G(p),requestId:G(b)};if(G(p)===`snooze`&&G(m)!==``){let t=new Date(G(m));if(Number.isNaN(t.getTime()))throw Error(`snooze time is not a date`);e.until=t.toISOString()}G(fe)&&(e.text=G(h)),s()&&(e.includeMembers=G(x));let n=await fi(e);L(b,crypto.randomUUID(),!0),L(f,null),L(m,``),L(h,``),L(x,!1),L(S,null),L(C,null),L(T,null),L(re,[],!0),L(ie,``),L(O,[],!0),L(oe,!1),L(ce,null),D+=1,n.applied===!1?L(v,`Already recorded (${n.reason??`duplicate`}).`):Array.isArray(n.members)&&n.members.length>0&&L(S,`Recorded on the incident and ${n.members.length} member briefing${n.members.length===1?``:`s`}.`),await o()()}catch(e){L(v,e instanceof Error?e.message:String(e),!0)}finally{L(g,!1)}}}async function ge(e){if(G(_)===null){L(_,e,!0),L(y,null),L(S,null),L(C,null),L(T,null);try{G(te)[e]??=crypto.randomUUID();let t=await mi(e,G(te)[e]);((t.generated_decision_ids?.length??0)>0||t.generated_next)&&L(C,Mi(e,t),!0);let n={...G(te)};delete n[e],L(te,n,!0),L(re,[],!0),L(ie,``),L(O,[],!0),L(oe,!1),L(ce,null),D+=1,await o()()}catch(e){L(y,e instanceof Error?e.message:String(e),!0)}finally{L(_,null)}}}async function j(){if(G(w)||!G(C)?.next)return;let e=G(C);L(w,!0),L(T,null);try{let t=await Pi(e,hi);E&&G(C)===e&&L(C,t,!0)}catch(t){E&&G(C)===e&&L(T,t instanceof Error?t.message:String(t),!0)}finally{E&&L(w,!1)}}async function _e(){if(G(ae)||G(de)||G(ue).length===0)return;let e=D,n=G(ne),r=G(ue)[G(ue).length-1].cursor;L(ae,!0),L(y,null);try{let i=await pi(t.subjectKind,t.subjectId,{limit:50,before:r});if(!E||e!==D||n!==G(ne))return;if(i.length===0){L(oe,!0),L(se,n,!0);return}G(ie)===n&&G(re).length>0&&(G(O).push({rows:G(re),key:G(ie)}),G(O).length>4&&(G(O).shift(),L(ce,`Earlier pages dropped from this view; the full audit remains available through the API.`),L(le,n,!0))),L(re,i,!0),L(ie,n,!0),L(oe,i.length<50),L(se,n,!0)}catch(t){if(!E||e!==D||n!==G(ne))return;L(y,t instanceof Error?t.message:String(t),!0)}finally{E&&L(ae,!1)}}function ve(){for(;G(O).length>0&&G(O)[G(O).length-1].key!==G(ne);)G(O).pop();let e=G(O).pop();e===void 0?(L(re,[],!0),L(ie,``)):(L(re,e.rows,!0),L(ie,e.key,!0)),L(oe,!1),L(se,G(ne),!0)}var ye=na();let be;var xe=R(ye),Se=B(xe,!0),Ce=V(xe,2),we=R(Ce),Te=B(we);M(Ce);var Ee=V(Ce,2),De=R(Ee),Oe=R(De);let ke;Z(V(Oe,2),16,n,e=>e,(e,n)=>{var r=Ii();let i;var a=R(r);qr(a);var o=V(a,2),s=B(o,!0),c=V(o,2),l=e=>{var t=Fi(),r=B(t,!0);H(()=>Y(r,{expected:`no further action`,not_expected:`investigate`,snooze:`hide until later`,note:`annotation only`}[n])),J(e,t)};X(c,e=>{u()&&e(l)}),M(r),H(e=>{i=Hr(r,1,`svelte-38cs4v`,null,i,{selected:G(p)===n}),Q(a,`name`,`decision-kind-${t.subjectId}`),Jr(a,n),Yr(a,G(p)===n),Y(s,e)},[()=>n===`not_expected`?`Not expected`:n.charAt(0).toUpperCase()+n.slice(1)]),K(`change`,a,()=>L(f,n,!0)),J(e,r)}),M(De);var Ae=V(De,2),je=B(Ae,!0),Me=V(Ae,2),Ne=e=>{var t=Li(),n=R(t);qr(n);var r=V(n);M(t),H(()=>Y(r,` Also apply to the ${c()??``} member briefing${c()===1?``:`s`} (snapshot now; later joiners stay untouched)`)),ei(n,()=>G(x),e=>L(x,e)),J(e,t)};X(Me,e=>{s()&&e(Ne)});var Pe=V(Me,2),Fe=e=>{var t=Ri(),n=B(t,!0);H(()=>Y(n,G(S))),J(e,t)};X(Pe,e=>{G(S)!==null&&e(Fe)});var Ie=V(Pe,2),Le=e=>{var t=zi(),n=V(R(t));qr(n),M(t),$r(n,()=>G(m),e=>L(m,e)),J(e,t)};X(Ie,e=>{G(p)===`snooze`&&e(Le)});var Re=V(Ie,2),Ve=e=>{var t=Bi(),n=R(t),r=V(n);Xe(r),M(t),H(()=>{Y(n,`${G(p)===`label`?`Label`:`Note`} `),Q(r,`maxlength`,G(pe))}),$r(r,()=>G(h),e=>L(h,e)),J(e,t)};X(Re,e=>{G(fe)&&e(Ve)});var He=V(Re,2),Ue=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(v))),J(e,t)};X(He,e=>{G(v)!==null&&e(Ue)});var We=V(He,2),Ge=B(We,!0);ii(We,e=>L(ee,e),()=>G(ee)),M(Ee);var Ke=V(Ee,2),qe=e=>{var t=Gi(),n=z(t),r=B(n,!0),i=V(n,2),a=e=>{var t=Ui();Z(t,21,()=>G(C).members,e=>e.decision_id,(e,t)=>{var n=Hi(),r=R(n),i=B(r,!0),a=V(r);M(n),H((e,n)=>{Q(r,`href`,e),Y(i,G(t).subject_id),Y(a,` — ${n??``}`)},[()=>`#/briefing/${encodeURIComponent(G(t).subject_id)}`,()=>G(t).kind.replace(`_`,` `)]),J(e,n)}),M(t),J(e,t)};X(i,e=>{G(C).members.length>0&&e(a)});var o=V(i,2),s=e=>{var t=Wi(),n=B(t,!0);H(()=>{t.disabled=G(w),Y(n,G(w)?`Loading…`:G(T)?`Retry remaining member decisions`:`Show remaining member decisions`)}),K(`click`,t,j),J(e,t)};X(o,e=>{G(C).next&&e(s)});var c=V(o,2),l=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(T))),J(e,t)};X(c,e=>{G(T)!==null&&e(l)}),H(e=>Y(r,e),[()=>Ni(G(C))]),J(e,t)};X(Ke,e=>{G(C)!==null&&e(qe)});var N=V(Ke,2),Je=e=>{J(e,Ki())};X(N,e=>{u()&&e(Je)});var Ye=V(N,2),Ze=e=>{J(e,qi())},Qe=e=>{var t=ta(),n=z(t);Z(n,21,()=>G(ue),e=>e.decision_id,(e,t)=>{var n=Qi(),r=R(n),i=B(r,!0),a=V(r,2),o=e=>{J(e,Ji())};X(a,e=>{G(t).undone&&e(o)});var s=V(a,2),c=e=>{var n=Yi(),r=B(n,!0);H(()=>Y(r,G(t).text)),J(e,n)};X(s,e=>{G(t).text&&e(c)});var l=V(s,2),u=e=>{var n=Xi(),r=B(n);H(()=>Y(r,`until ${G(t).until??``}`)),J(e,n)};X(l,e=>{G(t).until&&e(u)});var d=V(l,2),f=B(d,!0),p=V(d,2),m=e=>{var n=Zi(),r=B(n,!0);H(()=>{n.disabled=G(_)!==null,Y(r,G(_)===G(t).decision_id?`Undoing…`:`Undo`)}),K(`click`,n,()=>ge(G(t).decision_id)),J(e,n)};X(p,e=>{!G(t).undone&&G(t).kind!==`undo`&&e(m)}),M(n),H((e,n)=>{Y(i,e),Q(d,`datetime`,G(t).created_at),Y(f,n)},[()=>G(t).kind.replace(`_`,` `),()=>new Date(G(t).created_at).toLocaleString()]),J(e,n)}),M(n);var r=V(n,2),i=e=>{var t=Wi(),n=B(t,!0);H(()=>{t.disabled=G(ae),Y(n,G(ae)?`Loading…`:`Show older decisions`)}),K(`click`,t,_e),J(e,t)};X(r,e=>{G(de)||e(i)});var a=V(r,2),o=e=>{var t=$i();H(()=>t.disabled=G(ae)),K(`click`,t,ve),J(e,t)};X(a,e=>{G(ie)===G(ne)&&G(re).length>0&&e(o)});var s=V(a,2),c=e=>{var t=ea(),n=B(t,!0);H(()=>Y(n,G(k))),J(e,t)};X(s,e=>{G(k)!==null&&e(c)}),J(e,t)};X(Ye,e=>{G(ue).length===0?e(Ze):e(Qe,-1)});var $e=V(Ye,2),et=e=>{var t=Vi(),n=B(t,!0);H(()=>Y(n,G(y))),J(e,t)};X($e,e=>{G(y)!==null&&e(et)}),M(ye),H(e=>{be=Hr(ye,1,`svelte-38cs4v`,null,be,{"decision-card":u()}),Y(Se,u()?`Your decision`:`Operator decisions`),Q(we,`data-review`,i()?.kind??`unreviewed`),Y(Te,`Status: ${e??``}`),ke=Hr(Oe,1,``,null,ke,{"sr-only":u()}),Y(je,G(p)===null?`Choose a verdict to record it.`:l()[G(p)]??d[G(p)]??``),We.disabled=u()?G(g)||!G(me):G(g),Y(Ge,G(g)?`Recording…`:`Record decision`)},[()=>he()]),lr(`submit`,Ee,A),J(e,ye),Be()}ur([`change`,`click`]);var ia=q(` `),aa=q(` `,1);function oa(e,t){ze(t,!0);let n=$(t,`name`,3,`value`),r=I(`idle`),i=I(null),a=!0;ci(()=>{a=!1,G(i)!==null&&clearTimeout(G(i))});async function o(){G(i)!==null&&(clearTimeout(G(i)),L(i,null)),L(r,`idle`);try{if(navigator.clipboard?.writeText!==void 0)await navigator.clipboard.writeText(t.value);else{let e=document.createElement(`textarea`);e.value=t.value,e.className=`wb-clipboard-buffer`,document.body.appendChild(e);let n=document.activeElement,r=!1;try{e.select(),r=document.execCommand(`copy`)}finally{e.remove(),n instanceof HTMLElement&&n.focus({preventScroll:!0})}if(!r)throw Error(`copy rejected`)}if(!a)return;L(r,`copied`),L(i,setTimeout(()=>{L(r,`idle`),L(i,null)},1500),!0)}catch{a&&L(r,`failed`)}}var s=aa(),c=z(s),l=B(c,!0),u=V(c,2),d=e=>{var t=ia(),r=B(t);H(()=>Y(r,`Could not copy the ${n()??``}; select and copy it manually.`)),J(e,t)};X(u,e=>{G(r)===`failed`&&e(d)}),H(()=>{Q(c,`aria-label`,`Copy ${n()}`),Y(l,G(r)===`copied`?`Copied`:G(r)===`failed`?`Copy failed`:`Copy`)}),K(`click`,c,o),J(e,s),Be()}ur([`click`]);var sa=q(`

                        No retained observation for this event. Its reference is retained, but a saved scanner view is unavailable.

                        `),ca=q(`
                        Reported vendor
                        `),la=q(`
                        Reported OS
                        `),ua=q(`
                        Reported device
                        `),da=q(`

                        Some reported fields were truncated at ingest.

                        `),fa=q(`
                      1. `),pa=q(`
                          `),ma=q(`Open reported context → `),ha=q(`

                          No linked profile is available for this saved view.

                          `),ga=q(`

                          Hostname
                          IP address
                          MAC address

                          Saved report view · technical attributes
                           

                          `),_a=q(`
                          Reported

                          Reported scan target:
                          Received

                          `);function va(e,t){ze(t,!0);let n=$(t,`views`,19,()=>[]),r=$(t,`profileLinks`,19,()=>[]),i=P(()=>Ci(t.ref.occurred_at)),a=P(()=>Ci(t.ref.received_at)),o=e=>typeof e==`string`&&e.trim()!==``?e:`Not reported`;var s=_a(),c=R(s),l=R(c),u=R(l),d=B(u,!0),f=B(V(u),!0);M(l);var p=V(l,2),m=V(R(p)),h=B(m,!0);M(p),M(c);var g=V(c,2),_=R(g),v=V(R(_)),y=B(v,!0),b=V(v),x=e=>{J(e,vr(`(truncated)`))};X(b,e=>{t.ref.target_truncated&&e(x)});var S=V(b,3),C=B(S,!0);M(_);var w=V(_,2),T=e=>{J(e,sa())},ee=e=>{var t=yr();Z(z(t),17,n,e=>`${e.event_id}:${e.view_role}`,(e,t)=>{let n=P(()=>G(t).attributes??{});var i=ga(),a=R(i),s=B(a,!0),c=V(a,2),l=R(c),u=B(V(R(l)),!0);M(l);var d=V(l,2),f=B(V(R(d)),!0);M(d);var p=V(d,2),m=B(V(R(p)),!0);M(p);var h=V(p,2),g=e=>{var t=ca(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).vendor)),J(e,t)};X(h,e=>{G(n).vendor&&e(g)});var _=V(h,2),v=e=>{var t=la(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).os)),J(e,t)};X(_,e=>{G(n).os&&e(v)});var y=V(_,2),b=e=>{var t=ua(),r=B(V(R(t)),!0);M(t),H(()=>Y(r,G(n).device)),J(e,t)};X(y,e=>{G(n).device&&e(b)}),M(c);var x=V(c,2),S=R(x),C=B(S),w=B(V(S));M(x);var T=V(x,2),ee=B(T,!0),te=V(T,2),E=e=>{J(e,da())};X(te,e=>{G(n).attributes_truncated&&e(E)});var D=V(te,2),ne=B(V(R(D)),!0);M(D);var re=V(D,2),ie=B(re),O=V(re,2),ae=e=>{var n=pa();Z(n,21,()=>G(t).ports,e=>`${e.proto}/${e.port}`,(e,t)=>{var n=fa(),r=B(n);H(()=>Y(r,`${G(t).proto??``}/${G(t).port??``} ${G(t).state??``}${G(t).service?` (${G(t).service})`:``}`)),J(e,n)}),M(n),J(e,n)};X(O,e=>{(G(t).ports??[]).length>0&&e(ae)});var oe=V(O,2),se=e=>{var n=ma(),i=B(V(R(n)));M(n),H((e,t,r)=>{Q(n,`href`,e),Y(i,`${t??``} · ${r??``}`)},[()=>`#/devices/${encodeURIComponent(G(t).profile_id)}`,()=>Ti(G(t).reference_class),()=>r().find(e=>e.profile_id===G(t).profile_id)?.role??G(t).view_role]),J(e,n)},ce=e=>{J(e,ha())};X(oe,e=>{G(t).profile_id?e(se):e(ce,-1)}),M(i),H((e,r,a,o,c)=>{Q(i,`aria-label`,`${G(t).view_role} view`),Y(s,G(t).view_role===`previous`?`Before the reported change`:G(t).view_role===`current`?`After the reported change`:`Reported host`),Y(u,e),Y(f,r),Y(m,a),Y(C,`Scanner-reported identity confidence: ${(G(n).identity_confidence||`unknown`)??``}`),Y(w,`Identity source: ${(G(n).identity_source||`unknown`)??``}`),Y(ee,o),Y(ne,c),Y(ie,`Snapshot covers ${G(t).ports_total??``} ports${G(t).ports_truncated?` (truncated)`:``}.`)},[()=>o(G(n).hostname),()=>o(G(n).ip),()=>o(G(n).mac),()=>ji(G(n).randomized),()=>JSON.stringify(G(n),null,2)]),J(e,i)}),J(e,t)};X(w,e=>{n().length===0?e(T):e(ee,-1)}),M(g),M(s),H(()=>{Q(s,`data-evidence`,`event:${t.ref.event_id}`),Q(s,`aria-label`,`Saved report ${t.ref.event_id}`),Y(d,t.ref.event_id),Y(f,t.ref.event_type),Q(m,`datetime`,t.ref.occurred_at),Q(m,`title`,G(i).utc),Y(h,G(i).local),Y(y,t.ref.target||`Not reported`),Q(S,`datetime`,t.ref.received_at),Q(S,`title`,G(a).utc),Y(C,G(a).local)}),J(e,s),Be()}var ya=q(` `),ba=q(`
                        • Arguments and tokenized result
                           
                           
                        • `);function xa(e,t){ze(t,!0);let n=P(()=>`step-${t.step.step}`),r=P(()=>t.step.summary??(t.step.outcome===`unknown_subject`?`The investigator could not resolve this lookup to a permitted reported subject; history remains unknown.`:t.step.status===`blocked`?`Redaction blocked this result; the lookup did not complete.`:t.step.status===`error`?`Tool execution failed; result unavailable.`:`No interpreted result is available.`));var i=ba(),a=R(i),o=B(a,!0),s=V(a,2),c=e=>{var n=ya(),r=B(n,!0);H(e=>Y(r,e),[()=>t.step.outcome.replaceAll(`_`,` `)]),J(e,n)};X(s,e=>{t.step.outcome&&e(c)});var l=V(s,2),u=B(l),d=V(l,2),f=B(d,!0),p=V(d,2),m=V(R(p),2),h=B(m),g=B(V(m,2));M(p),M(i),H(()=>{Q(i,`id`,G(n)),Q(i,`data-evidence`,`step:${t.step.step}`),Y(o,t.step.tool),Y(u,`step ${t.step.step??``} · ${t.step.status??``} · ${t.step.duration_ms??``} ms`),Y(f,G(r)),Y(h,`Args: ${t.step.args??``}`),Y(g,`Result: ${t.step.result??``}`)}),J(e,i),Be()}function Sa(e,t){if(!e)return!1;let n=Array.from(e.querySelectorAll(`[data-evidence]`)).find(e=>e.dataset.evidence===t);if(!n)return!1;for(let t=n;t&&t!==e;t=t.parentElement)t.tagName===`DETAILS`&&(t.open=!0);return n.scrollIntoView({block:`nearest`}),n.focus({preventScroll:!0}),!0}var Ca=q(`

                          Briefing unavailable

                          `),wa=q(`

                          Loading briefing…

                          `),Ta=q(`

                          ← Back to Home

                          `),Ea=q(`

                          The saved view could not be refreshed. Your last loaded report is still shown.

                          `),Da=q(`
                          Reported host
                          IP address
                          MAC address
                          `,1),Oa=q(`
                          Reported identities
                          See the individual report views below

                          `),ka=q(`(truncated)`),Aa=q(` `),ja=q(``),Ma=q(``),Na=q(` `),Pa=q(`
                          Copy for your investigation
                          `),Fa=q(` `),Ia=q(`

                          Suggestion adjusted to respect the baseline floor and one-step raise limit.

                          `),La=q(`

                          Deterministic fallback text: the model path did not produce this briefing.

                          `),Ra=q(`

                          No referenced events retained.

                          `),za=q(`

                          More referenced events exist than shown.

                          `),Ba=q(`

                          More observations exist than shown; some saved views are omitted.

                          `),Va=q(`

                          Possible explanation:

                          `),Ha=q(`

                          `),Ua=q(`

                          Investigation has limits. Some lookups could not establish retained context. Missing lookup results do not prove this host has no history.

                          `),Wa=q(``),Ga=q(`
                        • `),Ka=q(`

                          What the investigator could establish

                            `,1),qa=q(``),Ja=q(`
                            Cited evidence
                            `),Ya=q(`
                          • Open reported context
                          • `),Xa=q(``),Za=q(`

                            No profile links persisted for this briefing.

                            `),Qa=q(`

                            More profile links exist than shown.

                            `),$a=q(`
                              `),eo=q(`

                              No agent trace is available for this briefing.

                              `),to=q(`

                              Model accounting

                              `,1),no=q(`

                              Reported scan target
                              Analysis source

                              Reported at the time shown. Network conditions may have changed since this report.

                              Why this priority

                              How the priority was computed
                              1. Baseline floor
                              2. Model suggestion
                              3. Final priority

                              Saved at report time

                              Open a report to see what the scanner supplied, with its linked context.

                              Technical details Tool calls, accounting and provenance

                              Executed tool steps

                              Provenance

                              Briefing
                              Group
                              Grouping version
                              Sequence
                              Created
                              Grouping anchor
                              `);function ro(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=$(t,`embedded`,3,!1),i=$(t,`ontitle`,3,null),a=bi();ci(()=>a.invalidate());let o=I(null),s=I(null),c=I(null),l=I(null),u=0,d=P(()=>G(s)??n()),f=P(()=>G(d)===null?``:Di(G(d).kind,G(d).observations,G(d).referenced_events));un(()=>{if(G(d)!==null&&i()){let e=G(d).briefing_id,t=G(f);nr(()=>i()(e,t))}});async function p(){a.invalidate();let e=a.start();L(c,null);try{let n=await di(t.id);a.current(e)&&L(s,n,!0)}catch(t){a.current(e)&&L(c,t instanceof Error?t.message:String(t),!0)}}un(()=>{if(n()!==null)return;let e=t.id;a.invalidate();let r=a.start();L(c,null),di(e).then(e=>{a.current(r)&&L(s,e,!0)},e=>{a.current(r)&&L(c,e instanceof Error?e.message:String(e),!0)})});let m=P(()=>G(d)===null?null:Ci(G(d).created_at)),h=P(()=>G(d)===null?null:ki(G(d).referenced_events)),g=P(()=>G(d)===null?null:Ai(G(d).observations)),_=P(()=>G(d)!==null&&(G(d).clamped||G(d).baseline!==G(d).priority||(G(d).model_suggestion??G(d).baseline)!==G(d).priority)),v=P(()=>G(d)?.referenced_events.length===1?G(d).referenced_events[0]:null),y=P(()=>G(d)!==null&&!G(d).observations_truncated&&!G(d).referenced_truncated),b=P(()=>(G(d)?.traces??[]).filter(e=>[`unknown_subject`,`blocked`,`error`,`unavailable`,`truncated`].includes(e.outcome))),x=P(()=>{let e=[];for(let t of G(d)?.traces??[]){let n=t.summary||`step:${t.step}`,r=e.find(e=>e.key===n);r||(r={key:n,summary:t.summary,steps:[]},e.push(r)),r.steps.push(t.step)}return e});function S(e){Sa(G(o),e)}function C(e){return(G(d)?.observations??[]).filter(t=>t.event_id===e)}function w(e){if(Si(e)||e.metaKey||e.ctrlKey||e.altKey)return;let t={e:`expected`,s:`snooze`,x:`not_expected`}[e.key];t!==void 0&&G(d)!==null&&(u+=1,L(l,{kind:t,seq:u},!0))}si(()=>(window.addEventListener(`keydown`,w),()=>{window.removeEventListener(`keydown`,w)}));var T=yr(),ee=z(T),te=e=>{var t=Ca(),n=V(R(t),2),r=B(n),i=V(n,2);M(t),H(()=>Y(r,`The bridge did not return this briefing (${G(c)??``}).`)),K(`click`,i,p),J(e,t)},E=e=>{J(e,wa())},D=e=>{var t=no();let n;var i=R(t),a=e=>{J(e,Ta())};X(i,e=>{r()||e(a)});var s=V(i,2),u=e=>{var t=Ea(),n=V(R(t));M(t),K(`click`,n,p),J(e,t)};X(s,e=>{G(c)!==null&&e(u)});var w=V(s,2),T=R(w),ee=R(T),te=R(ee),E=B(te,!0),D=V(te,2),ne=B(D);M(ee);var re=V(ee,2),ie=B(re,!0),O=V(re,2),ae=R(O),oe=e=>{var t=Da(),n=z(t),r=B(V(R(n)),!0);M(n);var i=V(n,2),a=V(R(i)),o=R(a,!0),s=V(o),c=e=>{oa(e,{get value(){return G(g).ip},name:`IP address`})};X(s,e=>{G(g).ip!==null&&e(c)}),M(a),M(i);var l=V(i,2),u=V(R(l)),d=R(u,!0),f=V(d),p=e=>{oa(e,{get value(){return G(g).mac},name:`MAC address`})};X(f,e=>{G(g).mac!==null&&e(p)}),M(u),M(l),H(()=>{Y(r,G(g).hostname??`Not reported`),Y(o,G(g).ip??`Not reported`),Y(d,G(g).mac??`Not reported`)}),J(e,t)},se=e=>{var t=Oa(),n=V(R(t)),r=B(V(R(n)),!0);M(n),M(t),H(()=>Y(r,G(y)?`Grouped changes can describe different host views.`:`Only part of the retained evidence is shown.`)),J(e,t)};X(ae,e=>{G(g)!==null&&G(y)?e(oe):e(se,-1)});var ce=V(ae,2),le=V(R(ce)),ue=R(le,!0),de=V(ue),k=e=>{J(e,ka())};X(de,e=>{G(v)?.target_truncated&&e(k)}),M(le),M(ce);var fe=V(ce,2),pe=R(fe),me=B(pe,!0),he=V(pe),A=R(he),ge=e=>{var t=Aa(),n=B(t,!0);H(()=>{Q(t,`title`,G(h).utc),Y(n,G(h).local)}),J(e,t)},j=e=>{J(e,vr(`Not reported`))};X(A,e=>{G(h)===null?e(j,-1):e(ge)}),M(he),M(fe);var _e=V(fe,2),ve=B(V(R(_e)),!0);M(_e),M(O);var be=V(O,2),xe=R(be),Se=B(xe),Ce=V(xe,2),we=e=>{var t=ja();K(`click`,t,()=>S(`event:${G(v).event_id}`)),J(e,t)},Te=e=>{var t=Ma();K(`click`,t,()=>S(`reports`)),J(e,t)};X(Ce,e=>{G(v)?e(we):G(d).referenced_events.length>0&&e(Te,1)}),M(be),M(T);var Ee=V(T,4),De=V(R(Ee),2),Oe=B(De,!0),ke=V(De,2),Ae=e=>{var t=Pa(),n=V(R(t),2),r=e=>{var t=Na(),n=R(t);oa(V(n),{get value(){return G(g).ip},name:`IP address`}),M(t),H(()=>Y(n,`${G(g).ip??``} `)),J(e,t)};X(n,e=>{G(g).ip!==null&&e(r)});var i=V(n,2),a=e=>{var t=Na(),n=R(t);oa(V(n),{get value(){return G(g).mac},name:`MAC address`}),M(t),H(()=>Y(n,`${G(g).mac??``} `)),J(e,t)};X(i,e=>{G(g).mac!==null&&e(a)}),M(t),J(e,t)};X(ke,e=>{G(g)!==null&&G(y)&&(G(g).ip!==null||G(g).mac!==null)&&e(Ae)}),M(Ee);var je=V(Ee,2),Me=R(je);{let e=P(()=>G(d).decisions??[]),t=P(()=>G(d).effective_review??null);ra(Me,{subjectKind:`briefing`,get subjectId(){return G(d).briefing_id},kinds:[`expected`,`not_expected`,`snooze`,`note`],get decisions(){return G(e)},get effective(){return G(t)},get preset(){return G(l)},reload:p,requireChoice:!0})}M(je);var Ne=V(je,2),Pe=V(R(Ne),2),Fe=B(Pe,!0),Ie=V(Pe,2),Le=R(Ie),Re=B(Le),ze=V(Le),Be=e=>{var t=Fa(),n=B(t,!0);H(()=>Y(n,G(d).baseline_rule)),J(e,t)};X(ze,e=>{G(d).baseline_rule&&e(Be)}),M(Ie);var Ve=V(Ie,2),He=V(R(Ve),2),Ue=R(He),We=B(V(R(Ue)),!0);M(Ue);var Ge=V(Ue,2),Ke=B(V(R(Ge)),!0);M(Ge);var qe=V(Ge,2),N=B(V(R(qe)),!0);M(qe),M(He);var Je=V(He,2),Ye=e=>{J(e,Ia())};X(Je,e=>{G(d).clamped&&e(Ye)}),M(Ve);var Xe=V(Ve,2),Ze=e=>{J(e,La())};X(Xe,e=>{G(d).fallback_used&&e(Ze)}),M(Ne);var Qe=V(Ne,2),$e=R(Qe),et=B(R($e));ye(),M($e);var tt=V($e,4),nt=e=>{J(e,Ra())},rt=e=>{var t=yr();Z(z(t),17,()=>G(d).referenced_events,e=>e.event_id,(e,t)=>{{let n=P(()=>C(G(t).event_id));va(e,{get ref(){return G(t)},get views(){return G(n)},get profileLinks(){return G(d).profiles}})}}),J(e,t)};X(tt,e=>{G(d).referenced_events.length===0?e(nt):e(rt,-1)});var it=V(tt,2),at=e=>{J(e,za())};X(it,e=>{G(d).referenced_truncated&&e(at)});var ot=V(it,2),st=e=>{J(e,Ba())};X(ot,e=>{G(d).observations_truncated&&e(st)}),M(Qe);var ct=V(Qe,2),lt=R(ct),ut=B(lt,!0),dt=V(lt,2),ft=e=>{var t=Va(),n=V(R(t));M(t),H(()=>Y(n,` ${G(d).likely_explanation??``}`)),J(e,t)};X(dt,e=>{G(d).likely_explanation&&e(ft)});var pt=V(dt,2),mt=e=>{var t=Ha(),n=B(R(t));M(t),H(()=>Y(n,`Explanation confidence (model self-assessment): ${G(d).confidence??``}`)),J(e,t)};X(pt,e=>{G(d).confidence&&e(mt)});var F=V(pt,2),ht=e=>{J(e,Ua())};X(F,e=>{G(b).length>0&&e(ht)});var gt=V(F,2),_t=B(gt,!0),vt=V(gt,2),yt=e=>{var t=Ka(),n=V(z(t),2);Z(n,21,()=>G(x),jr,(e,t)=>{var n=Ga(),r=V(R(n)),i=R(r),a=B(i,!0),o=V(i);Z(o,20,()=>G(t).steps,e=>e,(e,t)=>{var n=Wa(),r=B(n);H(()=>Y(r,`View step ${t??``}`)),K(`click`,n,()=>S(`step:${t}`)),J(e,n)}),M(o),M(r),M(n),H(()=>Y(a,G(t).summary||`No interpreted result is available. Open the tool step to inspect the saved response.`)),J(e,n)}),M(n),J(e,t)};X(vt,e=>{G(d).traces?.length&&e(yt)});var bt=V(vt,2),xt=e=>{var t=Ja();Z(V(R(t),2),19,()=>G(d).evidence_refs,(e,t)=>`${e.event_id??e.tool_step}:${t}`,(e,t)=>{var n=qa(),r=B(n,!0);H(()=>Y(r,G(t).event_id?`Event ${G(t).event_id}`:`Tool step ${G(t).tool_step}`)),K(`click`,n,()=>S(G(t).event_id?`event:${G(t).event_id}`:`step:${G(t).tool_step}`)),J(e,n)}),M(t),J(e,t)};X(bt,e=>{G(d).evidence_refs?.length&&e(xt)});var St=V(bt,2),Ct=e=>{var t=Xa(),n=V(R(t));Z(n,21,()=>G(d).profiles,e=>`${e.profile_id}:${e.role}`,(e,t)=>{var n=Ya(),r=R(n),i=B(V(r,2));M(n),H(e=>{Q(r,`href`,e),Y(i,`${G(t).profile_id??``} (${G(t).role??``})`)},[()=>`#/devices/${encodeURIComponent(G(t).profile_id)}`]),J(e,n)}),M(n),M(t),J(e,t)},wt=e=>{J(e,Za())};X(St,e=>{G(d).profiles.length>0?e(Ct):e(wt,-1)});var Tt=V(St,2),Et=e=>{J(e,Qa())};X(Tt,e=>{G(d).profiles_truncated&&e(Et)}),M(ct);var Dt=V(ct,2),Ot=V(R(Dt),2),kt=V(R(Ot),2),At=e=>{var t=$a();Z(t,21,()=>G(d).traces,e=>e.step,(e,t)=>{xa(e,{get step(){return G(t)}})}),M(t),J(e,t)},jt=e=>{J(e,eo())};X(kt,e=>{G(d).traces?.length?e(At):e(jt,-1)});var Mt=V(kt,2),Nt=e=>{var t=to(),n=B(V(z(t)));H(()=>Y(n,`${G(d).input_tokens??`Unknown`??``} input tokens · ${G(d).output_tokens??`Unknown`??``} output tokens · ${G(d).cost_usd==null?`Cost unknown`:`$${G(d).cost_usd}`}`)),J(e,t)};X(Mt,e=>{(G(d).input_tokens!=null||G(d).output_tokens!=null||G(d).cost_usd!=null)&&e(Nt)});var Pt=V(Mt,3),Ft=V(R(Pt)),I=B(Ft,!0),It=V(Ft,2),Lt=B(It,!0),Rt=V(It,3),zt=B(Rt,!0),Bt=V(Rt,2),Vt=B(Bt,!0),Ht=V(Bt,3),Ut=R(Ht),Wt=B(Ut,!0);M(Ht);var Gt=B(V(Ht,3),!0);M(Pt),M(Ot),M(Dt),M(w),M(t),ii(t,e=>L(o,e),()=>G(o)),H(e=>{n=Hr(t,1,`briefing dossier svelte-90l7ri`,null,n,{embedded:r()}),Y(E,G(f)),Q(D,`data-priority`,G(d).priority),Q(D,`aria-label`,`Priority: ${G(d).priority}`),Y(ne,`${G(d).priority??``} priority`),Y(ie,G(d).what_changed),Y(ue,G(v)?.target||(G(d).referenced_events.length>1?`Shown per report below`:`Not reported`)),Y(me,G(h)?.partial||G(d).referenced_truncated?`Available report times`:G(d).referenced_events.length>1?`Report time range`:`Report time`),Y(ve,e),Y(Se,`${G(d).referenced_events.length??``}${G(d).referenced_truncated?`+`:``} source report${G(d).referenced_events.length===1?``:`s`}`),Y(Oe,G(d).recommended_check),Y(Fe,G(d).priority_reason),Q(Le,`data-priority`,G(d).priority),Y(Re,`Final priority: ${G(d).priority??``}`),Ve.open=G(_),Y(We,G(d).baseline),Y(Ke,G(d).model_suggestion??`No model suggestion — baseline stands`),Y(N,G(d).priority),Y(et,`Scanner snapshot${G(d).referenced_events.length===1?``:`s`}`),Y(ut,G(d).source===`agent`?`Investigator notes`:`Analysis and retained context`),Y(_t,G(d).context),Y(I,G(d).briefing_id),Y(Lt,G(d).group_id),Y(zt,G(d).grouping_version),Y(Vt,G(d).seq),Q(Ut,`datetime`,G(d).created_at),Q(Ut,`title`,G(m)?.utc),Y(Wt,G(m)?.local),Y(Gt,G(d).host)},[()=>wi(G(d).source)]),J(e,t)};X(ee,e=>{G(c)!==null&&G(d)===null?e(te):G(d)===null?e(E,1):e(D,-1)}),J(e,T),Be()}ur([`click`]);var io=q(`

                              `),ao=q(`

                              Loading incident…

                              `),oo=q(`Closed `),so=q(`Open since `),co=q(`

                              Some members share identity evidence; only distinct qualified references count toward the burst threshold.

                              `),lo=q(`

                              More members exist than shown.

                              `),uo=q(`

                              No member evidence retained.

                              `),fo=q(`
                            1. `),po=q(`
                                `),mo=q(`

                                `),ho=q(`

                                Hypothesis:

                                `),go=q(`
                              1. `),_o=q(`

                                Cited reports:

                                  `,1),vo=q(`

                                  Summary

                                  `),yo=q(`

                                  Scope

                                  Window start: · Last member:

                                  Evidence

                                  `,1),bo=q(`

                                  Back to overview

                                  `,1);function xo(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=bi();ci(()=>{r.invalidate(),typeof window<`u`&&window.removeEventListener(`watcherbridge:incident`,c)});let i=I(null),a=I(null),o=P(()=>G(i)??n());async function s(){r.invalidate();let e=r.start();L(a,null);try{let n=await yi(t.id);r.current(e)&&L(i,n,!0)}catch(t){r.current(e)&&L(a,t instanceof Error?t.message:String(t),!0)}}function c(){s()}si(()=>{n()===null&&s(),window.addEventListener(`watcherbridge:incident`,c)});let l={burst_appearance:`Burst appearance`,burst_disappearance:`Burst disappearance`},u={expected:`Mark reviewed: this incident needs no further action. Members stay untouched unless included below.`,not_expected:`Flag for follow-up: this incident still needs a check.`,snooze:`Hide until a time. Empty keeps the server default (24h).`,note:`Free-text note, kept with the audit (max 2000 characters).`};function d(e){return`${`${e.member_count} member report${e.member_count===1?``:`s`}`} · ${`${e.qualified_refs} qualified upstream reference${e.qualified_refs===1?``:`s`}`}`}var f=bo(),p=V(z(f),2),m=e=>{var t=io(),n=R(t),r=V(n);M(t),H(()=>Y(n,`Refresh failed: ${G(a)??``} `)),K(`click`,r,c),J(e,t)};X(p,e=>{G(a)!==null&&e(m)});var h=V(p,2),g=e=>{J(e,ao())},_=e=>{let t=P(()=>G(o).incident),n=P(()=>Ci(G(t).opened_at)),r=P(()=>Ci(G(t).closed_at)),i=P(()=>Ci(G(t).window_start)),a=P(()=>Ci(G(t).last_member_at));var c=yo(),f=z(c),p=B(f,!0),m=V(f,2),h=R(m),g=B(h,!0),_=V(h,2),v=B(_),y=V(_,2),b=e=>{var n=oo(),i=V(R(n)),a=B(i,!0);M(n),H(()=>{Q(i,`datetime`,G(t).closed_at),Q(i,`title`,G(r).utc),Y(a,G(r).local)}),J(e,n)},x=e=>{var r=so(),i=V(R(r)),a=B(i,!0);M(r),H(()=>{Q(i,`datetime`,G(t).opened_at),Q(i,`title`,G(n).utc),Y(a,G(n).local)}),J(e,r)};X(y,e=>{G(t).status===`closed`&&G(t).closed_at?e(b):e(x,-1)}),M(m);var S=V(m,2),C=V(R(S),2),w=B(C),T=V(C,2),ee=B(T),te=V(T,2),E=V(R(te)),D=B(E,!0),ne=V(E,2),re=B(ne,!0);M(te),M(S);var ie=V(S,2),O=V(R(ie),2),ae=B(O,!0),oe=V(O,2),se=e=>{J(e,co())};X(oe,e=>{G(t).qualified_refs{J(e,lo())};X(ce,e=>{G(o).truncated&&e(le)});var ue=V(ce,2),de=e=>{J(e,uo())},k=e=>{var t=po();Z(t,21,()=>G(o).members,e=>e.briefing_id,(e,t)=>{let n=P(()=>Ci(G(t).created_at));var r=fo(),i=R(r),a=B(i,!0),o=V(i,2),s=B(o,!0),c=V(o,2),l=B(c,!0),u=B(V(c,2));M(r),H(()=>{Q(i,`href`,`#/briefing/${G(t).briefing_id}`),Y(a,G(t).briefing_id),Y(s,G(t).priority),Q(c,`datetime`,G(t).created_at),Q(c,`title`,G(n).utc),Y(l,G(n).local),Y(u,`${G(t).refs.length??``} qualified reference${G(t).refs.length===1?``:`s`}`)}),J(e,r)}),M(t),J(e,t)};X(ue,e=>{G(o).members.length===0?e(de):e(k,-1)}),M(ie);var fe=V(ie,2),pe=e=>{let t=P(()=>G(o).summary);var n=vo(),r=V(R(n),2),i=B(R(r));M(r);var a=V(r,2),s=e=>{var n=mo(),r=B(n,!0);H(()=>Y(r,G(t).summary)),J(e,n)};X(a,e=>{G(t).summary&&e(s)});var c=V(a,2),l=e=>{var n=ho(),r=V(R(n));M(n),H(()=>Y(r,` ${G(t).hypothesis??``}`)),J(e,n)};X(c,e=>{G(t).hypothesis&&e(l)});var u=V(c,2),d=e=>{var n=mo(),r=B(n);H(()=>Y(r,`Display confidence: ${G(t).confidence??``}`)),J(e,n)};X(u,e=>{G(t).confidence&&e(d)});var f=V(u,2),p=B(f),m=V(f,2),h=e=>{var n=mo(),r=B(n);H(()=>Y(r,`Priority explanation: ${G(t).reason??``}`)),J(e,n)};X(m,e=>{G(t).reason&&e(h)});var g=V(m,2),_=B(g),v=V(g,2),y=e=>{var n=_o(),r=V(z(n),2);Z(r,21,()=>G(t).citations,e=>e.member,(e,t)=>{var n=go(),r=R(n),i=V(r),a=B(i,!0);M(n),H(()=>{Y(r,`${G(t).member??``}: `),Q(i,`href`,`#/briefing/${G(t).briefing_id}`),Y(a,G(t).briefing_id)}),J(e,n)}),M(r),J(e,n)};X(v,e=>{G(t).citations.length&&e(y)});var b=V(v,2),x=e=>{var n=mo(),r=B(n,!0);H(()=>Y(r,G(t).error)),J(e,n)};X(b,e=>{G(t).error&&e(x)}),M(n),H(()=>{Y(i,`Summary status: ${G(t).status??``}`),Y(p,`Priority floor: ${G(t).floor??``}. Resolved priority: ${G(t).final_priority??``}${G(t).suggestion?` (suggested ${G(t).suggestion}${G(t).clamped?`, clamped`:``})`:` (no model raise)`}`),Y(_,`Evidence coverage: ${G(t).sampled??``} sampled member reports; ${G(t).omitted??``} omitted. The sample may not represent every member.`)}),J(e,n)};X(fe,e=>{G(o).summary&&e(pe)});var me=V(fe,2);{let e=P(()=>G(o).decisions??[]),n=P(()=>G(o).effective_review??null);ra(me,{subjectKind:`incident`,get subjectId(){return G(t).incident_id},kinds:[`expected`,`not_expected`,`snooze`,`note`],get decisions(){return G(e)},get effective(){return G(n)},memberScope:!0,get memberCount(){return G(t).member_count},get kindHelp(){return u},reload:s})}H(e=>{Y(p,l[G(t).kind]??G(t).kind),Y(g,G(t).status),Y(v,`Floor: ${G(t).floor??``}`),Y(w,`Source: ${G(t).source_scope??``}`),Y(ee,`Target: ${G(t).target??``}`),Q(E,`datetime`,G(t).window_start),Q(E,`title`,G(i).utc),Y(D,G(i).local),Q(ne,`datetime`,G(t).last_member_at),Q(ne,`title`,G(a).utc),Y(re,G(a).local),Y(ae,e)},[()=>d(G(t))]),J(e,c)};X(h,e=>{G(o)===null?e(g):e(_,-1)}),J(e,f),Be()}ur([`click`]);var So=q(`
                                • `),Co=q(`
                                    `),wo=q(`, last change `,1),To=q(`

                                      `);function Eo(e,t){ze(t,!0);let n=$(t,`reasons`,19,()=>[]);var r=To(),i=R(r),a=B(i),o=V(i,2),s=R(o),c=B(s,!0),l=V(s,2);Z(l,20,()=>[`quiet`,`review`,`act`],e=>e,(e,n)=>{var r=So(),i=B(r,!0);H(()=>{Q(r,`aria-current`,t.verdict===n?`true`:void 0),Y(i,n)}),J(e,r)}),M(l),M(o);var u=V(o,2),d=R(u),f=B(d),p=V(d,2),m=B(p),h=B(V(p,2));M(u);var g=V(u,2),_=e=>{var t=Co();Z(t,20,n,e=>e,(e,t)=>{var n=So(),r=B(n,!0);H(()=>Y(r,t)),J(e,n)}),M(t),J(e,t)};X(g,e=>{n().length>0&&e(_)});var v=V(g,2),y=R(v),b=V(y),x=e=>{var n=wo(),r=V(z(n)),i=B(r,!0);H(e=>{Q(r,`datetime`,t.lastChange),Q(r,`title`,t.lastChange),Y(i,e)},[()=>new Date(t.lastChange).toLocaleString()]),J(e,n)};X(b,e=>{t.lastChange&&e(x)}),M(v),M(r),H((e,n)=>{Q(r,`data-verdict`,t.verdict),Y(a,`Review overview · last ${e??``}`),Y(c,t.headline),Y(f,`Verdict: ${t.verdict??``}`),Q(p,`data-state`,t.coverage),Y(m,`Coverage: ${t.coverage??``}`),Y(h,`Liveness: ${t.liveness??``}`),Y(y,`${t.receipts??``} ${t.receipts===1?`receipt`:`receipts`} in the last ${n??``}`)},[()=>Oi(t.window),()=>Oi(t.window)]),J(e,r),Be()}var Do=q(`

                                      Nothing flagged. New briefings appear here when the bridge reports them.

                                      `),Oo=q(` `,1),ko=q(``),Ao=q(``),jo=q(`
                                      `),Mo=q(`

                                      `),No=q(`
                                    1. `),Po=q(`
                                        `),Fo=q(` `,1),Io=q(`
                                      1. `),Lo=q(`
                                          `),Ro=q(`

                                          `);function zo(e,t){ze(t,!0);let n=$(t,`items`,19,()=>[]),r=$(t,`selectedId`,3,null),i=$(t,`titles`,19,()=>({})),a=$(t,`onselect`,3,null),o=I(Ut({})),s=I(Ut({})),c=I(Ut({})),l=I(Ut({})),u=!0,d=0;si(()=>(window.addEventListener(`watcherbridge:incident`,f),()=>window.removeEventListener(`watcherbridge:incident`,f)));function f(){d++,L(s,{},!0),L(c,{},!0),L(l,{},!0),L(o,Object.fromEntries(n().filter(e=>e.kind===`incident`&&G(o)[e.id]).map(e=>[e.id,!0])),!0);for(let e of Object.keys(G(o)))m(e)}ci(()=>{u=!1});async function p(e){G(o)[e]?G(o)[e]=!1:(G(o)[e]=!0,G(s)[e]!==void 0||G(l)[e]||await m(e))}async function m(e){let t=d;delete G(c)[e],G(l)[e]=!0;try{let n=await yi(e);u&&t===d&&(G(s)[e]=n.members)}catch(n){u&&t===d&&(G(c)[e]=n instanceof Error?n.message:String(n))}finally{u&&t===d&&(G(l)[e]=!1)}}var h=Ro(),g=R(h),_=B(g),v=V(g,2),y=e=>{J(e,Do())},b=e=>{var t=Lo();Z(t,21,n,e=>e.id,(e,t)=>{var n=Io();{let e=e=>{var n=Oo(),r=z(n),a=B(r,!0),o=V(r,2),s=R(o),c=B(s),l=B(V(s),!0);M(o);var u=V(o,2),d=R(u),f=B(d,!0),p=V(d),m=B(p,!0);M(u),H((e,n)=>{Q(r,`data-priority`,G(t).priority),Q(r,`aria-label`,`Priority: ${G(t).priority}`),Y(a,G(t).priority),Y(c,`${G(t).kind===`incident`?`Incident · `:``}${e??``}`),Y(l,G(t).id),Y(f,G(t).status),Q(p,`datetime`,G(t).created_at),Q(p,`title`,G(t).created_at),Y(m,n)},[()=>G(t).kind===`briefing`?i()[G(t).id]??Di(G(t).change_kind):Ei(G(t).change_kind),()=>new Date(G(t).created_at).toLocaleString()]),J(e,n)};var u=R(n),d=n=>{var i=yr(),o=z(i),s=n=>{var i=ko(),o=R(i);e(o),M(i),H(()=>Q(i,`aria-current`,r()===G(t).id?`true`:void 0)),K(`click`,i,()=>a()(G(t))),J(n,i)},c=n=>{var r=Ao(),i=R(r);e(i),M(r),H(()=>Q(r,`href`,G(t).kind===`briefing`?`#/briefing/${G(t).id}`:`#/incidents/${G(t).id}`)),J(n,r)};X(o,e=>{a()?e(s):e(c,-1)}),J(n,i)},f=t=>{var n=jo(),r=R(n);e(r),M(n),J(t,n)};X(u,e=>{G(t).kind===`briefing`||G(t).kind===`incident`?e(d):e(f,-1)});var m=V(u,2),h=e=>{var n=Fo(),r=z(n),i=B(r,!0),a=V(r,2),u=e=>{var n=Mo(),r=B(n,!0);H(()=>Y(r,G(c)[G(t).id])),J(e,n)};X(a,e=>{G(c)[G(t).id]!==void 0&&e(u)});var d=V(a,2),f=e=>{var n=Po();Z(n,21,()=>G(s)[G(t).id],e=>e.briefing_id,(e,t)=>{var n=No(),r=R(n),i=B(r,!0),a=B(V(r,2),!0);M(n),H(()=>{Q(r,`href`,`#/briefing/${G(t).briefing_id}`),Y(i,G(t).briefing_id),Y(a,G(t).priority)}),J(e,n)}),M(n),J(e,n)};X(d,e=>{(G(o)[G(t).id]??!1)&&G(s)[G(t).id]!==void 0&&e(f)}),H(()=>{Q(r,`aria-expanded`,G(o)[G(t).id]??!1),r.disabled=G(l)[G(t).id]??!1,Y(i,G(o)[G(t).id]?`Hide members`:G(l)[G(t).id]?`Loading members…`:`Show members`)}),K(`click`,r,()=>p(G(t).id)),J(e,n)};X(m,e=>{G(t).kind===`incident`&&e(h)}),M(n)}H(()=>Q(n,`data-status`,G(t).status)),J(e,n)}),M(t),J(e,t)};X(v,e=>{n().length===0?e(y):e(b,-1)}),M(h),H(()=>Y(_,`Needs attention (${n().length??``})`)),J(e,h),Be()}ur([`click`]);var Bo=q(`
                                          `),Vo=q(`

                                          Reported changes

                                          Counts in this review window. Expected means explicitly marked by you.

                                          `);function Ho(e,t){ze(t,!0);let n=[`weak_appearance`,`weak_disappearance`,`port_closed`,`hostname_change`,`os_refinement`,`other`],r=$(t,`digest`,19,()=>({})),i=$(t,`explained`,19,()=>({}));var a=Vo(),o=V(R(a),4);Z(o,20,()=>n,e=>e,(e,t)=>{var n=Bo(),a=R(n),o=B(a,!0),s=V(a,2),c=R(s),l=B(V(c));M(s),M(n),H(e=>{Y(o,e),Y(c,`${r()[t]??0??``} `),Y(l,`expected ${i()[t]??0??``}`)},[()=>Ei(t)]),J(e,n)}),M(o),M(a),J(e,a),Be()}var Uo=q(`

                                          No sources registered yet.

                                          `),Wo=q(`Last report `),Go=q(`
                                        1. `),Ko=q(`
                                            `),qo=q(`
                                          • `),Jo=q(`
                                              `),Yo=q(`

                                              `);function Xo(e,t){ze(t,!0);let n=$(t,`sources`,19,()=>[]);var r=Yo(),i=R(r),a=B(i),o=V(i,2),s=e=>{J(e,Uo())},c=e=>{var t=Jo();Z(t,21,n,e=>e.scope,(e,t)=>{var n=qo(),r=R(n),i=B(r,!0),a=V(r,2),o=B(a),s=V(a,2),c=B(s),l=V(s,2),u=e=>{var n=Wo(),r=V(R(n)),i=B(r,!0);M(n),H(e=>{Q(r,`datetime`,G(t).last_report_at),Q(r,`title`,G(t).last_report_at),Y(i,e)},[()=>new Date(G(t).last_report_at).toLocaleString()]),J(e,n)};X(l,e=>{G(t).last_report_at&&e(u)});var d=V(l,2),f=e=>{var n=Ko();Z(n,20,()=>G(t).reasons,e=>e,(e,t)=>{var n=Go(),r=B(n,!0);H(()=>Y(r,t)),J(e,n)}),M(n),J(e,n)};X(d,e=>{G(t).reasons.length>0&&e(f)}),M(n),H(()=>{Y(i,G(t).scope),Y(o,`Status: ${G(t).status??``}`),Y(c,`Liveness: ${G(t).liveness??``}`)}),J(e,n)}),M(t),J(e,t)};X(o,e=>{n().length===0?e(s):e(c,-1)}),M(r),H(()=>Y(a,`Sources (${n().length??``})`)),J(e,r),Be()}var Zo=q(`

                                              Overview unavailable

                                              `),Qo=q(`

                                              Loading overview…

                                              `),$o=q(`

                                              `),es=q(``),ts=q(`

                                              Jump to selected review ↓

                                              `),ns=q(`
                                              Selected review Open full page →
                                              `,1),rs=q(`

                                              Select a review

                                              Choose an item in Needs attention to read its evidence and recommended check, and record your decision.

                                              New reports update the list automatically.

                                              `),is=q(`
                                              `),as=q(`

                                              j k move between rows · Enter select · ? shortcuts

                                              `,1);function os(e,t){ze(t,!0);let n=$(t,`overview`,3,null),r=$(t,`loadError`,3,null),i=$(t,`selected`,15,null),a=P(()=>i()?.id??null),o=I(Ut({}));function s(e,t){G(o)[e]!==t&&L(o,Object.fromEntries([...Object.entries(G(o)),[e,t]].slice(-32)),!0)}function c(e){i({id:e.id,kind:e.kind})}function l(){i(null),document.getElementById(`review-list`)?.focus()}let u=P(()=>n()!==null&&(n().coverage===`degraded`||n().coverage===`blind`));var d=yr(),f=z(d),p=e=>{var t=Zo(),n=B(V(R(t),2));M(t),H(()=>Y(n,`The bridge did not return an overview (${r()??``}). The connection retries automatically.`)),J(e,t)},m=e=>{J(e,Qo())},h=e=>{var t=as(),r=z(t),d=e=>{var t=$o(),r=B(t);H(()=>Y(r,`No receipts in the last ${n().window??``} — quiet here means no data, not a secure network.`)),J(e,t)};X(r,e=>{n().receipts===0&&e(d)});var f=V(r,2),p=e=>{var t=es(),r=B(t);H(()=>Y(r,`Coverage is ${n().coverage??``}. Treat quiet results with caution until the reasons below clear.`)),J(e,t)};X(f,e=>{G(u)&&e(p)});var m=V(f,2);{let e=P(()=>n().reasons??[]);Eo(m,{get verdict(){return n().verdict},get headline(){return n().headline},get coverage(){return n().coverage},get liveness(){return n().liveness},get reasons(){return G(e)},get receipts(){return n().receipts},get window(){return n().window},get lastChange(){return n().last_change}})}var h=V(m,2),g=R(h),_=R(g);zo(_,{get items(){return n().attention},get selectedId(){return G(a)},get titles(){return G(o)},onselect:c});var v=V(_,2),y=e=>{var t=ts();K(`click`,B(t),e=>{e.preventDefault(),document.getElementById(`selected-review`)?.focus()}),J(e,t)};X(v,e=>{i()&&e(y)});var b=V(v,2);{let e=P(()=>n().explained??{});Ho(b,{get digest(){return n().digest},get explained(){return G(e)}})}Xo(V(b,2),{get sources(){return n().sources}}),ye(2),M(g);var x=V(g,2),S=e=>{var t=is(),n=R(t),r=e=>{var t=ns(),n=z(t),r=V(R(n),2),a=V(r,2);M(n),Ar(V(n,2),()=>i().id,e=>{var t=yr(),n=z(t),r=e=>{ro(e,{get id(){return i().id},embedded:!0,ontitle:s})},a=e=>{xo(e,{get id(){return i().id}})};X(n,e=>{i().kind===`briefing`?e(r):e(a,-1)}),J(e,t)}),H(()=>Q(r,`href`,i().kind===`briefing`?`#/briefing/${i().id}`:`#/incidents/${i().id}`)),K(`click`,a,l),J(e,t)},a=e=>{J(e,rs())};X(n,e=>{i()?e(r):e(a,-1)}),M(t),J(e,t)};X(x,e=>{(i()!==null||n().attention.length>0)&&e(S)}),M(h),H(()=>Q(h,`data-empty`,n().attention.length===0&&i()===null)),J(e,t)};X(f,e=>{r()!==null&&n()===null?e(p):n()===null?e(m,1):e(h,-1)}),J(e,d),Be()}ur([`click`]);function ss(e,t,n){let r=()=>{t.dispatchEvent(new Event(`watcherbridge:incident`)),n()},i=[`open`,`incident`,`resync`];for(let t of i)e.addEventListener(t,r);return()=>{for(let t of i)e.removeEventListener(t,r)}}function cs(e,t,n,r){let i=new Set(e.map(n)),a=[];for(let o of t){if(e.length+a.length>=r)break;i.has(n(o))||(i.add(n(o)),a.push(o))}return a}var ls=q(`

                                              Loading reported hosts…

                                              `),us=q(`

                                              `,1),ds=q(`

                                              Connection recovered — this list may miss new reports.

                                              `),fs=q(`

                                              No reported subjects retained.

                                              `),ps=q(`
                                            • Last report
                                            • `),ms=q(`
                                                `),hs=q(`

                                                `),gs=q(`

                                                `),_s=q(``),vs=q(`

                                                End of retained list.

                                                `),ys=q(` `,1),bs=q(`

                                                Reported hosts

                                                Scanner-reported subjects. Classes distinguish device references, address context, unresolved events and legacy profiles.

                                                `);function xs(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=bi();ci(()=>r.invalidate());let i=I(Ut([])),a=I(null),o=I(!1),s=I(null),c=I(!0),l=I(!1),u=I(!1),d=P(()=>G(o)?[]:n()?.devices??[]),f=P(()=>[...G(d),...G(i)]),p=P(()=>G(a)??(G(o)?``:n()?.next_after??``)),m=P(()=>G(p)===``),h=P(()=>!G(m)&&G(f).length>=200),g=!1;un(()=>{let e=t.stream===`live`,n=e&&!g&&G(f).length>0;g=e,n&&L(l,!0)});function _(){L(c,!0),L(u,!0),v(!0)}async function v(e=!1){let t=r.start(),n=e?``:G(p);L(s,null);try{let s=await gi(n);if(!r.current(t))return;let d=cs(e?[]:G(f),s.devices,e=>e.profile_id,200);e?(L(o,!0),L(i,d,!0),L(l,!1)):L(i,[...G(i),...d],!0),L(a,s.next_after??``,!0),L(c,!1),L(u,!1)}catch(e){r.current(t)&&(L(s,e instanceof Error?e.message:String(e),!0),L(c,!1))}}si(()=>{n()===null?v():L(c,!1)});var y=bs(),b=V(R(y),4),x=e=>{J(e,ls())},S=e=>{var t=us(),n=z(t),r=B(n),i=V(n,2);H(()=>Y(r,`The bridge did not return reported hosts (${G(s)??``}).`)),K(`click`,i,()=>v()),J(e,t)},C=e=>{var t=ys(),n=z(t),r=e=>{var t=ds(),n=V(R(t));M(t),K(`click`,n,_),J(e,t)};X(n,e=>{G(l)&&e(r)});var i=V(n,2),a=e=>{J(e,fs())},o=e=>{var t=ms();Z(t,21,()=>G(f),e=>e.profile_id,(e,t)=>{let n=P(()=>Ci(G(t).last_report_at));var r=ps(),i=R(r),a=V(R(i),2),o=R(a),s=B(o,!0),c=V(o),l=B(c,!0),u=B(V(c),!0);M(a);var d=V(a,2),f=V(R(d)),p=B(f,!0);M(d),ye(2),M(i),M(r),H(e=>{Q(i,`href`,`#/devices/${G(t).profile_id}`),Y(s,G(t).operator_label||G(t).reported_id||`Unlabelled subject`),Y(l,G(t).profile_id),Y(u,e),Q(f,`datetime`,G(t).last_report_at),Q(f,`title`,G(n).utc),Y(p,G(n).local)},[()=>Ti(G(t).class)]),J(e,r)}),M(t),J(e,t)};X(i,e=>{G(f).length===0?e(a):e(o,-1)});var c=V(i,2),d=e=>{var t=hs(),n=R(t),r=V(n);M(t),H(()=>Y(n,`Page failed (${G(s)??``}) — earlier rows kept. `)),K(`click`,r,()=>v(G(u))),J(e,t)};X(c,e=>{G(s)!==null&&e(d)});var p=V(c,2),g=e=>{var t=gs();t.textContent=`List capped at 200 rows; use the API for the full set.`,J(e,t)},y=e=>{var t=_s();K(`click`,t,()=>v()),J(e,t)},b=e=>{J(e,vs())};X(p,e=>{G(h)?e(g):G(m)?e(b,-1):e(y,1)}),J(e,t)};X(b,e=>{G(c)&&G(f).length===0?e(x):G(s)!==null&&G(f).length===0?e(S,1):e(C,-1)}),M(y),J(e,y),Be()}ur([`click`]);var Ss=q(`

                                                Reported host unavailable

                                                Back to reported hosts

                                                `),Cs=q(`

                                                Loading reported host…

                                                `),ws=q(`

                                                Connection recovered — this page may miss new reports.

                                                `),Ts=q(`

                                                `),Es=q(`

                                                Earlier bridge-inferred association; not used for current identity.

                                                `),Ds=q(`

                                                `),Os=q(`
                                                Your label
                                                `,1),ks=q(`

                                                No retained observation.

                                                `),As=q(`
                                                `,1),js=q(`
                                              • `),Ms=q(`
                                                  `),Ns=q(`

                                                  Reported

                                                  `,1),Ps=q(`

                                                  No retained reports in this window.

                                                  `),Fs=q(`
                                                • `),Is=q(`
                                                    `),Ls=q(`

                                                    `),Rs=q(``),zs=q(`

                                                    End of retained history. Retention policy lives on the Health screen.

                                                    `),Bs=q(`

                                                    Back to reported hosts

                                                    Source scope
                                                    Upstream reference
                                                    Reported by
                                                    Association
                                                    First report
                                                    Last report

                                                    Latest evidence

                                                    History

                                                    `);function Vs(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=bi();ci(()=>r.invalidate());let i=I(null),a=I(Ut([])),o=I(null),s=I(!1),c=I(null),l=I(null),u=I(null),d=I(!1),f=P(()=>G(i)??n()),p=P(()=>G(s)?[]:n()?.history??[]),h=P(()=>[...G(p),...G(a)]),g=P(()=>G(o)??(G(s)?``:n()?.history_next_after??``)),_=P(()=>G(f)!==null&&G(g)===``),v=P(()=>!G(_)&&G(h).length>=200),y=!1;un(()=>{let e=t.stream===`live`,n=e&&!y&&G(f)!==null;y=e,n&&L(d,!0)});async function b(){r.invalidate();let e=r.start();L(l,null),L(u,null);try{let n=await _i(t.id);if(!r.current(e))return;L(i,n,!0),L(s,!0),L(a,n.history??[],!0),L(o,n.history_next_after??``,!0),L(c,null),L(d,!1)}catch(t){if(r.current(e)){let e=t instanceof Error?t.message:String(t);G(f)===null?L(l,e,!0):L(u,e,!0)}}}async function x(){let e=r.start(),n=G(g);L(c,null);try{let i=await _i(t.id,n);if(!r.current(e))return;L(a,[...G(a),...cs(G(h),i.history,e=>e.observation_id,200)],!0),L(o,i.history_next_after??``,!0)}catch(t){r.current(e)&&L(c,t instanceof Error?t.message:String(t),!0)}}si(()=>{n()===null&&b()});let S=P(()=>G(f)===null?null:Ci(G(f).first_report_at)),C=P(()=>G(f)===null?null:Ci(G(f).last_report_at));var w=yr(),T=z(w),ee=e=>{var t=Ss(),n=V(R(t),2),r=B(n),i=V(n,2),a=V(R(i),2);M(i),M(t),H(()=>Y(r,`The bridge did not return this profile (${G(l)??``}).`)),K(`click`,a,b),J(e,t)},te=e=>{J(e,Cs())},E=e=>{let t=P(()=>G(f).latest===null?null:Ci(G(f).latest.reported_at));var n=Bs(),r=V(R(n),2),i=e=>{var t=ws(),n=V(R(t));M(t),K(`click`,n,b),J(e,t)};X(r,e=>{G(d)&&e(i)});var a=V(r,2),o=e=>{var t=Ts(),n=R(t),r=V(n);M(t),H(()=>Y(n,`Refresh failed (${G(u)??``}) — showing retained data. `)),K(`click`,r,b),J(e,t)};X(a,e=>{G(u)!==null&&e(o)});var s=V(a,2),l=B(s),p=V(s,2),g=e=>{J(e,Es())},y=e=>{var t=Ds(),n=B(t);H(()=>Y(n,`This page describes ${G(f).class===`address`?`address context`:`unresolved events`}, not a device.`)),J(e,t)};X(p,e=>{G(f).class===`legacy`?e(g):(G(f).class===`address`||G(f).class===`event`)&&e(y,1)});var w=V(p,2),T=V(R(w),2),ee=B(T,!0),te=V(T,4),E=B(te,!0),D=V(te,4),ne=B(D),re=V(D,4),ie=B(re,!0),O=V(re,2),ae=e=>{var t=Os(),n=B(V(z(t),2),!0);H(()=>Y(n,G(f).operator_label)),J(e,t)};X(O,e=>{G(f).operator_label!==null&&G(f).operator_label!==void 0&&e(ae)});var oe=V(O,4),se=R(oe),ce=B(se,!0);M(oe);var le=V(oe,4),ue=R(le),de=B(ue,!0);M(le),M(w);var k=V(w,2);{let e=P(()=>G(f).decisions??[]);ra(k,{subjectKind:`reported_subject`,get subjectId(){return G(f).profile_id},kinds:[`note`,`label`],get decisions(){return G(e)},reload:b})}var fe=V(k,2),pe=V(R(fe),2),me=e=>{J(e,ks())},he=e=>{var n=Ns(),r=z(n),i=V(R(r)),a=B(i,!0),o=V(i);M(r);var s=V(r,2);Z(s,21,()=>Object.entries(G(f).latest.attributes??{}),([e,t])=>e,(e,t)=>{var n=P(()=>m(G(t),2));let r=()=>G(n)[0],i=()=>G(n)[1];var a=As(),o=z(a),s=B(o,!0),c=B(V(o,2),!0);H(e=>{Y(s,r()),Y(c,e)},[()=>JSON.stringify(i())]),J(e,a)}),M(s);var c=V(s,2),l=e=>{var t=Ms();Z(t,21,()=>G(f).latest.ports,e=>`${e.proto}/${e.port}`,(e,t)=>{var n=js(),r=B(n);H(()=>Y(r,`${G(t).proto??``}/${G(t).port??``} ${G(t).state??``}${G(t).service?` (${G(t).service})`:``}`)),J(e,n)}),M(t),J(e,t)};X(c,e=>{(G(f).latest.ports??[]).length>0&&e(l)});var u=B(V(c,2));H(()=>{Q(i,`datetime`,G(f).latest.reported_at),Q(i,`title`,G(t)?.utc),Y(a,G(t)?.local),Y(o,` (${G(f).latest.view_role??``}).`),Y(u,`Snapshot covers ${G(f).latest.ports_total??``} ports${G(f).latest.ports_truncated?` (truncated)`:``}.`)}),J(e,n)};X(pe,e=>{G(f).latest===null?e(me):e(he,-1)}),M(fe);var A=V(fe,2),ge=V(R(A),2),j=e=>{J(e,Ps())},_e=e=>{var t=Is();Z(t,21,()=>G(h),e=>e.observation_id,(e,t)=>{let n=P(()=>Ci(G(t).reported_at));var r=Fs(),i=R(r),a=V(i),o=B(a,!0),s=V(a);M(r),H(()=>{Y(i,`${G(t).event_id??``} (${G(t).view_role??``}) — `),Q(a,`datetime`,G(t).reported_at),Q(a,`title`,G(n).utc),Y(o,G(n).local),Y(s,`, ${G(t).ports_total??``} ports${G(t).ports_truncated?` (truncated)`:``}`)}),J(e,r)}),M(t),J(e,t)};X(ge,e=>{G(h).length===0?e(j):e(_e,-1)});var ve=V(ge,2),ye=e=>{var t=Ts(),n=R(t),r=V(n);M(t),H(()=>Y(n,`Older page failed (${G(c)??``}). `)),K(`click`,r,x),J(e,t)};X(ve,e=>{G(c)!==null&&e(ye)});var be=V(ve,2),xe=e=>{var t=Ls();t.textContent=`History capped at 200 rows; use the API for the full set.`,J(e,t)},Se=e=>{var t=Rs();K(`click`,t,x),J(e,t)},Ce=e=>{J(e,zs())};X(be,e=>{G(v)?e(xe):G(_)?e(Ce,-1):e(Se,1)}),M(A),M(n),H(e=>{Y(l,`${G(f).profile_id??``} — ${e??``}`),Y(ee,G(f).source_scope),Y(E,G(f).reported_id),Y(ne,`${G(f).reported_source??``} (confidence: ${(G(f).reported_confidence||`unknown`)??``})`),Y(ie,G(f).association_status),Q(se,`datetime`,G(f).first_report_at),Q(se,`title`,G(S)?.utc),Y(ce,G(S)?.local),Q(ue,`datetime`,G(f).last_report_at),Q(ue,`title`,G(C)?.utc),Y(de,G(C)?.local)},[()=>Ti(G(f).class)]),J(e,n)};X(T,e=>{G(l)!==null&&G(f)===null?e(ee):G(f)===null?e(te,1):e(E,-1)}),J(e,w),Be()}ur([`click`]);function Hs(e){return e==null?!1:e.status!==`completed`||(e.error??``)!==``}var Us=q(`

                                                    `,1),Ws=q(`

                                                    Loading health…

                                                    `),Gs=q(`
                                                  1. `),Ks=q(`
                                                      `),qs=q(`

                                                      `),Js=q(`

                                                      `),Ys=q(`

                                                      No sources registered.

                                                      `),Xs=q(`
                                                      Last completed scan
                                                      Scan result
                                                      Reported host records
                                                      Signing clock offset
                                                      `,1),Zs=q(`
                                                      Last report
                                                      `,1),Qs=q(`
                                                      Delivery
                                                      `,1),$s=q(`

                                                      Liveness
                                                      Identity namespace
                                                      Identity capability
                                                      Heartbeat
                                                      `),ec=q(`
                                                      `,1),tc=q(`

                                                      ; policy only — it never proves a per-profile boundary. Ask the API for per-profile history.

                                                      `),nc=q(`

                                                      `),rc=q(`

                                                      No fully successful purge recorded yet.

                                                      `),ic=q(`
                                                      Publisher
                                                      Posted
                                                      Dropped
                                                      Failed
                                                      `),ac=q(`

                                                      Summary

                                                      Coverage
                                                      Fallback briefings · 1h
                                                      Awaiting operator
                                                      Forward errors · 1h
                                                      Ingest queue
                                                      Pipeline (process lifetime)
                                                      Started
                                                      Ingest accepted
                                                      Ingest duplicates
                                                      Ingest rejected
                                                      Ingest saturated
                                                      Provider calls
                                                      Provider fallbacks
                                                      Redaction blocked
                                                      Writer saturated
                                                      Purge
                                                      `,1),oc=q(`

                                                      Health

                                                      Source coverage, processing, and retention. Liveness describes the source, not host presence.

                                                      `);function sc(e,t){ze(t,!0);let n=$(t,`initial`,3,null),r=bi();ci(()=>r.invalidate());let i=I(null),a=I(null),o=I(null),s=P(()=>G(i)??n());async function c(){r.invalidate();let e=r.start();L(a,null),L(o,null);try{let t=await vi();r.current(e)&&L(i,t,!0)}catch(t){if(r.current(e)){let e=t instanceof Error?t.message:String(t);G(s)===null?L(a,e,!0):L(o,e,!0)}}}si(()=>{n()===null&&c()});let l=!1;un(()=>{let e=t.stream===`live`,n=e&&!l&&G(i)!==null;l=e,n&&c()});function u(e){return`${Math.floor(e/86400)} days`}var d=oc(),f=V(R(d),4),p=e=>{var t=Us(),n=z(t),r=B(n),i=V(n,2);H(()=>Y(r,`The bridge did not return health (${G(a)??``}).`)),K(`click`,i,c),J(e,t)},h=e=>{J(e,Ws())},g=e=>{var t=ac(),n=z(t),r=V(R(n),2),i=R(r),a=V(R(i)),l=R(a),d=B(l,!0);M(a),M(i);var f=V(i,2),p=V(R(f)),h=R(p),g=B(V(h));M(p),M(f);var _=V(f,2),v=B(V(R(_)),!0);M(_);var y=V(_,2),b=B(V(R(y)),!0);M(y);var x=V(y,2),S=V(R(x)),C=R(S),w=B(V(C));M(S),M(x),M(r);var T=V(r,2),ee=e=>{var t=Ks();Z(t,20,()=>G(s).coverage_reasons,e=>e,(e,t)=>{var n=Gs(),r=B(n,!0);H(()=>Y(r,t)),J(e,n)}),M(t),J(e,t)};X(T,e=>{(G(s).coverage_reasons??[]).length>0&&e(ee)});var te=V(T,2),E=e=>{let t=P(()=>Ci(G(s).purge.last.finished_at??G(s).purge.last.started_at));var n=qs(),r=B(n);H(()=>Y(r,`Latest purge run ${G(s).purge.last.status??``} diff --git a/ui/src/lib/Detail.svelte b/ui/src/lib/Detail.svelte index f2deeb9..8daeefd 100644 --- a/ui/src/lib/Detail.svelte +++ b/ui/src/lib/Detail.svelte @@ -297,7 +297,7 @@ .identity-grid > div { min-width: 0; } .identity-grid dt { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 7px; } .identity-grid dd { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: 15px; font-weight: 600; margin: 0; } - .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } + /* .mono now lives in styles/utilities.css (single definition). */ .identity-group { grid-column: 1 / -1; } .identity-group p { margin: 5px 0 0; } .evidence-index { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px; margin-top: 24px; padding-top: 14px; border-top: 1px solid var(--border); color: var(--muted); font-size: 13px; } diff --git a/ui/src/lib/ReportView.svelte b/ui/src/lib/ReportView.svelte index 86052d4..72f2dea 100644 --- a/ui/src/lib/ReportView.svelte +++ b/ui/src/lib/ReportView.svelte @@ -61,5 +61,5 @@ .attributes { padding: 0; border: 0; } .attributes summary { font-size: 12px; color: var(--accent); } .profile-chip { display: flex; flex-wrap: wrap; gap: 5px 12px; border: 1px solid var(--border); background: var(--accent-soft); border-radius: 9px; padding: 10px 12px; text-decoration: none; font-size: 13px; } - .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; } + /* .mono now lives in styles/utilities.css (single definition). */ diff --git a/ui/src/main.js b/ui/src/main.js index 2d09e34..b3ce16e 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -1,6 +1,8 @@ import { mount } from 'svelte'; import App from './App.svelte'; -import './style.css'; +import './styles/tokens.css'; +import './styles/base.css'; +import './styles/utilities.css'; // Step 22 entry: mounts the shell only. Hash routes and screens land // in Steps 23-24; the entry shape stays stable so they slot in here. diff --git a/ui/src/style.css b/ui/src/styles/base.css similarity index 90% rename from ui/src/style.css rename to ui/src/styles/base.css index 24aefed..175eb9f 100644 --- a/ui/src/style.css +++ b/ui/src/styles/base.css @@ -1,27 +1,6 @@ +/* Global element defaults. Component classes move out in Phases 3-5; see inventory in the Phase 1 report. */ /* Shared local-only viewer styles; no remote fonts or inline assets. */ -:root { - font: 14px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; - color: var(--text); - background: var(--canvas); - --canvas: #f4f5f7; - --surface: #fff; - --subtle: #f8f9fb; - --text: #14171c; - --muted: #535d6b; - --border: #d9dde3; - --accent: #1b4f9c; - --accent-soft: #e8eef7; - --success: #225b38; - --success-bg: #e8f3ec; - --success-border: #9ec4aa; - --warning: #6b4700; - --warning-bg: #fff4d6; - --warning-border: #d9b94f; - --danger: #9e302c; - --danger-bg: #fcecea; - --danger-border: #dea49d; - color-scheme: light dark; -} + * { box-sizing: border-box; } @@ -717,46 +696,7 @@ label input:not([type=checkbox],[type=radio]),label select,label textarea { grid-template-columns: repeat(2,minmax(0,1fr)); } } -@media(prefers-color-scheme:dark) { - :root { - --canvas: #15191f; - --surface: #1e242d; - --subtle: #252c36; - --text: #edf0f5; - --muted: #b4bfce; - --border: #414b5a; - --accent: #9dc2ff; - --accent-soft: #293c58; - --success: #abd7b8; - --success-bg: #213b2b; - --success-border: #4e7a5d; - --warning: #f1d38e; - --warning-bg: #3d321e; - --warning-border: #8c7237; - --danger: #f3b3ad; - --danger-bg: #422b2b; - --danger-border: #9e625b; - } -} -@media(prefers-reduced-motion:reduce) { - *,*::before,*::after { - animation: none!important; - transition: none!important; - scroll-behavior: auto!important; - } -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip-path: inset(50%); - white-space: nowrap; - border: 0; -} .review-workspace[data-empty='true'] { grid-template-columns: minmax(0, 1fr); diff --git a/ui/src/styles/tokens.css b/ui/src/styles/tokens.css new file mode 100644 index 0000000..c10f572 --- /dev/null +++ b/ui/src/styles/tokens.css @@ -0,0 +1,52 @@ +/* Shared local-only viewer styles; no remote fonts or inline assets. */ +:root { + font: 14px/1.5 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; + color: var(--text); + background: var(--canvas); + --canvas: #f4f5f7; + --surface: #fff; + --subtle: #f8f9fb; + --text: #14171c; + --muted: #535d6b; + --border: #d9dde3; + --accent: #1b4f9c; + --accent-soft: #e8eef7; + --success: #225b38; + --success-bg: #e8f3ec; + --success-border: #9ec4aa; + --warning: #6b4700; + --warning-bg: #fff4d6; + --warning-border: #d9b94f; + --danger: #9e302c; + --danger-bg: #fcecea; + --danger-border: #dea49d; + color-scheme: light dark; +} +@media(prefers-color-scheme:dark) { + :root { + --canvas: #15191f; + --surface: #1e242d; + --subtle: #252c36; + --text: #edf0f5; + --muted: #b4bfce; + --border: #414b5a; + --accent: #9dc2ff; + --accent-soft: #293c58; + --success: #abd7b8; + --success-bg: #213b2b; + --success-border: #4e7a5d; + --warning: #f1d38e; + --warning-bg: #3d321e; + --warning-border: #8c7237; + --danger: #f3b3ad; + --danger-bg: #422b2b; + --danger-border: #9e625b; + } +} +@media(prefers-reduced-motion:reduce) { + *,*::before,*::after { + animation: none!important; + transition: none!important; + scroll-behavior: auto!important; + } +} diff --git a/ui/src/styles/utilities.css b/ui/src/styles/utilities.css new file mode 100644 index 0000000..37c52e8 --- /dev/null +++ b/ui/src/styles/utilities.css @@ -0,0 +1,14 @@ +/* Single-purpose utilities. Everything else lives with its component. */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; } From 1c2c0cee7e70414bd4e1dd3350e87f26f2145e7f Mon Sep 17 00:00:00 2001 From: ostefani Date: Mon, 9 Nov 2026 07:33:36 -0800 Subject: [PATCH 03/12] core: enforce UI layer import boundaries. --- ui/eslint.config.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ui/eslint.config.js b/ui/eslint.config.js index 50234b6..00e26b9 100644 --- a/ui/eslint.config.js +++ b/ui/eslint.config.js @@ -35,5 +35,49 @@ export default [ languageOptions: { globals: { ...globals.node } } + }, + { + // Folder-split boundaries (CONVENTIONS): components render props and + // never fetch; containers orchestrate and never import each other + // (App alone composes pages); lib stays free of UI layers. + // These patterns match nothing until Phases 2-5 create the folders, + // so they lock the contract before the first move. + files: ['src/components/**/*.{js,svelte}'], + rules: { + 'no-restricted-imports': ['error', { + patterns: [ + { group: ['$api', '$api/*'], message: 'components render props; fetching lives in containers.' }, + { group: ['$containers', '$containers/*'], message: 'components never import containers.' }, + { group: ['$lib/events/context*', '**/events/context*'], message: 'components read connection state from props, not context.' } + ] + }] + } + }, + { + files: ['src/containers/**/*.{js,svelte}'], + rules: { + 'no-restricted-imports': ['error', { + patterns: [ + { group: ['$containers', '$containers/*'], message: 'containers never import each other; App alone composes pages.' } + ] + }] + } + }, + { + // App is the single composer: it may import page containers. + files: ['src/containers/App/**/*.{js,svelte}'], + rules: { + 'no-restricted-imports': 'off' + } + }, + { + files: ['src/lib/**/*.{js,svelte.js}'], + rules: { + 'no-restricted-imports': ['error', { + patterns: [ + { group: ['$components', '$components/*', '$containers', '$containers/*'], message: 'lib stays UI-free; containers compose lib, never the reverse.' } + ] + }] + } } ]; From 177232e90a4f8cf3f613e9b041296ee63792775e Mon Sep 17 00:00:00 2001 From: ostefani Date: Mon, 9 Nov 2026 07:39:57 -0800 Subject: [PATCH 04/12] core: document UI folder-split conventions. --- ui/CONVENTIONS.md | 79 +++++++++++++++++++ ui/package.json | 3 +- ui/scripts/characterize.mjs | 150 ++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 ui/CONVENTIONS.md create mode 100644 ui/scripts/characterize.mjs diff --git a/ui/CONVENTIONS.md b/ui/CONVENTIONS.md new file mode 100644 index 0000000..0ca6c2c --- /dev/null +++ b/ui/CONVENTIONS.md @@ -0,0 +1,79 @@ +# UI conventions — folder split + +How the Svelte viewer is organized, and the rules every change must hold. +Governing contracts: [Designv9 §8](../.innerdoc/Designv9.md#8-svelte-viewer-and-product-language) +(dependency allowlist, JS only, 200 KiB gzip, hash routing), +[CONTRIBUTING](../CONTRIBUTING.md) (committed `dist`, `make ui-check`). + +## Roles + +| | `containers/` | `components/` | +|---|---|---| +| Role | Smart: fetch, hold state, subscribe, post, wire everything together | Dumb: render props, emit callbacks | +| May import | `$api`, `$lib`, components, context | Other components, `$lib/format` (pure helpers) | +| May not import | Other containers (`App` alone composes pages) | `$api`, `$containers`, context, `window` listeners | +| Local state | Only what orchestration needs | UI-only state (open/closed, a form draft, a copied flag) | + +Enforced by `eslint.config.js` (`no-restricted-imports`); `App` has its own +override as the single composer. `src/lib` holds no markup or CSS and never +imports UI layers. + +## Layout + +Every unit lives in its own folder — components included — so styles, +tests and the entry point travel together: + +``` +components// Name.svelte Name.module.css index.js +containers// Name.svelte (Name.css only when the container has its own markup) +``` + +Component props: data in, `on` callbacks out (`onselect`, +`ondecided`). No `reload` callback props — containers own refetching. +Appearance variants ride on props (`variant`, `layout`), never on +parent selectors reaching into children. Shared derivation lives in +`$lib/format/*` with plain-node unit coverage, not inside components. + +## CSS + +- CSS Modules (`*.module.css`), Vite-native. No `svelte-preprocess`: + it is outside the Design §8.1 allowlist, and without it ` diff --git a/ui/src/components/AppFooter/AppFooter.module.css b/ui/src/components/AppFooter/AppFooter.module.css new file mode 100644 index 0000000..883ca46 --- /dev/null +++ b/ui/src/components/AppFooter/AppFooter.module.css @@ -0,0 +1,16 @@ +.footer { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 8px; + padding: 16px 28px; + border-top: 1px solid var(--border); + font-size: 12px; + color: var(--muted); +} + +@media (max-width: 640px) { + .footer { + padding: 16px; + } +} diff --git a/ui/src/components/AppFooter/AppFooter.svelte b/ui/src/components/AppFooter/AppFooter.svelte new file mode 100644 index 0000000..69903ce --- /dev/null +++ b/ui/src/components/AppFooter/AppFooter.svelte @@ -0,0 +1,7 @@ + + +
                                                      WatcherBridge · Local review workspaceScanner reports are evidence, not a live inventory.
                                                      diff --git a/ui/src/components/AppFooter/index.js b/ui/src/components/AppFooter/index.js new file mode 100644 index 0000000..b9b7c0a --- /dev/null +++ b/ui/src/components/AppFooter/index.js @@ -0,0 +1 @@ +export { default } from './AppFooter.svelte'; diff --git a/ui/src/components/AppHeader/AppHeader.module.css b/ui/src/components/AppHeader/AppHeader.module.css new file mode 100644 index 0000000..dbd9780 --- /dev/null +++ b/ui/src/components/AppHeader/AppHeader.module.css @@ -0,0 +1,73 @@ +.header { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 12px 28px; + padding: 12px 28px; + background: var(--surface); + border-bottom: 1px solid var(--border); +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + color: var(--text); + font-size: 18px; + font-weight: 700; + text-decoration: none; + letter-spacing: -0.5px; +} + +.brandMark { + display: grid; + place-items: center; + width: 30px; + height: 30px; + border-radius: 8px; + background: var(--text); + color: var(--surface); + font-size: 17px; +} + +.header nav { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.header nav a, +.header nav button { + display: inline-flex; + align-items: center; + padding: 8px 14px; + min-height: 44px; + border: 0; + border-radius: 8px; + background: transparent; + text-decoration: none; + color: var(--muted); +} + +.header nav a[aria-current='page'] { + background: var(--accent-soft); + color: var(--accent); + font-weight: 650; +} + +@media (max-width: 640px) { + .header { + padding: 12px 16px; + gap: 10px; + } + + .header nav { + order: 3; + width: 100%; + } + + .header nav a, + .header nav button { + padding: 8px 10px; + } +} diff --git a/ui/src/components/AppHeader/AppHeader.svelte b/ui/src/components/AppHeader/AppHeader.svelte new file mode 100644 index 0000000..cb9b991 --- /dev/null +++ b/ui/src/components/AppHeader/AppHeader.svelte @@ -0,0 +1,20 @@ + + +
                                                      + WatcherBridge + + +
                                                      diff --git a/ui/src/components/AppHeader/index.js b/ui/src/components/AppHeader/index.js new file mode 100644 index 0000000..3b4156c --- /dev/null +++ b/ui/src/components/AppHeader/index.js @@ -0,0 +1 @@ +export { default } from './AppHeader.svelte'; diff --git a/ui/src/components/AttentionList/AttentionList.svelte b/ui/src/components/AttentionList/AttentionList.svelte new file mode 100644 index 0000000..e62f53b --- /dev/null +++ b/ui/src/components/AttentionList/AttentionList.svelte @@ -0,0 +1,59 @@ + + +
                                                      +

                                                      Needs attention ({items.length})

                                                      + {#if items.length === 0} +

                                                      Nothing flagged. New briefings appear here when the bridge reports them.

                                                      + {:else} +
                                                        + {#each items as item (item.id)} +
                                                      1. + {#snippet row()} + {item.priority} + {item.kind === 'incident' ? 'Incident · ' : ''}{item.kind === 'briefing' ? (titles[item.id] ?? reviewTitle(item.change_kind)) : changeLabel(item.change_kind)}{item.id} + {item.status} + {/snippet} + {#if item.kind === 'briefing' || item.kind === 'incident'} + {#if onselect} + + {:else} + {@render row()} + {/if} + {:else} +
                                                        {@render row()}
                                                        + {/if} + {#if item.kind === 'incident'} + {@const open = expanded[item.id] ?? false} + + {@render members?.(item.id, open)} + {/if} +
                                                      2. + {/each} +
                                                      + {/if} +
                                                      diff --git a/ui/src/components/AttentionList/index.js b/ui/src/components/AttentionList/index.js new file mode 100644 index 0000000..77931d3 --- /dev/null +++ b/ui/src/components/AttentionList/index.js @@ -0,0 +1 @@ +export { default } from './AttentionList.svelte'; diff --git a/ui/src/components/BriefingSummary/BriefingSummary.svelte b/ui/src/components/BriefingSummary/BriefingSummary.svelte new file mode 100644 index 0000000..9f502df --- /dev/null +++ b/ui/src/components/BriefingSummary/BriefingSummary.svelte @@ -0,0 +1,39 @@ + + +
                                                      +
                                                      +

                                                      {title}

                                                      + {priority} priority +
                                                      +

                                                      {description}

                                                      + +
                                                      + {eventCount}{eventsTruncated ? '+' : ''} source report{eventCount === 1 ? '' : 's'} + {#if primaryEvent} + + {:else if eventCount > 0} + + {/if} +
                                                      +
                                                      diff --git a/ui/src/components/BriefingSummary/index.js b/ui/src/components/BriefingSummary/index.js new file mode 100644 index 0000000..7b22186 --- /dev/null +++ b/ui/src/components/BriefingSummary/index.js @@ -0,0 +1 @@ +export { default } from './BriefingSummary.svelte'; diff --git a/ui/src/lib/ConnectionStatus.svelte b/ui/src/components/ConnectionStatus/ConnectionStatus.svelte similarity index 100% rename from ui/src/lib/ConnectionStatus.svelte rename to ui/src/components/ConnectionStatus/ConnectionStatus.svelte diff --git a/ui/src/components/ConnectionStatus/index.js b/ui/src/components/ConnectionStatus/index.js new file mode 100644 index 0000000..1ae2e09 --- /dev/null +++ b/ui/src/components/ConnectionStatus/index.js @@ -0,0 +1 @@ +export { default } from './ConnectionStatus.svelte'; diff --git a/ui/src/components/CopyValue/CopyValue.module.css b/ui/src/components/CopyValue/CopyValue.module.css new file mode 100644 index 0000000..f9af96c --- /dev/null +++ b/ui/src/components/CopyValue/CopyValue.module.css @@ -0,0 +1,13 @@ +:global(.wb-clipboard-buffer) { + position: fixed; + opacity: 0; + pointer-events: none; +} + +.copyBtn { + font-size: 12px; + padding: 4px 10px; + min-height: 32px; + color: var(--accent); + background: var(--accent-soft); +} diff --git a/ui/src/lib/CopyValue.svelte b/ui/src/components/CopyValue/CopyValue.svelte similarity index 82% rename from ui/src/lib/CopyValue.svelte rename to ui/src/components/CopyValue/CopyValue.svelte index a79521c..a429174 100644 --- a/ui/src/lib/CopyValue.svelte +++ b/ui/src/components/CopyValue/CopyValue.svelte @@ -1,5 +1,6 @@ - {#if state === 'failed'} Could not copy the {name}; select and copy it manually. {/if} - diff --git a/ui/src/components/CopyValue/index.js b/ui/src/components/CopyValue/index.js new file mode 100644 index 0000000..bf88580 --- /dev/null +++ b/ui/src/components/CopyValue/index.js @@ -0,0 +1 @@ +export { default } from './CopyValue.svelte'; diff --git a/ui/src/components/DecisionComposer/DecisionComposer.module.css b/ui/src/components/DecisionComposer/DecisionComposer.module.css new file mode 100644 index 0000000..2948fb2 --- /dev/null +++ b/ui/src/components/DecisionComposer/DecisionComposer.module.css @@ -0,0 +1,64 @@ +.composer { + display: contents; +} + +.composer fieldset { + display: grid; + gap: 0; + border: 1px solid var(--border); + border-radius: 11px; + padding: 6px 0; + margin: 0 0 12px; + min-width: 0; +} + +.composer fieldset label { + display: grid; + grid-template-columns: 20px minmax(0,1fr); + align-items: center; + gap: 2px 12px; + padding: 12px 16px; + margin: 0; + cursor: pointer; + min-height: 66px; +} + +.composer fieldset label:hover { + background: var(--subtle); +} + +.composer fieldset label.selected { + background: var(--accent-soft); +} + +.composer fieldset input { + grid-row: 1 / span 2; + width: 18px; + height: 18px; + padding: 0; + margin: 0; + accent-color: var(--accent); +} + +.decisionLabel { + font-weight: 600; +} + +.decisionHint { + grid-column: 2; + font-size: 12px; + color: var(--muted); +} + +.composer button[type=submit] { + width: 100%; + margin-top: 8px; + color: var(--surface); + background: var(--text); + border-color: var(--text); + font-weight: 700; +} + +.composer button[type=submit]:disabled { + opacity: .45; +} diff --git a/ui/src/components/DecisionComposer/DecisionComposer.svelte b/ui/src/components/DecisionComposer/DecisionComposer.svelte new file mode 100644 index 0000000..f5cfb57 --- /dev/null +++ b/ui/src/components/DecisionComposer/DecisionComposer.svelte @@ -0,0 +1,130 @@ + + +
                                                      +
                                                      + Choose a decision + {#each kinds as k (k)} + + {/each} +
                                                      +

                                                      {kind === null ? 'Choose a verdict to record it.' : (kindHelp[kind] ?? KIND_HELP[kind] ?? '')}

                                                      + {#if memberScope} + + {/if} + {#if outcomeNote !== null} +

                                                      {outcomeNote}

                                                      + {/if} + {#if kind === 'snooze'} + + {/if} + {#if needsText} + + {/if} + {#if formError !== null} +

                                                      {formError}

                                                      + {/if} + +
                                                      diff --git a/ui/src/components/DecisionComposer/index.js b/ui/src/components/DecisionComposer/index.js new file mode 100644 index 0000000..a0c6c7b --- /dev/null +++ b/ui/src/components/DecisionComposer/index.js @@ -0,0 +1 @@ +export { default } from './DecisionComposer.svelte'; diff --git a/ui/src/components/DecisionStatus/DecisionStatus.module.css b/ui/src/components/DecisionStatus/DecisionStatus.module.css new file mode 100644 index 0000000..49dfb6d --- /dev/null +++ b/ui/src/components/DecisionStatus/DecisionStatus.module.css @@ -0,0 +1,33 @@ +.title { + font-size: 20px; + margin-bottom: 10px; + letter-spacing: -.4px; +} + +.status { + margin-bottom: 20px; +} + +.status .review { + padding: 5px 11px; + font-size: 12px; +} + +.status .review[data-review=unreviewed], +.status .review[data-review=snooze] { + color: var(--warning); + border-color: var(--warning-border); + background: var(--warning-bg); +} + +.status .review[data-review=expected] { + color: var(--success); + border-color: var(--success-border); + background: var(--success-bg); +} + +.status .review[data-review=not_expected] { + color: var(--danger); + border-color: var(--danger-border); + background: var(--danger-bg); +} diff --git a/ui/src/components/DecisionStatus/DecisionStatus.svelte b/ui/src/components/DecisionStatus/DecisionStatus.svelte new file mode 100644 index 0000000..4c065f8 --- /dev/null +++ b/ui/src/components/DecisionStatus/DecisionStatus.svelte @@ -0,0 +1,25 @@ + + +

                                                      {title}

                                                      +

                                                      Status: {statusLine()}

                                                      diff --git a/ui/src/components/DecisionStatus/index.js b/ui/src/components/DecisionStatus/index.js new file mode 100644 index 0000000..4574547 --- /dev/null +++ b/ui/src/components/DecisionStatus/index.js @@ -0,0 +1 @@ +export { default } from './DecisionStatus.svelte'; diff --git a/ui/src/components/DecisionTrail/DecisionTrail.module.css b/ui/src/components/DecisionTrail/DecisionTrail.module.css new file mode 100644 index 0000000..187f78a --- /dev/null +++ b/ui/src/components/DecisionTrail/DecisionTrail.module.css @@ -0,0 +1,51 @@ +.historyHeading { + margin: 24px 0 12px; + padding-top: 20px; + border-top: 1px solid var(--border); + text-transform: uppercase; + letter-spacing: .07em; + font-size: 11px; + color: var(--muted); +} + +.emptyHistory { + color: var(--muted); + font-size: 13px; + margin-bottom: 0; +} + +.trail { + padding: 0; + list-style: none; +} + +.trail li { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + border-top: 1px solid var(--border); + padding: 12px 0; + font-size: 13px; +} + +.trail time { + color: var(--muted); + font-size: 11px; +} + +.trail .text { + flex-basis: 100%; + overflow-wrap: anywhere; +} + +.trail .until { + color: var(--muted); + font-size: 12px; +} + +.trail button { + min-height: 32px; + padding: 4px 8px; + font-size: 12px; +} diff --git a/ui/src/components/DecisionTrail/DecisionTrail.svelte b/ui/src/components/DecisionTrail/DecisionTrail.svelte new file mode 100644 index 0000000..d6809bb --- /dev/null +++ b/ui/src/components/DecisionTrail/DecisionTrail.svelte @@ -0,0 +1,61 @@ + + +{#if card}

                                                      Recorded decisions

                                                      {/if} +{#if trail.length === 0} +

                                                      No decisions recorded.

                                                      +{:else} +
                                                        + {#each trail as d (d.decision_id)} +
                                                      1. + {d.kind.replace('_', ' ')} + {#if d.undone}undone{/if} + {#if d.text}{d.text}{/if} + {#if d.until}until {d.until}{/if} + + {#if !d.undone && d.kind !== 'undo'} + + {/if} +
                                                      2. + {/each} +
                                                      + {#if !exhausted} + + {/if} + {#if showNewer} + + {/if} + {#if note !== null} +

                                                      {note}

                                                      + {/if} +{/if} +{#if listError !== null} +

                                                      {listError}

                                                      +{/if} diff --git a/ui/src/components/DecisionTrail/index.js b/ui/src/components/DecisionTrail/index.js new file mode 100644 index 0000000..b23db20 --- /dev/null +++ b/ui/src/components/DecisionTrail/index.js @@ -0,0 +1 @@ +export { default } from './DecisionTrail.svelte'; diff --git a/ui/src/components/DeviceList/DeviceList.svelte b/ui/src/components/DeviceList/DeviceList.svelte new file mode 100644 index 0000000..c7518dc --- /dev/null +++ b/ui/src/components/DeviceList/DeviceList.svelte @@ -0,0 +1,43 @@ + + +{#if stale} +

                                                      Connection recovered — this list may miss new reports.

                                                      +{/if} +{#if devices.length === 0} +

                                                      No reported subjects retained.

                                                      +{:else} +
                                                        + {#each devices as dev (dev.profile_id)} + + {/each} +
                                                      +{/if} +{#if error !== null} +

                                                      Page failed ({error}) — earlier rows kept.

                                                      +{/if} +{#if capped} +

                                                      List capped at {cap} rows; use the API for the full set.

                                                      +{:else if !exhausted} + +{:else} +

                                                      End of retained list.

                                                      +{/if} diff --git a/ui/src/components/DeviceList/index.js b/ui/src/components/DeviceList/index.js new file mode 100644 index 0000000..7e0ca6e --- /dev/null +++ b/ui/src/components/DeviceList/index.js @@ -0,0 +1 @@ +export { default } from './DeviceList.svelte'; diff --git a/ui/src/components/DeviceProfile/DeviceProfile.svelte b/ui/src/components/DeviceProfile/DeviceProfile.svelte new file mode 100644 index 0000000..f9ae57d --- /dev/null +++ b/ui/src/components/DeviceProfile/DeviceProfile.svelte @@ -0,0 +1,35 @@ + + +

                                                      {profile.profile_id} — {classLabel(profile.class)}

                                                      +{#if profile.class === 'legacy'} +

                                                      Earlier bridge-inferred association; not used for current identity.

                                                      +{:else if profile.class === 'address' || profile.class === 'event'} +

                                                      This page describes {profile.class === 'address' ? 'address context' : 'unresolved events'}, not a device.

                                                      +{/if} +
                                                      +
                                                      Source scope
                                                      +
                                                      {profile.source_scope}
                                                      +
                                                      Upstream reference
                                                      +
                                                      {profile.reported_id}
                                                      +
                                                      Reported by
                                                      +
                                                      {profile.reported_source} (confidence: {profile.reported_confidence || 'unknown'})
                                                      +
                                                      Association
                                                      +
                                                      {profile.association_status}
                                                      + {#if profile.operator_label !== null && profile.operator_label !== undefined} +
                                                      Your label
                                                      +
                                                      {profile.operator_label}
                                                      + {/if} +
                                                      First report
                                                      +
                                                      +
                                                      Last report
                                                      +
                                                      +
                                                      diff --git a/ui/src/components/DeviceProfile/index.js b/ui/src/components/DeviceProfile/index.js new file mode 100644 index 0000000..d97ed8d --- /dev/null +++ b/ui/src/components/DeviceProfile/index.js @@ -0,0 +1 @@ +export { default } from './DeviceProfile.svelte'; diff --git a/ui/src/components/DeviceRow/DeviceRow.svelte b/ui/src/components/DeviceRow/DeviceRow.svelte new file mode 100644 index 0000000..530ef89 --- /dev/null +++ b/ui/src/components/DeviceRow/DeviceRow.svelte @@ -0,0 +1,17 @@ + + +
                                                    • + + + {dev.operator_label || dev.reported_id || 'Unlabelled subject'}{dev.profile_id}{classLabel(dev.class)} + Last report + + +
                                                    • diff --git a/ui/src/components/DeviceRow/index.js b/ui/src/components/DeviceRow/index.js new file mode 100644 index 0000000..e798bf5 --- /dev/null +++ b/ui/src/components/DeviceRow/index.js @@ -0,0 +1 @@ +export { default } from './DeviceRow.svelte'; diff --git a/ui/src/lib/DigestBar.svelte b/ui/src/components/DigestBar/DigestBar.svelte similarity index 95% rename from ui/src/lib/DigestBar.svelte rename to ui/src/components/DigestBar/DigestBar.svelte index ec73b99..9c264ae 100644 --- a/ui/src/lib/DigestBar.svelte +++ b/ui/src/components/DigestBar/DigestBar.svelte @@ -1,5 +1,5 @@ + +
                                                      +

                                                      History

                                                      + {#if observations.length === 0} +

                                                      No retained reports in this window.

                                                      + {:else} +
                                                        + {#each observations as obs (obs.observation_id)} + {@const at = displayTime(obs.reported_at)} +
                                                      1. + {obs.event_id} ({obs.view_role}) — + , + {obs.ports_total} ports{obs.ports_truncated ? ' (truncated)' : ''} +
                                                      2. + {/each} +
                                                      + {/if} + {#if historyError !== null} +

                                                      Older page failed ({historyError}).

                                                      + {/if} + {#if capped} +

                                                      History capped at {cap} rows; use the API for the full set.

                                                      + {:else if !exhausted} + + {:else} +

                                                      End of retained history. Retention policy lives on the Health screen.

                                                      + {/if} +
                                                      diff --git a/ui/src/components/HistoryList/index.js b/ui/src/components/HistoryList/index.js new file mode 100644 index 0000000..1d4f70c --- /dev/null +++ b/ui/src/components/HistoryList/index.js @@ -0,0 +1 @@ +export { default } from './HistoryList.svelte'; diff --git a/ui/src/components/IdentityGrid/IdentityGrid.svelte b/ui/src/components/IdentityGrid/IdentityGrid.svelte new file mode 100644 index 0000000..c20691c --- /dev/null +++ b/ui/src/components/IdentityGrid/IdentityGrid.svelte @@ -0,0 +1,30 @@ + + +
                                                      + {#if identity !== null && identityComplete} +
                                                      Reported host
                                                      {identity.hostname ?? 'Not reported'}
                                                      +
                                                      IP address
                                                      {identity.ip ?? 'Not reported'}{#if identity.ip !== null}{/if}
                                                      +
                                                      MAC address
                                                      {identity.mac ?? 'Not reported'}{#if identity.mac !== null}{/if}
                                                      + {:else} +
                                                      Reported identities
                                                      See the individual report views below

                                                      {identityComplete ? 'Grouped changes can describe different host views.' : 'Only part of the retained evidence is shown.'}

                                                      + {/if} +
                                                      Reported scan target
                                                      {primaryEvent?.target || (eventCount > 1 ? 'Shown per report below' : 'Not reported')}{#if primaryEvent?.target_truncated} (truncated){/if}
                                                      +
                                                      {range?.partial || referencedTruncated ? 'Available report times' : eventCount > 1 ? 'Report time range' : 'Report time'}
                                                      {#if range !== null}{range.local}{:else}Not reported{/if}
                                                      +
                                                      Analysis source
                                                      {sourceLabel(source)}
                                                      +
                                                      diff --git a/ui/src/components/IdentityGrid/index.js b/ui/src/components/IdentityGrid/index.js new file mode 100644 index 0000000..f894b98 --- /dev/null +++ b/ui/src/components/IdentityGrid/index.js @@ -0,0 +1 @@ +export { default } from './IdentityGrid.svelte'; diff --git a/ui/src/components/IncidentEvidence/IncidentEvidence.svelte b/ui/src/components/IncidentEvidence/IncidentEvidence.svelte new file mode 100644 index 0000000..7b98a70 --- /dev/null +++ b/ui/src/components/IncidentEvidence/IncidentEvidence.svelte @@ -0,0 +1,40 @@ + + +
                                                      +

                                                      Evidence

                                                      +

                                                      {countLine(incident)}

                                                      + {#if incident.qualified_refs < incident.member_count} +

                                                      Some members share identity evidence; only distinct qualified references count toward the burst threshold.

                                                      + {/if} + {#if truncated} +

                                                      More members exist than shown.

                                                      + {/if} + {#if members.length === 0} +

                                                      No member evidence retained.

                                                      + {:else} +
                                                        + {#each members as m (m.briefing_id)} + {@const reported = displayTime(m.created_at)} +
                                                      1. + {m.briefing_id} + {m.priority} + + {m.refs.length} qualified reference{m.refs.length === 1 ? '' : 's'} +
                                                      2. + {/each} +
                                                      + {/if} +
                                                      diff --git a/ui/src/components/IncidentEvidence/index.js b/ui/src/components/IncidentEvidence/index.js new file mode 100644 index 0000000..a6292cd --- /dev/null +++ b/ui/src/components/IncidentEvidence/index.js @@ -0,0 +1 @@ +export { default } from './IncidentEvidence.svelte'; diff --git a/ui/src/components/IncidentHeader/IncidentHeader.svelte b/ui/src/components/IncidentHeader/IncidentHeader.svelte new file mode 100644 index 0000000..286b4d0 --- /dev/null +++ b/ui/src/components/IncidentHeader/IncidentHeader.svelte @@ -0,0 +1,26 @@ + + +

                                                      {KIND_LABEL[incident.kind] ?? incident.kind}

                                                      +

                                                      + {incident.status} + Floor: {incident.floor} + {#if incident.status === 'closed' && incident.closed_at} + Closed + {:else} + Open since + {/if} +

                                                      diff --git a/ui/src/components/IncidentHeader/index.js b/ui/src/components/IncidentHeader/index.js new file mode 100644 index 0000000..df254c3 --- /dev/null +++ b/ui/src/components/IncidentHeader/index.js @@ -0,0 +1 @@ +export { default } from './IncidentHeader.svelte'; diff --git a/ui/src/components/IncidentScope/IncidentScope.svelte b/ui/src/components/IncidentScope/IncidentScope.svelte new file mode 100644 index 0000000..c91a761 --- /dev/null +++ b/ui/src/components/IncidentScope/IncidentScope.svelte @@ -0,0 +1,16 @@ + + +
                                                      +

                                                      Scope

                                                      +

                                                      Source: {incident.source_scope}

                                                      +

                                                      Target: {incident.target}

                                                      +

                                                      Window start: · Last member:

                                                      +
                                                      diff --git a/ui/src/components/IncidentScope/index.js b/ui/src/components/IncidentScope/index.js new file mode 100644 index 0000000..906cc1f --- /dev/null +++ b/ui/src/components/IncidentScope/index.js @@ -0,0 +1 @@ +export { default } from './IncidentScope.svelte'; diff --git a/ui/src/components/IncidentSummary/IncidentSummary.svelte b/ui/src/components/IncidentSummary/IncidentSummary.svelte new file mode 100644 index 0000000..ca53b41 --- /dev/null +++ b/ui/src/components/IncidentSummary/IncidentSummary.svelte @@ -0,0 +1,21 @@ + + +
                                                      +

                                                      Summary

                                                      +

                                                      Summary status: {summary.status}

                                                      + {#if summary.summary}

                                                      {summary.summary}

                                                      {/if} + {#if summary.hypothesis}

                                                      Hypothesis: {summary.hypothesis}

                                                      {/if} + {#if summary.confidence}

                                                      Display confidence: {summary.confidence}

                                                      {/if} +

                                                      Priority floor: {summary.floor}. Resolved priority: {summary.final_priority}{summary.suggestion ? ` (suggested ${summary.suggestion}${summary.clamped ? ', clamped' : ''})` : ' (no model raise)'}

                                                      + {#if summary.reason}

                                                      Priority explanation: {summary.reason}

                                                      {/if} +

                                                      Evidence coverage: {summary.sampled} sampled member reports; {summary.omitted} omitted. The sample may not represent every member.

                                                      + {#if summary.citations.length} +

                                                      Cited reports:

                                                      + + {/if} + {#if summary.error}

                                                      {summary.error}

                                                      {/if} +
                                                      diff --git a/ui/src/components/IncidentSummary/index.js b/ui/src/components/IncidentSummary/index.js new file mode 100644 index 0000000..4d71241 --- /dev/null +++ b/ui/src/components/IncidentSummary/index.js @@ -0,0 +1 @@ +export { default } from './IncidentSummary.svelte'; diff --git a/ui/src/components/InvestigatorNotes/InvestigatorNotes.svelte b/ui/src/components/InvestigatorNotes/InvestigatorNotes.svelte new file mode 100644 index 0000000..d589a5a --- /dev/null +++ b/ui/src/components/InvestigatorNotes/InvestigatorNotes.svelte @@ -0,0 +1,41 @@ + + +
                                                      +

                                                      {source === 'agent' ? 'Investigator notes' : 'Analysis and retained context'}

                                                      + {#if likelyExplanation}

                                                      Possible explanation: {likelyExplanation}

                                                      {/if} + {#if confidence}

                                                      Explanation confidence (model self-assessment): {confidence}

                                                      {/if} + {#if limited}

                                                      Investigation has limits. Some lookups could not establish retained context. Missing lookup results do not prove this host has no history.

                                                      {/if} +

                                                      {context}

                                                      + + {#if evidenceRefs?.length} +
                                                      Cited evidence + {#each evidenceRefs as ref, i (`${ref.event_id ?? ref.tool_step}:${i}`)} + + {/each} +
                                                      + {/if} + {#if profiles.length > 0} + + {:else}

                                                      No profile links persisted for this briefing.

                                                      {/if} + {#if profilesTruncated}

                                                      More profile links exist than shown.

                                                      {/if} +
                                                      diff --git a/ui/src/components/InvestigatorNotes/index.js b/ui/src/components/InvestigatorNotes/index.js new file mode 100644 index 0000000..9546152 --- /dev/null +++ b/ui/src/components/InvestigatorNotes/index.js @@ -0,0 +1 @@ +export { default } from './InvestigatorNotes.svelte'; diff --git a/ui/src/components/LatestEvidence/LatestEvidence.svelte b/ui/src/components/LatestEvidence/LatestEvidence.svelte new file mode 100644 index 0000000..3f8ff2b --- /dev/null +++ b/ui/src/components/LatestEvidence/LatestEvidence.svelte @@ -0,0 +1,31 @@ + + +
                                                      +

                                                      Latest evidence

                                                      + {#if latest === null} +

                                                      No retained observation.

                                                      + {:else} +

                                                      Reported ({latest.view_role}).

                                                      +
                                                      + {#each Object.entries(latest.attributes ?? {}) as [key, value] (key)} +
                                                      {key}
                                                      +
                                                      {JSON.stringify(value)}
                                                      + {/each} +
                                                      + {#if (latest.ports ?? []).length > 0} +
                                                        + {#each latest.ports as port (`${port.proto}/${port.port}`)} +
                                                      • {port.proto}/{port.port} {port.state}{port.service ? ` (${port.service})` : ''}
                                                      • + {/each} +
                                                      + {/if} +

                                                      Snapshot covers {latest.ports_total} ports{latest.ports_truncated ? ' (truncated)' : ''}.

                                                      + {/if} +
                                                      diff --git a/ui/src/components/LatestEvidence/index.js b/ui/src/components/LatestEvidence/index.js new file mode 100644 index 0000000..8c1e945 --- /dev/null +++ b/ui/src/components/LatestEvidence/index.js @@ -0,0 +1 @@ +export { default } from './LatestEvidence.svelte'; diff --git a/ui/src/components/LookupReceipts/LookupReceipts.svelte b/ui/src/components/LookupReceipts/LookupReceipts.svelte new file mode 100644 index 0000000..acf9bd8 --- /dev/null +++ b/ui/src/components/LookupReceipts/LookupReceipts.svelte @@ -0,0 +1,16 @@ + + +{#if receipts?.length} +

                                                      What the investigator could establish

                                                      +
                                                        + {#each receipts as receipt, i (i)} +
                                                      • {receipt.summary || 'No interpreted result is available. Open the tool step to inspect the saved response.'}

                                                        {#each receipt.steps as step (step)}{/each}
                                                      • + {/each} +
                                                      +{/if} diff --git a/ui/src/components/LookupReceipts/index.js b/ui/src/components/LookupReceipts/index.js new file mode 100644 index 0000000..2f26e19 --- /dev/null +++ b/ui/src/components/LookupReceipts/index.js @@ -0,0 +1 @@ +export { default } from './LookupReceipts.svelte'; diff --git a/ui/src/components/MemberUndoReview/MemberUndoReview.svelte b/ui/src/components/MemberUndoReview/MemberUndoReview.svelte new file mode 100644 index 0000000..ff66c6a --- /dev/null +++ b/ui/src/components/MemberUndoReview/MemberUndoReview.svelte @@ -0,0 +1,22 @@ + + +

                                                      {memberUndoNote(review)}

                                                      +{#if review.members.length > 0} +
                                                        + {#each review.members as m (m.decision_id)} +
                                                      • {m.subject_id} — {m.kind.replace('_', ' ')}
                                                      • + {/each} +
                                                      +{/if} +{#if review.next} + +{/if} +{#if error !== null}

                                                      {error}

                                                      {/if} diff --git a/ui/src/components/MemberUndoReview/index.js b/ui/src/components/MemberUndoReview/index.js new file mode 100644 index 0000000..f64fe7b --- /dev/null +++ b/ui/src/components/MemberUndoReview/index.js @@ -0,0 +1 @@ +export { default } from './MemberUndoReview.svelte'; diff --git a/ui/src/components/PriorityExplanation/PriorityExplanation.svelte b/ui/src/components/PriorityExplanation/PriorityExplanation.svelte new file mode 100644 index 0000000..2559266 --- /dev/null +++ b/ui/src/components/PriorityExplanation/PriorityExplanation.svelte @@ -0,0 +1,31 @@ + + +
                                                      +

                                                      Why this priority

                                                      +

                                                      {priorityReason}

                                                      +
                                                      Final priority: {priority}{#if baselineRule}{baselineRule}{/if}
                                                      +
                                                      + How the priority was computed +
                                                        +
                                                      1. Baseline floor {baseline}
                                                      2. +
                                                      3. Model suggestion {modelSuggestion ?? 'No model suggestion — baseline stands'}
                                                      4. +
                                                      5. Final priority {priority}
                                                      6. +
                                                      + {#if clamped}

                                                      Suggestion adjusted to respect the baseline floor and one-step raise limit.

                                                      {/if} +
                                                      + {#if fallbackUsed}

                                                      Deterministic fallback text: the model path did not produce this briefing.

                                                      {/if} +
                                                      diff --git a/ui/src/components/PriorityExplanation/index.js b/ui/src/components/PriorityExplanation/index.js new file mode 100644 index 0000000..f460ff4 --- /dev/null +++ b/ui/src/components/PriorityExplanation/index.js @@ -0,0 +1 @@ +export { default } from './PriorityExplanation.svelte'; diff --git a/ui/src/components/RecommendedCheck/RecommendedCheck.svelte b/ui/src/components/RecommendedCheck/RecommendedCheck.svelte new file mode 100644 index 0000000..8cfdb36 --- /dev/null +++ b/ui/src/components/RecommendedCheck/RecommendedCheck.svelte @@ -0,0 +1,19 @@ + + + diff --git a/ui/src/components/RecommendedCheck/index.js b/ui/src/components/RecommendedCheck/index.js new file mode 100644 index 0000000..0dbb119 --- /dev/null +++ b/ui/src/components/RecommendedCheck/index.js @@ -0,0 +1 @@ +export { default } from './RecommendedCheck.svelte'; diff --git a/ui/src/components/ReportView/ReportView.module.css b/ui/src/components/ReportView/ReportView.module.css new file mode 100644 index 0000000..4b15291 --- /dev/null +++ b/ui/src/components/ReportView/ReportView.module.css @@ -0,0 +1,95 @@ +.card { + border: 1px solid var(--border); + border-radius: 11px; + padding: 0 16px; + margin: 12px 0 0; + background: var(--surface); + overflow-wrap: anywhere; +} + +.card summary { + padding: 16px 0; +} + +.heading { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + max-width: 100%; + vertical-align: top; +} + +.reportId { + font-weight: 600; +} + +.reportTime { + display: block; + font-size: 12px; + font-weight: 400; + color: var(--muted); + margin: 7px 0 0 16px; +} + +.body { + padding: 16px 0 6px; + border-top: 1px solid var(--border); +} + +.view { + border-top: 1px solid var(--border); + padding: 16px 0 0; + margin: 16px 0; +} + +.view h4 { + font-size: 14px; +} + +.identity { + display: grid; + grid-template-columns: repeat(auto-fit,minmax(min(100%,180px),1fr)); + gap: 14px; + margin: 0 0 16px; +} + +.identity dt { + font-size: 11px; + text-transform: uppercase; + letter-spacing: .05em; + margin-bottom: 4px; +} + +.identity dd { + margin: 0; +} + +.confidence { + display: flex; + gap: 8px; + align-items: center; + flex-wrap: wrap; +} + +.attributes { + padding: 0; + border: 0; +} + +.attributes summary { + font-size: 12px; + color: var(--accent); +} + +.profileChip { + display: flex; + flex-wrap: wrap; + gap: 5px 12px; + border: 1px solid var(--border); + background: var(--accent-soft); + border-radius: 9px; + padding: 10px 12px; + text-decoration: none; + font-size: 13px; +} diff --git a/ui/src/components/ReportView/ReportView.svelte b/ui/src/components/ReportView/ReportView.svelte new file mode 100644 index 0000000..f6437ad --- /dev/null +++ b/ui/src/components/ReportView/ReportView.svelte @@ -0,0 +1,47 @@ + + +
                                                      + + {ref.event_id}{ref.event_type} + Reported + +
                                                      +

                                                      Reported scan target: {ref.target || 'Not reported'}{#if ref.target_truncated} (truncated){/if}
                                                      Received

                                                      + {#if views.length === 0} +

                                                      No retained observation for this event. Its reference is retained, but a saved scanner view is unavailable.

                                                      + {:else} + {#each views as view (`${view.event_id}:${view.view_role}`)} + {@const attrs = view.attributes ?? {}} +
                                                      +

                                                      {view.view_role === 'previous' ? 'Before the reported change' : view.view_role === 'current' ? 'After the reported change' : 'Reported host'}

                                                      +
                                                      +
                                                      Hostname
                                                      {value(attrs.hostname)}
                                                      +
                                                      IP address
                                                      {value(attrs.ip)}
                                                      +
                                                      MAC address
                                                      {value(attrs.mac)}
                                                      + {#if attrs.vendor}
                                                      Reported vendor
                                                      {attrs.vendor}
                                                      {/if} + {#if attrs.os}
                                                      Reported OS
                                                      {attrs.os}
                                                      {/if} + {#if attrs.device}
                                                      Reported device
                                                      {attrs.device}
                                                      {/if} +
                                                      +

                                                      Scanner-reported identity confidence: {attrs.identity_confidence || 'unknown'}Identity source: {attrs.identity_source || 'unknown'}

                                                      +

                                                      {randomizedLabel(attrs.randomized)}

                                                      + {#if attrs.attributes_truncated}

                                                      Some reported fields were truncated at ingest.

                                                      {/if} +
                                                      Saved report view · technical attributes
                                                      {JSON.stringify(attrs, null, 2)}
                                                      +

                                                      Snapshot covers {view.ports_total} ports{view.ports_truncated ? ' (truncated)' : ''}.

                                                      + {#if (view.ports ?? []).length > 0} +
                                                        {#each view.ports as port (`${port.proto}/${port.port}`)}
                                                      • {port.proto}/{port.port} {port.state}{port.service ? ` (${port.service})` : ''}
                                                      • {/each}
                                                      + {/if} + {#if view.profile_id} + Open reported context →{classLabel(view.reference_class)} · {profileLinks.find((link) => link.profile_id === view.profile_id)?.role ?? view.view_role} + {:else}

                                                      No linked profile is available for this saved view.

                                                      {/if} +
                                                      + {/each} + {/if} +
                                                      +
                                                      diff --git a/ui/src/components/ReportView/index.js b/ui/src/components/ReportView/index.js new file mode 100644 index 0000000..3a4fa63 --- /dev/null +++ b/ui/src/components/ReportView/index.js @@ -0,0 +1 @@ +export { default } from './ReportView.svelte'; diff --git a/ui/src/components/ScannerReports/ScannerReports.svelte b/ui/src/components/ScannerReports/ScannerReports.svelte new file mode 100644 index 0000000..f1f6357 --- /dev/null +++ b/ui/src/components/ScannerReports/ScannerReports.svelte @@ -0,0 +1,22 @@ + + +
                                                      +

                                                      Scanner snapshot{events.length === 1 ? '' : 's'}

                                                      Saved at report time
                                                      +

                                                      Open a report to see what the scanner supplied, with its linked context.

                                                      + {#if events.length === 0}

                                                      No referenced events retained.

                                                      + {:else}{#each events as ref (ref.event_id)}{/each}{/if} + {#if referencedTruncated}

                                                      More referenced events exist than shown.

                                                      {/if} + {#if observationsTruncated}

                                                      More observations exist than shown; some saved views are omitted.

                                                      {/if} +
                                                      diff --git a/ui/src/components/ScannerReports/index.js b/ui/src/components/ScannerReports/index.js new file mode 100644 index 0000000..33eab3d --- /dev/null +++ b/ui/src/components/ScannerReports/index.js @@ -0,0 +1 @@ +export { default } from './ScannerReports.svelte'; diff --git a/ui/src/components/ShortcutsHelp/ShortcutsHelp.module.css b/ui/src/components/ShortcutsHelp/ShortcutsHelp.module.css new file mode 100644 index 0000000..d73ac1b --- /dev/null +++ b/ui/src/components/ShortcutsHelp/ShortcutsHelp.module.css @@ -0,0 +1,7 @@ +.help { + margin: 16px 28px; + padding: 20px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; +} diff --git a/ui/src/components/ShortcutsHelp/ShortcutsHelp.svelte b/ui/src/components/ShortcutsHelp/ShortcutsHelp.svelte new file mode 100644 index 0000000..29534a6 --- /dev/null +++ b/ui/src/components/ShortcutsHelp/ShortcutsHelp.svelte @@ -0,0 +1,25 @@ + + +
                                                      +

                                                      Keyboard shortcuts

                                                      +
                                                        + {#each globalKeys as binding (binding.key)} +
                                                      • {binding.key} — {binding.label}
                                                      • + {/each} +
                                                      • g then h — Home
                                                      • +
                                                      • g then d — Reported hosts
                                                      • + {#each briefingKeys as binding (binding.key)} +
                                                      • {binding.key} — on a briefing: {binding.label}, then confirm
                                                      • + {/each} +
                                                      +

                                                      Shortcuts stay silent while typing. Briefing keys preset the composer; nothing submits on its own.

                                                      +
                                                      diff --git a/ui/src/components/ShortcutsHelp/index.js b/ui/src/components/ShortcutsHelp/index.js new file mode 100644 index 0000000..4081759 --- /dev/null +++ b/ui/src/components/ShortcutsHelp/index.js @@ -0,0 +1 @@ +export { default } from './ShortcutsHelp.svelte'; diff --git a/ui/src/components/SkipLink/SkipLink.module.css b/ui/src/components/SkipLink/SkipLink.module.css new file mode 100644 index 0000000..bf4c8f4 --- /dev/null +++ b/ui/src/components/SkipLink/SkipLink.module.css @@ -0,0 +1,12 @@ +.skip { + position: fixed; + left: 16px; + top: -100px; + z-index: 10; + padding: 12px; + background: var(--surface); +} + +.skip:focus { + top: 12px; +} diff --git a/ui/src/components/SkipLink/SkipLink.svelte b/ui/src/components/SkipLink/SkipLink.svelte new file mode 100644 index 0000000..20fbdd4 --- /dev/null +++ b/ui/src/components/SkipLink/SkipLink.svelte @@ -0,0 +1,8 @@ + + + { event.preventDefault(); document.getElementById('content')?.focus(); }}>Skip to content diff --git a/ui/src/components/SkipLink/index.js b/ui/src/components/SkipLink/index.js new file mode 100644 index 0000000..add57cc --- /dev/null +++ b/ui/src/components/SkipLink/index.js @@ -0,0 +1 @@ +export { default } from './SkipLink.svelte'; diff --git a/ui/src/components/SnapshotNote/SnapshotNote.svelte b/ui/src/components/SnapshotNote/SnapshotNote.svelte new file mode 100644 index 0000000..121a887 --- /dev/null +++ b/ui/src/components/SnapshotNote/SnapshotNote.svelte @@ -0,0 +1,3 @@ + +

                                                      Reported at the time shown. Network conditions may have changed since this report.

                                                      diff --git a/ui/src/components/SnapshotNote/index.js b/ui/src/components/SnapshotNote/index.js new file mode 100644 index 0000000..7d99db1 --- /dev/null +++ b/ui/src/components/SnapshotNote/index.js @@ -0,0 +1 @@ +export { default } from './SnapshotNote.svelte'; diff --git a/ui/src/lib/SourceCoverage.svelte b/ui/src/components/SourceCoverage/SourceCoverage.svelte similarity index 100% rename from ui/src/lib/SourceCoverage.svelte rename to ui/src/components/SourceCoverage/SourceCoverage.svelte diff --git a/ui/src/components/SourceCoverage/index.js b/ui/src/components/SourceCoverage/index.js new file mode 100644 index 0000000..be7ed29 --- /dev/null +++ b/ui/src/components/SourceCoverage/index.js @@ -0,0 +1 @@ +export { default } from './SourceCoverage.svelte'; diff --git a/ui/src/components/TechnicalDetails/TechnicalDetails.svelte b/ui/src/components/TechnicalDetails/TechnicalDetails.svelte new file mode 100644 index 0000000..44e9d0e --- /dev/null +++ b/ui/src/components/TechnicalDetails/TechnicalDetails.svelte @@ -0,0 +1,37 @@ + + +
                                                      + Technical details Tool calls, accounting and provenance +

                                                      Executed tool steps

                                                      + {#if traces?.length}
                                                        {#each traces as step (step.step)}{/each}
                                                      {:else}

                                                      No agent trace is available for this briefing.

                                                      {/if} + {#if inputTokens != null || outputTokens != null || costUsd != null} +

                                                      Model accounting

                                                      {inputTokens ?? 'Unknown'} input tokens · {outputTokens ?? 'Unknown'} output tokens · {costUsd == null ? 'Cost unknown' : `$${costUsd}`}

                                                      + {/if} +

                                                      Provenance

                                                      +
                                                      Briefing
                                                      {briefingId}
                                                      Group
                                                      {groupId}
                                                      +
                                                      Grouping version
                                                      {groupingVersion}
                                                      Sequence
                                                      {seq}
                                                      +
                                                      Created
                                                      +
                                                      Grouping anchor
                                                      {host}
                                                      +
                                                      +
                                                      +
                                                      diff --git a/ui/src/components/TechnicalDetails/index.js b/ui/src/components/TechnicalDetails/index.js new file mode 100644 index 0000000..86ad10d --- /dev/null +++ b/ui/src/components/TechnicalDetails/index.js @@ -0,0 +1 @@ +export { default } from './TechnicalDetails.svelte'; diff --git a/ui/src/components/TraceStep/TraceStep.module.css b/ui/src/components/TraceStep/TraceStep.module.css new file mode 100644 index 0000000..68a0cbb --- /dev/null +++ b/ui/src/components/TraceStep/TraceStep.module.css @@ -0,0 +1,15 @@ +.step { + list-style: none; + border-top: 1px solid var(--border); + padding: 12px 0; +} + +.toolName { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + font-weight: 600; +} + +.step p { + margin: 8px 0; +} diff --git a/ui/src/lib/TraceStep.svelte b/ui/src/components/TraceStep/TraceStep.svelte similarity index 76% rename from ui/src/lib/TraceStep.svelte rename to ui/src/components/TraceStep/TraceStep.svelte index 90ff04a..364b7fb 100644 --- a/ui/src/lib/TraceStep.svelte +++ b/ui/src/components/TraceStep/TraceStep.svelte @@ -1,4 +1,5 @@ -
                                                    • - {step.tool} +
                                                    • + {step.tool} {#if step.outcome}{step.outcome.replaceAll('_', ' ')}{/if} step {step.step} · {step.status} · {step.duration_ms} ms

                                                      {headline}

                                                      @@ -31,18 +32,3 @@
                                                    • - diff --git a/ui/src/components/TraceStep/index.js b/ui/src/components/TraceStep/index.js new file mode 100644 index 0000000..52e3e71 --- /dev/null +++ b/ui/src/components/TraceStep/index.js @@ -0,0 +1 @@ +export { default } from './TraceStep.svelte'; diff --git a/ui/src/lib/VerdictHeader.svelte b/ui/src/components/VerdictHeader/VerdictHeader.svelte similarity index 96% rename from ui/src/lib/VerdictHeader.svelte rename to ui/src/components/VerdictHeader/VerdictHeader.svelte index 1cf10a4..1c73d29 100644 --- a/ui/src/lib/VerdictHeader.svelte +++ b/ui/src/components/VerdictHeader/VerdictHeader.svelte @@ -1,5 +1,5 @@ + +{#if notice !== null && shown === null} +
                                                      +

                                                      Briefing unavailable

                                                      +

                                                      The bridge did not return this briefing ({notice}).

                                                      + +
                                                      +{:else if shown === null} +

                                                      Loading briefing…

                                                      +{:else} +
                                                      + {#if layout === 'page'}

                                                      ← Back to Home

                                                      {/if} + {#if notice !== null} +

                                                      The saved view could not be refreshed. Your last loaded report is still shown.

                                                      + {/if} + +
                                                      + + + + + + + + + + + + + 0} + context={shown.context} + receipts={receipts} + evidenceRefs={shown.evidence_refs} + profiles={shown.profiles} + profilesTruncated={shown.profiles_truncated} + onJump={jumpTo} + /> + + +
                                                      +
                                                      +{/if} diff --git a/ui/src/containers/BriefingContainer/index.js b/ui/src/containers/BriefingContainer/index.js new file mode 100644 index 0000000..3365d9d --- /dev/null +++ b/ui/src/containers/BriefingContainer/index.js @@ -0,0 +1 @@ +export { default } from './BriefingContainer.svelte'; diff --git a/ui/src/containers/DecisionsContainer/DecisionsContainer.module.css b/ui/src/containers/DecisionsContainer/DecisionsContainer.module.css new file mode 100644 index 0000000..f710152 --- /dev/null +++ b/ui/src/containers/DecisionsContainer/DecisionsContainer.module.css @@ -0,0 +1,7 @@ +.card { + padding: 26px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 16px; + overflow-wrap: anywhere; +} diff --git a/ui/src/containers/DecisionsContainer/DecisionsContainer.svelte b/ui/src/containers/DecisionsContainer/DecisionsContainer.svelte new file mode 100644 index 0000000..fad01f8 --- /dev/null +++ b/ui/src/containers/DecisionsContainer/DecisionsContainer.svelte @@ -0,0 +1,293 @@ + + +
                                                      + + + {#if memberReview !== null} + + {/if} + 0} + {note} + {listError} + {paging} + {undoPending} + {card} + onUndo={undo} + onOlder={older} + onNewer={newer} + /> +
                                                      diff --git a/ui/src/containers/DecisionsContainer/index.js b/ui/src/containers/DecisionsContainer/index.js new file mode 100644 index 0000000..8598db5 --- /dev/null +++ b/ui/src/containers/DecisionsContainer/index.js @@ -0,0 +1 @@ +export { default } from './DecisionsContainer.svelte'; diff --git a/ui/src/containers/DeviceContainer/DeviceContainer.svelte b/ui/src/containers/DeviceContainer/DeviceContainer.svelte new file mode 100644 index 0000000..a95b918 --- /dev/null +++ b/ui/src/containers/DeviceContainer/DeviceContainer.svelte @@ -0,0 +1,126 @@ + + +{#if $error !== null && profile === null} +
                                                      +

                                                      Reported host unavailable

                                                      +

                                                      The bridge did not return this profile ({$error}).

                                                      +

                                                      Back to reported hosts

                                                      +
                                                      +{:else if profile === null} +

                                                      Loading reported host…

                                                      +{:else} +
                                                      +

                                                      Back to reported hosts

                                                      + {#if stale} +

                                                      Connection recovered — this page may miss new reports.

                                                      + {/if} + {#if notice !== null} +

                                                      Refresh failed ({notice}) — showing retained data.

                                                      + {/if} + + + + +
                                                      +{/if} diff --git a/ui/src/containers/DeviceContainer/index.js b/ui/src/containers/DeviceContainer/index.js new file mode 100644 index 0000000..de9b3a1 --- /dev/null +++ b/ui/src/containers/DeviceContainer/index.js @@ -0,0 +1 @@ +export { default } from './DeviceContainer.svelte'; diff --git a/ui/src/containers/DevicesContainer/DevicesContainer.svelte b/ui/src/containers/DevicesContainer/DevicesContainer.svelte new file mode 100644 index 0000000..86fe9d3 --- /dev/null +++ b/ui/src/containers/DevicesContainer/DevicesContainer.svelte @@ -0,0 +1,109 @@ + + +
                                                      +

                                                      Reported hosts

                                                      +

                                                      Scanner-reported subjects. Classes distinguish device references, address context, unresolved events and legacy profiles.

                                                      + {#if loading} +

                                                      Loading reported hosts…

                                                      + {:else if $error !== null && empty} +

                                                      The bridge did not return reported hosts ({$error}).

                                                      + + {:else} + + {/if} +
                                                      diff --git a/ui/src/containers/DevicesContainer/index.js b/ui/src/containers/DevicesContainer/index.js new file mode 100644 index 0000000..2412d16 --- /dev/null +++ b/ui/src/containers/DevicesContainer/index.js @@ -0,0 +1 @@ +export { default } from './DevicesContainer.svelte'; diff --git a/ui/src/containers/HealthContainer/HealthContainer.svelte b/ui/src/containers/HealthContainer/HealthContainer.svelte index 1834599..2d8f759 100644 --- a/ui/src/containers/HealthContainer/HealthContainer.svelte +++ b/ui/src/containers/HealthContainer/HealthContainer.svelte @@ -3,6 +3,7 @@ import { fetchHealth } from '@/api.js'; import { createResource, failureNotice } from '@/lib/async/resource.js'; import { shouldRefreshOnReconnect } from '@/lib/events/reconnect.js'; + import { getConnectionOrNull } from '@/lib/events/context.js'; import HealthSummary from '@/components/HealthSummary/HealthSummary.svelte'; import SourceList from '@/components/SourceList/SourceList.svelte'; import PipelineDetails from '@/components/PipelineDetails/PipelineDetails.svelte'; @@ -14,7 +15,11 @@ // `initial` carries a prerender snapshot. A failed refresh keeps // rendered data with a notice and Retry; only a data-less failure // becomes an error. - let { stream, initial = null } = $props(); + let { initial = null } = $props(); + + // Liveness comes from the shared connection (context), never a + // drilled prop; standalone renders simply never refresh on an edge. + const connection = getConnectionOrNull(); // Snapshot-first rendering: fetched data wins once it lands, but the // mount-time snapshot fills in until (or instead of) the fetch. Reads @@ -38,12 +43,17 @@ // stale (was BUG-3: the edge required fetched data to exist). let wasLive = false; $effect(() => { - const live = stream === 'live'; - const edge = shouldRefreshOnReconnect({ live, wasLive, data: $data, initial }); - wasLive = live; - if (edge) { - resource.refresh(); + if (!connection) { + return; } + return connection.connection.subscribe((state) => { + const live = state === 'live'; + const edge = shouldRefreshOnReconnect({ live, wasLive, data: $data, initial }); + wasLive = live; + if (edge) { + resource.refresh(); + } + }); }); diff --git a/ui/src/lib/Home.svelte b/ui/src/containers/HomeContainer/HomeContainer.svelte similarity index 72% rename from ui/src/lib/Home.svelte rename to ui/src/containers/HomeContainer/HomeContainer.svelte index 8774a32..ba93cf7 100644 --- a/ui/src/lib/Home.svelte +++ b/ui/src/containers/HomeContainer/HomeContainer.svelte @@ -1,19 +1,23 @@ + +

                                                      Back to overview

                                                      + +{#if notice !== null} +

                                                      Refresh failed: {notice}

                                                      +{/if} +{#if shown === null} +

                                                      Loading incident…

                                                      +{:else} + {@const inc = shown.incident} + + + + {#if shown.summary} + + {/if} + + +{/if} diff --git a/ui/src/containers/IncidentContainer/index.js b/ui/src/containers/IncidentContainer/index.js new file mode 100644 index 0000000..b06203d --- /dev/null +++ b/ui/src/containers/IncidentContainer/index.js @@ -0,0 +1 @@ +export { default } from './IncidentContainer.svelte'; diff --git a/ui/src/containers/IncidentMembersContainer/IncidentMembersContainer.svelte b/ui/src/containers/IncidentMembersContainer/IncidentMembersContainer.svelte new file mode 100644 index 0000000..cbacca8 --- /dev/null +++ b/ui/src/containers/IncidentMembersContainer/IncidentMembersContainer.svelte @@ -0,0 +1,107 @@ + + +{#if expanded} + {#if loading && members === undefined} +

                                                      Loading members…

                                                      + {:else if loadError !== undefined} +

                                                      {loadError}

                                                      + {:else if members !== undefined} +
                                                        + {#each members as m (m.briefing_id)} +
                                                      1. + {m.briefing_id} + {m.priority} +
                                                      2. + {/each} +
                                                      + {/if} +{/if} diff --git a/ui/src/containers/IncidentMembersContainer/index.js b/ui/src/containers/IncidentMembersContainer/index.js new file mode 100644 index 0000000..a316e8e --- /dev/null +++ b/ui/src/containers/IncidentMembersContainer/index.js @@ -0,0 +1 @@ +export { default } from './IncidentMembersContainer.svelte'; diff --git a/ui/src/lib/AttentionList.svelte b/ui/src/lib/AttentionList.svelte deleted file mode 100644 index 92160c4..0000000 --- a/ui/src/lib/AttentionList.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - -
                                                      -

                                                      Needs attention ({items.length})

                                                      - {#if items.length === 0} -

                                                      Nothing flagged. New briefings appear here when the bridge reports them.

                                                      - {:else} -
                                                        - {#each items as item (item.id)} -
                                                      1. - {#snippet row()} - {item.priority} - {item.kind === 'incident' ? 'Incident · ' : ''}{item.kind === 'briefing' ? (titles[item.id] ?? reviewTitle(item.change_kind)) : changeLabel(item.change_kind)}{item.id} - {item.status} - {/snippet} - {#if item.kind === 'briefing' || item.kind === 'incident'} - {#if onselect} - - {:else} - {@render row()} - {/if} - {:else} -
                                                        {@render row()}
                                                        - {/if} - {#if item.kind === 'incident'} - - {#if expandError[item.id] !== undefined} -

                                                        {expandError[item.id]}

                                                        - {/if} - {#if (expanded[item.id] ?? false) && memberCache[item.id] !== undefined} -
                                                          - {#each memberCache[item.id] as m (m.briefing_id)} -
                                                        1. - {m.briefing_id} - {m.priority} -
                                                        2. - {/each} -
                                                        - {/if} - {/if} -
                                                      2. - {/each} -
                                                      - {/if} -
                                                      diff --git a/ui/src/lib/DecisionPanel.svelte b/ui/src/lib/DecisionPanel.svelte deleted file mode 100644 index f038d35..0000000 --- a/ui/src/lib/DecisionPanel.svelte +++ /dev/null @@ -1,445 +0,0 @@ - - -
                                                      -

                                                      {requireChoice ? 'Your decision' : 'Operator decisions'}

                                                      -

                                                      Status: {statusLine()}

                                                      - -
                                                      -
                                                      - Choose a decision - {#each kinds as k (k)} - - {/each} -
                                                      -

                                                      {kind === null ? 'Choose a verdict to record it.' : (kindHelp[kind] ?? KIND_HELP[kind] ?? '')}

                                                      - {#if memberScope} - - {/if} - {#if outcomeNote !== null} -

                                                      {outcomeNote}

                                                      - {/if} - {#if kind === 'snooze'} - - {/if} - {#if needsText} - - {/if} - {#if formError !== null} -

                                                      {formError}

                                                      - {/if} - -
                                                      - - {#if memberReview !== null} -

                                                      {memberUndoNote(memberReview)}

                                                      - {#if memberReview.members.length > 0} -
                                                        - {#each memberReview.members as m (m.decision_id)} -
                                                      • {m.subject_id} — {m.kind.replace('_', ' ')}
                                                      • - {/each} -
                                                      - {/if} - {#if memberReview.next} - - {/if} - {#if memberError !== null}

                                                      {memberError}

                                                      {/if} - {/if} - - {#if requireChoice}

                                                      Recorded decisions

                                                      {/if} - {#if trail.length === 0} -

                                                      No decisions recorded.

                                                      - {:else} -
                                                        - {#each trail as d (d.decision_id)} -
                                                      1. - {d.kind.replace('_', ' ')} - {#if d.undone}undone{/if} - {#if d.text}{d.text}{/if} - {#if d.until}until {d.until}{/if} - - {#if !d.undone && d.kind !== 'undo'} - - {/if} -
                                                      2. - {/each} -
                                                      - {#if !exhausted} - - {/if} - {#if extraKey === headKey && extra.length > 0} - - {/if} - {#if note !== null} -

                                                      {note}

                                                      - {/if} - {/if} - {#if listError !== null} -

                                                      {listError}

                                                      - {/if} -
                                                      - - diff --git a/ui/src/lib/Detail.svelte b/ui/src/lib/Detail.svelte deleted file mode 100644 index d0416e7..0000000 --- a/ui/src/lib/Detail.svelte +++ /dev/null @@ -1,353 +0,0 @@ - - -{#if error !== null && shown === null} -
                                                      -

                                                      Briefing unavailable

                                                      -

                                                      The bridge did not return this briefing ({error}).

                                                      - -
                                                      -{:else if shown === null} -

                                                      Loading briefing…

                                                      -{:else} -
                                                      - {#if !embedded}

                                                      ← Back to Home

                                                      {/if} - {#if error !== null} -

                                                      The saved view could not be refreshed. Your last loaded report is still shown.

                                                      - {/if} - -
                                                      -
                                                      -
                                                      -

                                                      {title}

                                                      - {shown.priority} priority -
                                                      -

                                                      {shown.what_changed}

                                                      -
                                                      - {#if identity !== null && identityComplete} -
                                                      Reported host
                                                      {identity.hostname ?? 'Not reported'}
                                                      -
                                                      IP address
                                                      {identity.ip ?? 'Not reported'}{#if identity.ip !== null}{/if}
                                                      -
                                                      MAC address
                                                      {identity.mac ?? 'Not reported'}{#if identity.mac !== null}{/if}
                                                      - {:else} -
                                                      Reported identities
                                                      See the individual report views below

                                                      {identityComplete ? 'Grouped changes can describe different host views.' : 'Only part of the retained evidence is shown.'}

                                                      - {/if} -
                                                      Reported scan target
                                                      {primaryEvent?.target || (shown.referenced_events.length > 1 ? 'Shown per report below' : 'Not reported')}{#if primaryEvent?.target_truncated} (truncated){/if}
                                                      -
                                                      {range?.partial || shown.referenced_truncated ? 'Available report times' : shown.referenced_events.length > 1 ? 'Report time range' : 'Report time'}
                                                      {#if range !== null}{range.local}{:else}Not reported{/if}
                                                      -
                                                      Analysis source
                                                      {sourceLabel(shown.source)}
                                                      -
                                                      -
                                                      - {shown.referenced_events.length}{shown.referenced_truncated ? '+' : ''} source report{shown.referenced_events.length === 1 ? '' : 's'} - {#if primaryEvent} - - {:else if shown.referenced_events.length > 0} - - {/if} -
                                                      -
                                                      - -

                                                      Reported at the time shown. Network conditions may have changed since this report.

                                                      - - - - - -
                                                      -

                                                      Why this priority

                                                      -

                                                      {shown.priority_reason}

                                                      -
                                                      Final priority: {shown.priority}{#if shown.baseline_rule}{shown.baseline_rule}{/if}
                                                      -
                                                      - How the priority was computed -
                                                        -
                                                      1. Baseline floor {shown.baseline}
                                                      2. -
                                                      3. Model suggestion {shown.model_suggestion ?? 'No model suggestion — baseline stands'}
                                                      4. -
                                                      5. Final priority {shown.priority}
                                                      6. -
                                                      - {#if shown.clamped}

                                                      Suggestion adjusted to respect the baseline floor and one-step raise limit.

                                                      {/if} -
                                                      - {#if shown.fallback_used}

                                                      Deterministic fallback text: the model path did not produce this briefing.

                                                      {/if} -
                                                      - -
                                                      -

                                                      Scanner snapshot{shown.referenced_events.length === 1 ? '' : 's'}

                                                      Saved at report time
                                                      -

                                                      Open a report to see what the scanner supplied, with its linked context.

                                                      - {#if shown.referenced_events.length === 0}

                                                      No referenced events retained.

                                                      - {:else}{#each shown.referenced_events as ref (ref.event_id)}{/each}{/if} - {#if shown.referenced_truncated}

                                                      More referenced events exist than shown.

                                                      {/if} - {#if shown.observations_truncated}

                                                      More observations exist than shown; some saved views are omitted.

                                                      {/if} -
                                                      - -
                                                      -

                                                      {shown.source === 'agent' ? 'Investigator notes' : 'Analysis and retained context'}

                                                      - {#if shown.likely_explanation}

                                                      Possible explanation: {shown.likely_explanation}

                                                      {/if} - {#if shown.confidence}

                                                      Explanation confidence (model self-assessment): {shown.confidence}

                                                      {/if} - {#if lookupLimits.length > 0}

                                                      Investigation has limits. Some lookups could not establish retained context. Missing lookup results do not prove this host has no history.

                                                      {/if} -

                                                      {shown.context}

                                                      - {#if shown.traces?.length} -

                                                      What the investigator could establish

                                                      -
                                                        - {#each lookupReceipts as receipt, i (i)} -
                                                      • {receipt.summary || 'No interpreted result is available. Open the tool step to inspect the saved response.'}

                                                        {#each receipt.steps as step (step)}{/each}
                                                      • - {/each} -
                                                      - {/if} - {#if shown.evidence_refs?.length} -
                                                      Cited evidence - {#each shown.evidence_refs as ref, i (`${ref.event_id ?? ref.tool_step}:${i}`)} - - {/each} -
                                                      - {/if} - {#if shown.profiles.length > 0} - - {:else}

                                                      No profile links persisted for this briefing.

                                                      {/if} - {#if shown.profiles_truncated}

                                                      More profile links exist than shown.

                                                      {/if} -
                                                      - -
                                                      - Technical details Tool calls, accounting and provenance -

                                                      Executed tool steps

                                                      - {#if shown.traces?.length}
                                                        {#each shown.traces as step (step.step)}{/each}
                                                      {:else}

                                                      No agent trace is available for this briefing.

                                                      {/if} - {#if shown.input_tokens != null || shown.output_tokens != null || shown.cost_usd != null} -

                                                      Model accounting

                                                      {shown.input_tokens ?? 'Unknown'} input tokens · {shown.output_tokens ?? 'Unknown'} output tokens · {shown.cost_usd == null ? 'Cost unknown' : `$${shown.cost_usd}`}

                                                      - {/if} -

                                                      Provenance

                                                      -
                                                      Briefing
                                                      {shown.briefing_id}
                                                      Group
                                                      {shown.group_id}
                                                      -
                                                      Grouping version
                                                      {shown.grouping_version}
                                                      Sequence
                                                      {shown.seq}
                                                      -
                                                      Created
                                                      -
                                                      Grouping anchor
                                                      {shown.host}
                                                      -
                                                      -
                                                      -
                                                      -
                                                      -
                                                      -{/if} - - diff --git a/ui/src/lib/Device.svelte b/ui/src/lib/Device.svelte deleted file mode 100644 index 19902fb..0000000 --- a/ui/src/lib/Device.svelte +++ /dev/null @@ -1,215 +0,0 @@ - - -{#if error !== null && profile === null} -
                                                      -

                                                      Reported host unavailable

                                                      -

                                                      The bridge did not return this profile ({error}).

                                                      -

                                                      Back to reported hosts

                                                      -
                                                      -{:else if profile === null} -

                                                      Loading reported host…

                                                      -{:else} - {@const latestTime = profile.latest === null ? null : displayTime(profile.latest.reported_at)} -
                                                      -

                                                      Back to reported hosts

                                                      - {#if stale} -

                                                      Connection recovered — this page may miss new reports.

                                                      - {/if} - {#if refreshError !== null} -

                                                      Refresh failed ({refreshError}) — showing retained data.

                                                      - {/if} -

                                                      {profile.profile_id} — {classLabel(profile.class)}

                                                      - {#if profile.class === 'legacy'} -

                                                      Earlier bridge-inferred association; not used for current identity.

                                                      - {:else if profile.class === 'address' || profile.class === 'event'} -

                                                      This page describes {profile.class === 'address' ? 'address context' : 'unresolved events'}, not a device.

                                                      - {/if} -
                                                      -
                                                      Source scope
                                                      -
                                                      {profile.source_scope}
                                                      -
                                                      Upstream reference
                                                      -
                                                      {profile.reported_id}
                                                      -
                                                      Reported by
                                                      -
                                                      {profile.reported_source} (confidence: {profile.reported_confidence || 'unknown'})
                                                      -
                                                      Association
                                                      -
                                                      {profile.association_status}
                                                      - {#if profile.operator_label !== null && profile.operator_label !== undefined} -
                                                      Your label
                                                      -
                                                      {profile.operator_label}
                                                      - {/if} -
                                                      First report
                                                      -
                                                      -
                                                      Last report
                                                      -
                                                      -
                                                      - - - -
                                                      -

                                                      Latest evidence

                                                      - {#if profile.latest === null} -

                                                      No retained observation.

                                                      - {:else} -

                                                      Reported ({profile.latest.view_role}).

                                                      -
                                                      - {#each Object.entries(profile.latest.attributes ?? {}) as [key, value] (key)} -
                                                      {key}
                                                      -
                                                      {JSON.stringify(value)}
                                                      - {/each} -
                                                      - {#if (profile.latest.ports ?? []).length > 0} -
                                                        - {#each profile.latest.ports as port (`${port.proto}/${port.port}`)} -
                                                      • {port.proto}/{port.port} {port.state}{port.service ? ` (${port.service})` : ''}
                                                      • - {/each} -
                                                      - {/if} -

                                                      Snapshot covers {profile.latest.ports_total} ports{profile.latest.ports_truncated ? ' (truncated)' : ''}.

                                                      - {/if} -
                                                      - -
                                                      -

                                                      History

                                                      - {#if history.length === 0} -

                                                      No retained reports in this window.

                                                      - {:else} -
                                                        - {#each history as obs (obs.observation_id)} - {@const at = displayTime(obs.reported_at)} -
                                                      1. - {obs.event_id} ({obs.view_role}) — - , - {obs.ports_total} ports{obs.ports_truncated ? ' (truncated)' : ''} -
                                                      2. - {/each} -
                                                      - {/if} - {#if historyError !== null} -

                                                      Older page failed ({historyError}).

                                                      - {/if} - {#if historyCapped} -

                                                      History capped at {HISTORY_CAP} rows; use the API for the full set.

                                                      - {:else if !historyExhausted} - - {:else} -

                                                      End of retained history. Retention policy lives on the Health screen.

                                                      - {/if} -
                                                      -
                                                      -{/if} diff --git a/ui/src/lib/Devices.svelte b/ui/src/lib/Devices.svelte deleted file mode 100644 index 707863d..0000000 --- a/ui/src/lib/Devices.svelte +++ /dev/null @@ -1,146 +0,0 @@ - - -
                                                      -

                                                      Reported hosts

                                                      -

                                                      Scanner-reported subjects. Classes distinguish device references, address context, unresolved events and legacy profiles.

                                                      - {#if loading && devices.length === 0} -

                                                      Loading reported hosts…

                                                      - {:else if error !== null && devices.length === 0} -

                                                      The bridge did not return reported hosts ({error}).

                                                      - - {:else} - {#if stale} -

                                                      Connection recovered — this list may miss new reports.

                                                      - {/if} - {#if devices.length === 0} -

                                                      No reported subjects retained.

                                                      - {:else} - - {/if} - {#if error !== null} -

                                                      Page failed ({error}) — earlier rows kept.

                                                      - {/if} - {#if capped} -

                                                      List capped at {ROW_CAP} rows; use the API for the full set.

                                                      - {:else if !exhausted} - - {:else} -

                                                      End of retained list.

                                                      - {/if} - {/if} -
                                                      diff --git a/ui/src/lib/Incident.svelte b/ui/src/lib/Incident.svelte deleted file mode 100644 index 8af8d58..0000000 --- a/ui/src/lib/Incident.svelte +++ /dev/null @@ -1,170 +0,0 @@ - - -

                                                      Back to overview

                                                      - -{#if error !== null} -

                                                      Refresh failed: {error}

                                                      -{/if} -{#if shown === null} -

                                                      Loading incident…

                                                      -{:else} - {@const inc = shown.incident} - {@const opened = displayTime(inc.opened_at)} - {@const closed = displayTime(inc.closed_at)} - {@const start = displayTime(inc.window_start)} - {@const last = displayTime(inc.last_member_at)} -

                                                      {KIND_LABEL[inc.kind] ?? inc.kind}

                                                      -

                                                      - {inc.status} - Floor: {inc.floor} - {#if inc.status === 'closed' && inc.closed_at} - Closed - {:else} - Open since - {/if} -

                                                      - -
                                                      -

                                                      Scope

                                                      -

                                                      Source: {inc.source_scope}

                                                      -

                                                      Target: {inc.target}

                                                      -

                                                      Window start: · Last member:

                                                      -
                                                      - -
                                                      -

                                                      Evidence

                                                      -

                                                      {countLine(inc)}

                                                      - {#if inc.qualified_refs < inc.member_count} -

                                                      Some members share identity evidence; only distinct qualified references count toward the burst threshold.

                                                      - {/if} - {#if shown.truncated} -

                                                      More members exist than shown.

                                                      - {/if} - {#if shown.members.length === 0} -

                                                      No member evidence retained.

                                                      - {:else} -
                                                        - {#each shown.members as m (m.briefing_id)} - {@const reported = displayTime(m.created_at)} -
                                                      1. - {m.briefing_id} - {m.priority} - - {m.refs.length} qualified reference{m.refs.length === 1 ? '' : 's'} -
                                                      2. - {/each} -
                                                      - {/if} -
                                                      - - {#if shown.summary} - {@const summary = shown.summary} -
                                                      -

                                                      Summary

                                                      -

                                                      Summary status: {summary.status}

                                                      - {#if summary.summary}

                                                      {summary.summary}

                                                      {/if} - {#if summary.hypothesis}

                                                      Hypothesis: {summary.hypothesis}

                                                      {/if} - {#if summary.confidence}

                                                      Display confidence: {summary.confidence}

                                                      {/if} -

                                                      Priority floor: {summary.floor}. Resolved priority: {summary.final_priority}{summary.suggestion ? ` (suggested ${summary.suggestion}${summary.clamped ? ', clamped' : ''})` : ' (no model raise)'}

                                                      - {#if summary.reason}

                                                      Priority explanation: {summary.reason}

                                                      {/if} -

                                                      Evidence coverage: {summary.sampled} sampled member reports; {summary.omitted} omitted. The sample may not represent every member.

                                                      - {#if summary.citations.length} -

                                                      Cited reports:

                                                      - - {/if} - {#if summary.error}

                                                      {summary.error}

                                                      {/if} -
                                                      - {/if} - - -{/if} diff --git a/ui/src/lib/ReportView.svelte b/ui/src/lib/ReportView.svelte deleted file mode 100644 index 72f2dea..0000000 --- a/ui/src/lib/ReportView.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - -
                                                      - - {ref.event_id}{ref.event_type} - Reported - -
                                                      -

                                                      Reported scan target: {ref.target || 'Not reported'}{#if ref.target_truncated} (truncated){/if}
                                                      Received

                                                      - {#if views.length === 0} -

                                                      No retained observation for this event. Its reference is retained, but a saved scanner view is unavailable.

                                                      - {:else} - {#each views as view (`${view.event_id}:${view.view_role}`)} - {@const attrs = view.attributes ?? {}} -
                                                      -

                                                      {view.view_role === 'previous' ? 'Before the reported change' : view.view_role === 'current' ? 'After the reported change' : 'Reported host'}

                                                      -
                                                      -
                                                      Hostname
                                                      {value(attrs.hostname)}
                                                      -
                                                      IP address
                                                      {value(attrs.ip)}
                                                      -
                                                      MAC address
                                                      {value(attrs.mac)}
                                                      - {#if attrs.vendor}
                                                      Reported vendor
                                                      {attrs.vendor}
                                                      {/if} - {#if attrs.os}
                                                      Reported OS
                                                      {attrs.os}
                                                      {/if} - {#if attrs.device}
                                                      Reported device
                                                      {attrs.device}
                                                      {/if} -
                                                      -

                                                      Scanner-reported identity confidence: {attrs.identity_confidence || 'unknown'}Identity source: {attrs.identity_source || 'unknown'}

                                                      -

                                                      {randomizedLabel(attrs.randomized)}

                                                      - {#if attrs.attributes_truncated}

                                                      Some reported fields were truncated at ingest.

                                                      {/if} -
                                                      Saved report view · technical attributes
                                                      {JSON.stringify(attrs, null, 2)}
                                                      -

                                                      Snapshot covers {view.ports_total} ports{view.ports_truncated ? ' (truncated)' : ''}.

                                                      - {#if (view.ports ?? []).length > 0} -
                                                        {#each view.ports as port (`${port.proto}/${port.port}`)}
                                                      • {port.proto}/{port.port} {port.state}{port.service ? ` (${port.service})` : ''}
                                                      • {/each}
                                                      - {/if} - {#if view.profile_id} - Open reported context →{classLabel(view.reference_class)} · {profileLinks.find((link) => link.profile_id === view.profile_id)?.role ?? view.view_role} - {:else}

                                                      No linked profile is available for this saved view.

                                                      {/if} -
                                                      - {/each} - {/if} -
                                                      -
                                                      - - diff --git a/ui/src/lib/async/pager.js b/ui/src/lib/async/pager.js index c13182f..3754353 100644 --- a/ui/src/lib/async/pager.js +++ b/ui/src/lib/async/pager.js @@ -2,18 +2,26 @@ // Reset (reload) and append (load-more) run on independent tokens, so a // reload landing after a page start still applies — the BUG-1 case. // Failures preserve existing rows; only the error slot changes. +// +// Unlike createResource, the pager seeds from the mount-time snapshot: +// cursor and merge live here, so the container passes `initial` once and +// never rewrites it. A reset retires the snapshot by replacing rows. +// `initial` also accepts a thunk returning the snapshot: .svelte callers +// must read the prop inside a closure, or the compiler's init-capture +// rule treats it as a stale local capture. import { writable, derived, get } from 'svelte/store'; import { nextPageRows } from '@/lib/paging.js'; /** * @template T - * @param {{fetchPage: (cursor: string) => Promise<{rows: T[], nextAfter: string}>, keyOf: (row: T) => string|number, cap: number}} options + * @param {{fetchPage: (cursor: string) => Promise<{rows: T[], nextAfter: string}>, keyOf: (row: T) => string|number, cap: number, initial?: {rows: T[], nextAfter: string}|(() => ({rows: T[], nextAfter: string}|null))|null}} options */ -export function createPager({ fetchPage, keyOf, cap }) { - const rows = writable([]); - const cursor = writable(''); - const started = writable(false); +export function createPager({ fetchPage, keyOf, cap, initial = null }) { + const snap = typeof initial === 'function' ? initial() : initial; + const rows = writable(snap ? [...snap.rows] : []); + const cursor = writable(snap ? (snap.nextAfter ?? '') : ''); + const started = writable(snap !== null); const error = writable(null); const loadingMore = writable(false); const exhausted = derived([cursor, started], ([$cursor, $started]) => $started && $cursor === ''); @@ -54,5 +62,18 @@ export function createPager({ fetchPage, keyOf, cap }) { return loadMore(true); } - return { rows, cursor, exhausted, capped, error, loadingMore, loadMore, reset }; + // Replace every row outright (profile reload with embedded history): + // voids in-flight pages, clears the error and restarts from the new + // cursor. Failures never reach here — only applied data replaces. + function replace(nextRows, nextAfter = '') { + resetGen += 1; + pageGen += 1; + error.set(null); + loadingMore.set(false); + rows.set([...nextRows]); + cursor.set(nextAfter ?? ''); + started.set(true); + } + + return { rows, cursor, started, exhausted, capped, error, loadingMore, loadMore, reset, replace }; } diff --git a/ui/src/lib/events/context.js b/ui/src/lib/events/context.js index 3aca845..ebe897a 100644 --- a/ui/src/lib/events/context.js +++ b/ui/src/lib/events/context.js @@ -17,3 +17,18 @@ export function setConnection(stream) { export function getConnection() { return getContext(KEY); } + +/** + * The connection, or null when rendered outside App (SSR harness, + * standalone screens). Screens use this to read connection state and + * the incident revision from context instead of prop drilling; the + * null path keeps direct renders working without a provider. + * @returns {ReturnType|null} + */ +export function getConnectionOrNull() { + try { + return getConnection(); + } catch { + return null; + } +} diff --git a/ui/src/lib/events/stream.js b/ui/src/lib/events/stream.js index 4e38c71..57d75a0 100644 --- a/ui/src/lib/events/stream.js +++ b/ui/src/lib/events/stream.js @@ -5,9 +5,9 @@ // No module singletons: App creates one instance and shares it through // `./context.js`, so prerender never connects. // -// Mirrors the current App wiring: `open`/`incident`/`resync` all mean -// "incident state may have changed" (see `../incidentEvents.js`, which -// this replaces at migration time). +// `open`/`incident`/`resync` all mean "incident state may have +// changed". Screens subscribe to `incidentRevision` from context, so +// no window-event bus remains. import { writable } from 'svelte/store'; diff --git a/ui/src/lib/format/briefingView.js b/ui/src/lib/format/briefingView.js new file mode 100644 index 0000000..a2b88ee --- /dev/null +++ b/ui/src/lib/format/briefingView.js @@ -0,0 +1,54 @@ +// Pure briefing dossier derivations (Designv9 §8.2). No markup, no +// fetch: BriefingContainer calls these, and scripts/characterize.mjs +// covers them in plain node. + +/** + * The priority comparison opens when the reader needs it: equal + * baseline, suggestion and final stay collapsed; a difference or a + * clamp opens the detail. + * @param {{clamped?: boolean, baseline?: string, priority?: string, model_suggestion?: string|null}|null} shown + */ +export function priorityDiffers(shown) { + return shown !== null && + (shown.clamped || shown.baseline !== shown.priority || + (shown.model_suggestion ?? shown.baseline) !== shown.priority); +} + +/** + * Lookup steps that could not establish retained context: unknown + * subjects, blocked or failed lookups stay explicit limitations. + * @param {Array<{outcome?: string}>} traces + */ +export function lookupLimits(traces) { + return (traces ?? []).filter((step) => + ['unknown_subject', 'blocked', 'error', 'unavailable', 'truncated'].includes(step.outcome)); +} + +/** + * Group tool steps into lookup receipts by their summary, preserving + * first-seen order. + * @param {Array<{summary?: string, step?: number}>} traces + */ +export function lookupReceipts(traces) { + const groups = []; + for (const step of traces ?? []) { + const key = step.summary || `step:${step.step}`; + let group = groups.find((receipt) => receipt.key === key); + if (!group) { + group = { key, summary: step.summary, steps: [] }; + groups.push(group); + } + group.steps.push(step.step); + } + return groups; +} + +/** + * The single-view identity grid renders only when every observation + * and event is retained; a truncated dossier falls back to per-report + * views with an explicit notice. + * @param {{observations_truncated?: boolean, referenced_truncated?: boolean}|null} shown + */ +export function identityComplete(shown) { + return shown !== null && !shown.observations_truncated && !shown.referenced_truncated; +} diff --git a/ui/src/lib/format/evidence.js b/ui/src/lib/format/evidence.js new file mode 100644 index 0000000..13a1786 --- /dev/null +++ b/ui/src/lib/format/evidence.js @@ -0,0 +1,17 @@ +// Evidence navigation keys: the contract between dossier sections +// and revealEvidence (../evidenceNavigation.js). Sections tag targets +// with data-evidence keys; buttons jump by key without changing the +// hash route. Keys are `event:`, `step:` or the +// literal reports key for the scanner-reports section. + +export const REPORTS_KEY = 'reports'; + +/** @param {string} eventId */ +export function eventKey(eventId) { + return `event:${eventId}`; +} + +/** @param {number|string} step */ +export function stepKey(step) { + return `step:${step}`; +} diff --git a/ui/src/lib/incidentEvents.js b/ui/src/lib/incidentEvents.js deleted file mode 100644 index 87dc473..0000000 --- a/ui/src/lib/incidentEvents.js +++ /dev/null @@ -1,17 +0,0 @@ -// Incident updates have no replay cursor. An opened connection and a -// resync invalidate local incident state just like a live update does. -export function forwardIncidentEvents(source, target, refreshOverview) { - const refresh = () => { - target.dispatchEvent(new Event('watcherbridge:incident')); - refreshOverview(); - }; - const events = ['open', 'incident', 'resync']; - for (const event of events) { - source.addEventListener(event, refresh); - } - return () => { - for (const event of events) { - source.removeEventListener(event, refresh); - } - }; -} diff --git a/ui/src/main.js b/ui/src/main.js index b3ce16e..9f19042 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -3,6 +3,8 @@ import App from './App.svelte'; import './styles/tokens.css'; import './styles/base.css'; import './styles/utilities.css'; +import './styles/dossier.css'; +import './styles/shell.css'; // Step 22 entry: mounts the shell only. Hash routes and screens land // in Steps 23-24; the entry shape stays stable so they slot in here. diff --git a/ui/src/styles/base.css b/ui/src/styles/base.css index bc0a2b0..9cab822 100644 --- a/ui/src/styles/base.css +++ b/ui/src/styles/base.css @@ -600,22 +600,9 @@ label { label input:not([type=checkbox],[type=radio]),label select,label textarea { margin-top: 4px; } -.trail { - list-style: none; - padding: 0; -} -.trail li { - border-top: 1px solid var(--border); - padding: 12px 0; - font-size: 13px; -} -.trail .text { - overflow-wrap: anywhere; -} -.trail .until { - color: var(--muted); - font-size: 12px; -} +/* The decision trail moved to components/DecisionTrail with its own + module (transitional global rules removed once the last global + consumer migrated). */ @media(max-width:1050px) { .review-workspace { grid-template-columns: minmax(0,1fr); diff --git a/ui/src/styles/dossier.css b/ui/src/styles/dossier.css new file mode 100644 index 0000000..8cc3da0 --- /dev/null +++ b/ui/src/styles/dossier.css @@ -0,0 +1,80 @@ +/* Briefing dossier page shell (plan step 34). Moved verbatim from the + old Detail screen's scoped