salidium 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,122 +1,73 @@
1
1
  # Salidium
2
2
 
3
- Salidium translates coding-agent work into a trustworthy, structured explanation a human can read in seconds:
4
-
5
- - **What** the agent is doing and what changed
6
- - **Why** (the user's ask and the agent's stated discoveries — always attributed)
7
- - **How** (plan and approach)
8
- - **Verified** — what evidence actually shows (test/build/typecheck/lint runs parsed from real output)
9
- - **Left** — what remains
10
- - **Review** — what needs a human (waiting for permission, failing checks, unverified changes, destructive commands, claims without evidence)
11
-
12
- The daemon, event store, deterministic report, and interface run entirely on your machine with no Salidium telemetry or hosted service. Salidium observes the agent you already run through supported integration points (hooks and the agent's own session logs), derives facts deterministically, and relays the agent's claims verbatim and labelled. Every observed statement links back to the raw record it came from.
13
-
14
- The optional visual explanation uses your selected installed Claude Code or Codex CLI. Once per completed turn, Salidium sends that agent a bounded, redacted summary of the ask, statements, file names, and check outcomes; the CLI may contact its provider and consume your existing plan or API allowance. The invocation is tool-free, treats the session evidence as untrusted data, and accepts only a bounded, runtime-validated result. Older Codex versions that do not support the tool-disable flags fail closed and simply omit the generated block. Set `SALIDIUM_EXPLAINER=off` (or the legacy `SALIDIUM_EXPLAIN=0`) to send nothing to an agent. The deterministic report still works when explanation is off, unavailable, or fails.
15
-
16
- It is also allowed to say nothing. A claim the classifier cannot place is recorded and reachable through `record`, and filed under no heading — Salidium may say less than it knows, and may not say something it does not know.
17
-
18
- The session page is laid out as a written document rather than a dashboard: the product mark, the session title, the facts that identify the run, and the **badges** — did it work, what needs a human, what is unfinished — then a rule, then the **explanation**: one sentence saying what the session is about, followed by the diagrams that carry the meaning. Supporting facts stay behind their toolbar controls so the explanation remains the canvas.
19
-
20
- Detail is opened by section rather than by one global depth control. **Evidence** opens coverage, checks, changes, and the event record; **Rewind** reconstructs the session at a chosen moment; and **Quantities** and **History** share one supporting inspector beside the page, replacing one another without squeezing the explanation into a third column.
21
-
22
- The session list groups by the question it answers — **Needs you**, **Working**, **Recent** — because you are usually running more than one agent at a time. It folds away with `[`, and the sessions that started and ended without running a turn are collapsed behind one line. Light, dark and system themes are all first-class and the choice is remembered.
23
-
24
- Providers today: **Claude Code** (hooks + transcript, primary) and **Codex** (rollout files + hooks).
25
-
26
- ## Run it
27
-
28
- Requirements: Node ≥ 24, because the daemon uses the built-in `node:sqlite`.
3
+ **Understand what your coding agent did.** Salidium turns Claude Code and Codex sessions into a
4
+ clear, evidence-linked report of what changed, why, what was verified, and what needs you.
29
5
 
30
6
  ```bash
31
7
  npx salidium
32
8
  ```
33
9
 
34
- On first run, Salidium detects Claude Code and Codex from their local commands or state directories. If a detected agent still needs configuration, Salidium shows the settings files it wants to update and asks one combined permission question. Approved connections are merged with existing hooks and settings, checked, then Salidium starts and opens the interface. If Salidium adds or changes Codex hooks, Codex requires one additional review in `/hooks` before those hooks run.
10
+ Salidium is [MIT-licensed](LICENSE), open source, and local-first. The report, event store, and
11
+ interface run on your machine with no Salidium telemetry. Raw transcripts stay local. Optional
12
+ generated explanations send a bounded, redacted excerpt through your installed agent CLI, which
13
+ may contact its provider; they can be turned off.
35
14
 
36
- Later, the same command starts or finds the daemon and opens Salidium without repeating completed setup. In a non-interactive terminal, Salidium never waits for input and does not change provider settings unless `--yes` is passed. Use `--no-open` when a browser should not open.
15
+ ## See the work without reading every line
37
16
 
38
- `salidium` is a single self-contained script with no runtime dependencies: its public workspace
39
- packages are not published separately and are bundled into it, so running it adds nothing to your
40
- tree but Salidium itself.
41
-
42
- ```bash
43
- salidium install-hooks # connect detected agents manually
44
- salidium uninstall-hooks
45
- salidium doctor # check or troubleshoot the local setup
46
- salidium show # print the same report as text (--detail=summary|detail|source)
47
- salidium audit-claims # measure the claim classifier against every session in your own store
48
- salidium reingest --all # re-read your session files after an upgrade taught the adapters something new
49
- salidium retention # preview/set forever, 30, 90, or 365 day local history retention
50
- salidium retention compact # offline integrity-check and return reusable database pages to the OS
51
- salidium pin [session] # exempt a session from automatic retention
52
- salidium forget <id> # remove a session while preventing source-file resurrection
53
- salidium status # daemon and hook state
54
- salidium restart # stop it, start it again, and open the UI on the new token
55
- salidium stop
56
- ```
17
+ ```text
18
+ Agent response Salidium report
19
+ ────────────────────────────── ──────────────────────────────
20
+ "The cursor advanced before the WHY
21
+ storage transaction committed..." → Evidence could be lost before storage was durable.
57
22
 
58
- `install-hooks`, `uninstall-hooks`, and `doctor` remain available for manual setup and recovery. Installation and removal touch only Salidium-owned hook entries; unrelated provider settings and hooks are preserved.
23
+ "I now queue the record, flush, HOW
24
+ then advance the cursor." → Queue the record. Flush storage. Advance the cursor.
59
25
 
60
- Existing transcripts from the last 7 days are imported on first start (`SALIDIUM_HISTORY_DAYS`, a whole number 0 or greater); new sessions appear within a second of their first hook. State lives in `~/.salidium` (0700): `salidium.db`, `daemon.json` (port + token, 0600), the hook relay script, and a spool for hooks that fired while the daemon was down.
61
-
62
- ## Development
63
-
64
- ```bash
65
- pnpm install
66
- pnpm build # typecheck + build every package and the UI
67
- pnpm salidium # run the CLI from source (Node type stripping)
68
- pnpm typecheck # tsc --build (project references)
69
- pnpm lint # biome check
70
- pnpm format # biome format --write
71
- pnpm test # vitest: unit, adapter fixtures, daemon integration, performance
72
- pnpm test:e2e # real-daemon Chromium + automated accessibility checks
73
- pnpm test:e2e:full # Chromium, Firefox, and WebKit portability matrix
74
- pnpm package # build the publishable bundle into packages/cli/bundle
75
- pnpm --filter @salidium/ui dev # Vite dev server for the UI, proxied to the running daemon (open it with #token=… from ~/.salidium/daemon.json)
26
+ test/build output → VERIFIED 42 tests passed
27
+ unsupported agent claim → REVIEW Claim needs evidence
76
28
  ```
77
29
 
78
- CI runs lint, build and the full test suite on Ubuntu with Node 24, plus focused native macOS and
79
- Windows checks. It then packs the tarball, unpacks it somewhere with no workspace above it and
80
- starts the daemon from it — because the published bundle and the checkout resolve paths
81
- differently, and only the second arrangement is what a user gets.
82
-
83
- Release preparation, the manual-only publish workflow, and rollback steps are documented in
84
- [`docs/releasing.md`](docs/releasing.md).
30
+ Salidium gives you:
85
31
 
86
- ### Measuring the claims layer
32
+ - **What changed** across the agent's work and files.
33
+ - **Why and how**, with reported reasoning kept clearly attributed.
34
+ - **Verified results** parsed from real test, build, typecheck, and lint output.
35
+ - **Left and Review** for unfinished work, failures, unknowns, and claims needing a person.
36
+ - **Evidence and Rewind** to inspect the original record or reconstruct an earlier moment.
87
37
 
88
- `salidium audit-claims` replays every agent message in your store through the classifier and reports what it did with them: the distribution by kind and by rule, how many messages produced nothing at all, and a seeded random sample of each rule's output to read. Classifier behavior can vary with providers and writing styles, so the honest way to know whether its conservative rules hold for your sessions is to measure your own local corpus.
38
+ Supported agents: **Claude Code** and **Codex**. On first run, Salidium finds them, shows the local
39
+ settings it wants to update, asks permission, and opens the local interface. Node.js 24 or newer is
40
+ required.
89
41
 
90
- ```bash
91
- salidium audit-claims # counts, plus 8 sampled claims per rule
92
- salidium audit-claims --only=discovery --sample=20
93
- salidium audit-claims --json # for a script
94
- ```
42
+ ## Trust model
95
43
 
96
- `pnpm salidium …` runs the CLI from source with Node's type stripping (`--conditions=development`); `pnpm build` emits `dist/` for every package.
44
+ The complete account-free local product lives in this repository. A hosted Salidium service may
45
+ later provide cross-device or team coordination, but the local product does not require it and raw
46
+ provider evidence remains local by default. Read the
47
+ [open-source and hosted-service boundary](docs/open-source-boundary.md) for the exact public and
48
+ private split.
97
49
 
98
- ## Architecture (short)
50
+ Optional generated explanations send a bounded, locally redacted excerpt through the Claude or
51
+ Codex CLI you selected; that CLI may contact its provider and use your plan or API allowance. Set
52
+ `SALIDIUM_EXPLAINER=off` to disable them. The fact-based report still works.
99
53
 
100
- ```
101
- agent runtime ──hooks (async sh relay)──▶ ┐
102
- ──session log (tailed)────▶ ├─▶ provider adapter → canonical events → dedupe → redact → reducer → semantic state + change log
103
- ┘ │
104
- SQLite (events, changes, checkpoints) ◀─┘─▶ SSE → UI (same reducer)
105
- ```
54
+ ## Learn more
106
55
 
107
- - `packages/protocol` — zod schemas shared by everything: canonical events, semantic changes, wire protocol, provenance vocabulary.
108
- - `packages/core` — pure derivation: `applyEvent(state, event) → SemanticChange[]`, session projections, verification parsing, review rules, redaction, replay.
109
- - `packages/adapter-kit`, `packages/adapters/*` — provider parsers (transcript/rollout lines + hook payloads → events with deterministic ids).
110
- - `packages/daemon` tailing, hook ingress, persistence, git snapshots, loopback HTTP/SSE server.
111
- - `packages/ui` React client that folds the event stream with the same reducer.
112
- - `packages/cli` — `salidium` command.
56
+ - [Using Salidium](docs/using-salidium.md): setup, report guide, commands, privacy, and recovery
57
+ - [Architecture](docs/architecture.md): evidence, replay, storage, and extension boundaries
58
+ - [Long-term intelligence](docs/long-term-intelligence.md): memory, retrieval, consent, and evaluation
59
+ - [Evaluation protocol](docs/evaluation-protocol.md): how a future recall claim would have to be proven
60
+ - [Contributing](CONTRIBUTING.md) and [security policy](SECURITY.md)
113
61
 
114
- See [docs/architecture.md](docs/architecture.md) for the decisions and boundaries.
62
+ ## Development
115
63
 
116
- ## Status
64
+ ```bash
65
+ pnpm install --frozen-lockfile
66
+ pnpm lint
67
+ pnpm typecheck
68
+ pnpm test
69
+ pnpm build
70
+ ```
117
71
 
118
- MVP. The integration and replay paths are exercised against synthetic fixtures and local provider
119
- sessions for Claude Code and Codex. The claim classifier is deliberately conservative, but its
120
- behavior may vary across providers and writing styles; `audit-claims` is how to evaluate it on your
121
- own store. Native Windows history import is supported, but live hooks still require POSIX `sh` and
122
- `curl`. See the architecture document for the complete limitations.
72
+ Run from source with `pnpm salidium`. The public CLI is bundled as one self-contained script; the
73
+ separately releasable `@salidium/sync-contract` contains only strict interoperability schemas.
@@ -1114,7 +1114,8 @@ function applyVerificationReview(state, event, log) {
1114
1114
  epistemic: v.outcomeEpistemic
1115
1115
  });
1116
1116
  } else if (v.outcome === "pass" && v.scope !== "partial") {
1117
- resolve(state, event, (r) => r.id === `verification-failed:${v.method}` || r.rule === "changes-unverified");
1117
+ const covers = v.method !== "lint";
1118
+ resolve(state, event, (r) => r.id === `verification-failed:${v.method}` || covers && r.rule === "changes-unverified");
1118
1119
  }
1119
1120
  }
1120
1121
  function applyReviewRulesAfterEvent(state, event, log) {
@@ -23044,6 +23045,7 @@ var SessionCoordinator = class _SessionCoordinator {
23044
23045
  cadence: envAllows ? "turn" : "off",
23045
23046
  idleEndMs: IDLE_END_MS,
23046
23047
  explainSession: explainWithStatus,
23048
+ now: Date.now,
23047
23049
  ...args.options
23048
23050
  };
23049
23051
  const cp = store.latestCheckpoint(sessionId, REDUCER_VERSION);
@@ -23083,7 +23085,7 @@ var SessionCoordinator = class _SessionCoordinator {
23083
23085
  return coord;
23084
23086
  }
23085
23087
  get summary() {
23086
- const s = summarizeSession(this.state, Date.now());
23088
+ const s = summarizeSession(this.state, this.opts.now());
23087
23089
  s.explanationStatus = this.explanationIsCurrent() ? "generated" : this.explanationStatus;
23088
23090
  return s;
23089
23091
  }
@@ -23397,10 +23399,13 @@ var SessionRegistry = class {
23397
23399
  * load: it is one value for the whole daemon, and a coordinator is created on the ingest path.
23398
23400
  */
23399
23401
  explainerCadence = "turn";
23402
+ /** Handed to every coordinator this registry loads; see `CoordinatorOptions.now`. */
23403
+ now;
23400
23404
  constructor(store, opts = {}) {
23401
23405
  this.store = store;
23402
23406
  if (opts.explainerCadence)
23403
23407
  this.explainerCadence = opts.explainerCadence;
23408
+ this.now = opts.now;
23404
23409
  this.listener = {
23405
23410
  onEvents: (sessionId, events, changes) => {
23406
23411
  for (const s of this.allSubscribers) {
@@ -23444,7 +23449,7 @@ var SessionRegistry = class {
23444
23449
  cwd: hint?.cwd,
23445
23450
  store: this.store,
23446
23451
  listener: this.listener,
23447
- options: { cadence: this.explainerCadence }
23452
+ options: { cadence: this.explainerCadence, ...this.now ? { now: this.now } : {} }
23448
23453
  });
23449
23454
  c.onError = (err) => this.onPersistError?.(sessionId, err);
23450
23455
  this.live.set(sessionId, c);
@@ -24169,7 +24174,7 @@ import { dirname as dirname2 } from "node:path";
24169
24174
  import { DatabaseSync } from "node:sqlite";
24170
24175
  import { isDeepStrictEqual } from "node:util";
24171
24176
  import { gunzipSync, gzipSync } from "node:zlib";
24172
- var SCHEMA_VERSION = 5;
24177
+ var SCHEMA_VERSION = 6;
24173
24178
  var INGEST_PARSER_REVISION = "2026-08-19.1";
24174
24179
  function existingSchemaVersion(db) {
24175
24180
  try {
@@ -24312,6 +24317,109 @@ CREATE TABLE IF NOT EXISTS usage_rollups (
24312
24317
  cache_write_tokens INTEGER NOT NULL DEFAULT 0
24313
24318
  );
24314
24319
  `;
24320
+ var SCHEMA_6_DDL = `
24321
+ CREATE TABLE IF NOT EXISTS sync_identity (
24322
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
24323
+ replica_id TEXT NOT NULL UNIQUE,
24324
+ created_at TEXT NOT NULL
24325
+ );
24326
+ CREATE TABLE IF NOT EXISTS sync_streams (
24327
+ stream_id TEXT PRIMARY KEY,
24328
+ replica_id TEXT NOT NULL,
24329
+ -- Positions are unique per generation, not per replica. A replica restored from an older backup
24330
+ -- bumps this so its reused positions are a declared restart rather than a content conflict.
24331
+ replica_generation INTEGER NOT NULL DEFAULT 1,
24332
+ destination_id TEXT NOT NULL UNIQUE,
24333
+ next_control_position INTEGER NOT NULL DEFAULT 0,
24334
+ next_data_position INTEGER NOT NULL DEFAULT 0,
24335
+ acknowledged_control_position INTEGER NOT NULL DEFAULT -1,
24336
+ acknowledged_data_position INTEGER NOT NULL DEFAULT -1,
24337
+ previous_control_operation_id TEXT,
24338
+ previous_data_operation_id TEXT,
24339
+ created_at TEXT NOT NULL
24340
+ );
24341
+ CREATE TABLE IF NOT EXISTS sync_consent_revisions (
24342
+ stream_id TEXT NOT NULL,
24343
+ grant_id TEXT NOT NULL,
24344
+ revision INTEGER NOT NULL,
24345
+ status TEXT NOT NULL CHECK (status IN ('active', 'revoked')),
24346
+ destination_id TEXT NOT NULL,
24347
+ json TEXT NOT NULL,
24348
+ recorded_at TEXT NOT NULL,
24349
+ PRIMARY KEY (stream_id, grant_id, revision),
24350
+ FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
24351
+ );
24352
+ CREATE INDEX IF NOT EXISTS sync_consent_current
24353
+ ON sync_consent_revisions(stream_id, grant_id, revision DESC);
24354
+ CREATE TABLE IF NOT EXISTS intelligence_records (
24355
+ stream_id TEXT NOT NULL,
24356
+ item_id TEXT NOT NULL,
24357
+ revision INTEGER NOT NULL,
24358
+ kind TEXT NOT NULL,
24359
+ grant_id TEXT NOT NULL,
24360
+ grant_revision INTEGER NOT NULL,
24361
+ current INTEGER NOT NULL CHECK (current IN (0, 1)),
24362
+ json TEXT NOT NULL,
24363
+ recorded_at TEXT NOT NULL,
24364
+ PRIMARY KEY (stream_id, item_id, revision),
24365
+ FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
24366
+ );
24367
+ CREATE UNIQUE INDEX IF NOT EXISTS intelligence_one_current_revision
24368
+ ON intelligence_records(stream_id, item_id) WHERE current = 1;
24369
+ CREATE TABLE IF NOT EXISTS intelligence_evidence_map (
24370
+ evidence_id TEXT PRIMARY KEY,
24371
+ independence_id TEXT NOT NULL,
24372
+ session_id TEXT,
24373
+ event_id TEXT,
24374
+ authority TEXT NOT NULL,
24375
+ role TEXT NOT NULL,
24376
+ captured_at TEXT NOT NULL,
24377
+ descriptor_digest TEXT NOT NULL,
24378
+ created_at TEXT NOT NULL
24379
+ );
24380
+ CREATE TABLE IF NOT EXISTS intelligence_tombstones (
24381
+ stream_id TEXT NOT NULL,
24382
+ item_id TEXT NOT NULL,
24383
+ delete_through_revision INTEGER NOT NULL,
24384
+ reason TEXT NOT NULL,
24385
+ requested_at TEXT NOT NULL,
24386
+ receipt_id TEXT,
24387
+ completed_at TEXT,
24388
+ PRIMARY KEY (stream_id, item_id),
24389
+ FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
24390
+ );
24391
+ CREATE TABLE IF NOT EXISTS sync_outbox (
24392
+ stream_id TEXT NOT NULL,
24393
+ lane TEXT NOT NULL CHECK (lane IN ('control', 'data')),
24394
+ position INTEGER NOT NULL,
24395
+ operation_id TEXT NOT NULL UNIQUE,
24396
+ operation_type TEXT NOT NULL,
24397
+ content_digest TEXT NOT NULL,
24398
+ grant_id TEXT,
24399
+ item_id TEXT,
24400
+ json TEXT NOT NULL,
24401
+ created_at TEXT NOT NULL,
24402
+ attempts INTEGER NOT NULL DEFAULT 0,
24403
+ next_attempt_at TEXT,
24404
+ acknowledged_at TEXT,
24405
+ -- A destination can refuse an operation permanently. That is a distinct outcome from durable
24406
+ -- acceptance and must never be recorded as one, or a refused record would read as delivered.
24407
+ refused_at TEXT,
24408
+ refusal_code TEXT,
24409
+ PRIMARY KEY (stream_id, lane, position),
24410
+ FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
24411
+ );
24412
+ CREATE INDEX IF NOT EXISTS sync_outbox_pending
24413
+ ON sync_outbox(stream_id, lane, acknowledged_at, position);
24414
+ CREATE TABLE IF NOT EXISTS sync_deletion_receipts (
24415
+ receipt_id TEXT PRIMARY KEY,
24416
+ stream_id TEXT NOT NULL,
24417
+ operation_id TEXT NOT NULL,
24418
+ json TEXT NOT NULL,
24419
+ completed_at TEXT NOT NULL,
24420
+ FOREIGN KEY (stream_id) REFERENCES sync_streams(stream_id)
24421
+ );
24422
+ `;
24315
24423
  function canonicalizeTimestamp(value) {
24316
24424
  if (typeof value !== "string" || value.length === 0)
24317
24425
  return void 0;
@@ -24579,6 +24687,10 @@ function migrateToSchema5(db, migratedAt = (/* @__PURE__ */ new Date()).toISOStr
24579
24687
  enqueueMissing.run(source.path, source.sessionId, source.provider, source.agentId ?? null, INGEST_PARSER_REVISION, migratedAt);
24580
24688
  db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run("schema_5_migrated_at", migratedAt);
24581
24689
  }
24690
+ function migrateToSchema6(db, migratedAt = (/* @__PURE__ */ new Date()).toISOString()) {
24691
+ db.exec(SCHEMA_6_DDL);
24692
+ db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run("schema_6_migrated_at", migratedAt);
24693
+ }
24582
24694
  var SESSION_PAGE = 500;
24583
24695
  var REAL_SESSION = `COALESCE(json_extract(summary_json, '$.internal'), 0) = 0
24584
24696
  AND instr(COALESCE(title, ''), '[salidium-explainer]') = 0`;
@@ -24632,6 +24744,8 @@ var SqliteStore = class {
24632
24744
  }
24633
24745
  this.db.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA temp_store = MEMORY; PRAGMA foreign_keys = ON;");
24634
24746
  this.db.exec(DDL);
24747
+ if (priorVersion === void 0 && !hadLegacyTables)
24748
+ this.db.exec(SCHEMA_6_DDL);
24635
24749
  try {
24636
24750
  this.db.exec("ALTER TABLE changes ADD COLUMN reducer_version TEXT");
24637
24751
  } catch {
@@ -24647,6 +24761,8 @@ var SqliteStore = class {
24647
24761
  migrateToSchema4(this.db);
24648
24762
  if ((priorVersion ?? 0) < 5)
24649
24763
  migrateToSchema5(this.db);
24764
+ if ((priorVersion ?? 0) < 6)
24765
+ migrateToSchema6(this.db);
24650
24766
  this.db.exec("COMMIT");
24651
24767
  } catch (error51) {
24652
24768
  try {
@@ -25336,7 +25452,8 @@ async function startDaemon(overrides = {}) {
25336
25452
  const stored = readSettings(config2.home, (reason) => log.warn("settings invalid; optional explanations disabled", { reason }));
25337
25453
  const envOff = configuredExplainerMode(process.env) === "off";
25338
25454
  const registry2 = new SessionRegistry(store, {
25339
- explainerCadence: effectiveCadence(stored.explainerCadence)
25455
+ explainerCadence: effectiveCadence(stored.explainerCadence),
25456
+ ...overrides.now ? { now: overrides.now } : {}
25340
25457
  });
25341
25458
  const explainerSettings = () => {
25342
25459
  const usage = registry2.explainerUsage();
@@ -25937,7 +26054,11 @@ function createIntegration(definition) {
25937
26054
  ];
25938
26055
  }
25939
26056
  if (inspection.status === "configured") {
25940
- return [{ level: "ok", message: `${definition.name} connection is configured` }];
26057
+ const ok = {
26058
+ level: "ok",
26059
+ message: `${definition.name} connection is configured`
26060
+ };
26061
+ return definition.standingNote ? [ok, { level: "info", message: definition.standingNote }] : [ok];
25941
26062
  }
25942
26063
  if (inspection.status === "partial") {
25943
26064
  return [
@@ -25978,6 +26099,7 @@ var codex = createIntegration({
25978
26099
  id: "codex",
25979
26100
  name: "Codex",
25980
26101
  command: "codex",
26102
+ standingNote: "Codex honours a hook only once it has been trusted there: open /hooks in Codex. Salidium cannot tell whether that has been done",
25981
26103
  stateDirectory(context) {
25982
26104
  return environment(context).CODEX_HOME ?? join11(context.userHome, ".codex");
25983
26105
  },
@@ -26570,6 +26692,9 @@ ${uiUrl(running)}
26570
26692
  for (const validation of provider.validate(context)) {
26571
26693
  if (validation.level === "attention")
26572
26694
  process.stdout.write(` needs attention: ${validation.message}
26695
+ `);
26696
+ else if (validation.level === "info")
26697
+ process.stdout.write(` note: ${validation.message}
26573
26698
  `);
26574
26699
  }
26575
26700
  }
@@ -0,0 +1 @@
1
+ :root{--font-sans:ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--brand-charcoal:#344047;--brand-warm-white:#f5f4ef;--gray-1:#fff;--gray-2:#f7f7f5;--gray-3:#eae9e6;--gray-4:#e3e2e0;--gray-5:#d0cec9;--gray-6:#b4b3ae;--gray-7:#8b8a85;--gray-8:#5f5e5b;--gray-9:#48463f;--gray-10:#37352f;--bg:var(--gray-1);--bg-subtle:var(--gray-2);--bg-muted:var(--gray-3);--border:var(--gray-4);--border-strong:var(--gray-5);--text:var(--gray-10);--text-secondary:var(--gray-8);--text-tertiary:#66655f;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}:root:not([data-theme=light]){--gray-1:#191919;--gray-2:#202020;--gray-3:#2c2c2c;--gray-4:#2f2f2f;--gray-5:#3d3d3d;--gray-6:#525252;--gray-7:#7a7a7a;--gray-8:#b4b4b2;--gray-9:#d4d4d3;--gray-10:#ededec;--text-tertiary:#9a9a9a}}:root[data-theme=dark]{--gray-1:#191919;--gray-2:#202020;--gray-3:#2c2c2c;--gray-4:#2f2f2f;--gray-5:#3d3d3d;--gray-6:#525252;--gray-7:#7a7a7a;--gray-8:#b4b4b2;--gray-9:#d4d4d3;--gray-10:#ededec;--text-tertiary:#9a9a9a}:root{--z-toolbar:5;--z-float:6;--z-panel:30;--z-drawer:40;--text-xs:11px;--text-sm:12.5px;--text-base:14px;--text-md:15px;--text-lg:19px;--text-xl:23px;--text-display:30px;--leading:1.5;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--radius-xs:4px;--radius-sm:6px;--radius-md:10px;--radius-lg:16px;--row-h:28px;--ease:cubic-bezier(.2, 0, 0, 1);--motion-fast:.12s;--motion-base:.18s;--control-h:28px;--control-px:9px;--control-radius:var(--radius-sm);--control-gap:6px;--icon:14px;--foot-h:42px;--page-w:1000px;--float-bg:color-mix(in srgb, var(--bg) 82%, transparent);--float-blur:blur(12px) saturate(1.6);--scroll-thumb:#37352f33;--scroll-thumb-hover:#37352f61;--measure:68ch;--accent:#3b5bdb;--accent-fg:#fff;--accent-soft:#3b5bdb1a;--ok:#147638;--ok-soft:#15803d1f;--warn:#8a5a12;--warn-soft:#b7791f24;--danger:#c0392b;--danger-soft:#c0392b1f;--facet-why:#7c4bb0;--facet-how:#25708a;--brand-claude:#a8452a;--brand-claude-soft:#d9775724;--brand-codex:#0b7a5e;--brand-codex-soft:#10a37f24;--diff-add-bg:#15803d1f;--diff-del-bg:#c0392b1f;--shadow:0 1px 2px #0000000f;--shadow-float:0 6px 24px #0000001a, 0 1px 3px #00000014;--shadow-float-sm:0 2px 8px #00000012, 0 1px 2px #0000000f}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--accent:#8fa6ff;--accent-fg:#191919;--accent-soft:#8fa6ff24;--ok:#5fcf88;--ok-soft:#5fcf8824;--warn:#e0a83c;--warn-soft:#e0a83c29;--danger:#f0736a;--danger-soft:#f0736a29;--facet-why:#b98ce0;--facet-how:#57b0c9;--brand-claude:#e79b7d;--brand-claude-soft:#d9775729;--brand-codex:#4fc4a3;--brand-codex-soft:#10a37f29;--diff-add-bg:#5fcf8824;--diff-del-bg:#f0736a24;--shadow:0 1px 2px #0006;--shadow-float:0 6px 24px #00000070, 0 1px 3px #0000004d;--shadow-float-sm:0 2px 8px #00000057, 0 1px 2px #0000003d;--scroll-thumb:#ededec2e;--scroll-thumb-hover:#ededec57}}:root[data-theme=dark]{--accent:#8fa6ff;--accent-fg:#191919;--accent-soft:#8fa6ff24;--ok:#5fcf88;--ok-soft:#5fcf8824;--warn:#e0a83c;--warn-soft:#e0a83c29;--danger:#f0736a;--danger-soft:#f0736a29;--facet-why:#b98ce0;--facet-how:#57b0c9;--brand-claude:#e79b7d;--brand-claude-soft:#d9775729;--brand-codex:#4fc4a3;--brand-codex-soft:#10a37f29;--diff-add-bg:#5fcf8824;--diff-del-bg:#f0736a24;--shadow:0 1px 2px #0006;--shadow-float:0 6px 24px #00000070, 0 1px 3px #0000004d;--shadow-float-sm:0 2px 8px #00000057, 0 1px 2px #0000003d;--scroll-thumb:#ededec2e;--scroll-thumb-hover:#ededec57}*,:before,:after{box-sizing:border-box}@supports (corner-shape:squircle){*,:before,:after{corner-shape:squircle}[class*=dot],[class*=mark],[class*=pulse],.opt-mark,.pop-help{corner-shape:round}}.arrives{--leave-delay:0s;opacity:0;transform:translate(var(--slide,0), var(--rise,0));transition:opacity var(--motion-base) var(--ease), transform var(--motion-base) var(--ease), visibility var(--motion-base) var(--ease), position 0s var(--leave-delay) allow-discrete}.arrives:not(.is-open){--leave-delay:var(--motion-base);visibility:hidden;pointer-events:none}.arrives.is-open{opacity:1;transform:none}@starting-style{.arrives.is-open{opacity:0;transform:translate(var(--slide,0), var(--rise,0))}}html,body,#root{height:100%;margin:0;overflow:hidden}body{background:var(--bg);color:var(--text);font-family:var(--font-sans);font-size:var(--text-base);font-weight:400;line-height:var(--leading);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility}h1,h2,h3{letter-spacing:-.01em;margin:0;font-weight:500}button,input,select{font:inherit;color:inherit}button{cursor:pointer;background:0 0;border:0;padding:0}a{color:var(--accent);text-decoration:none}code,.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:.94em}.num{font-variant-numeric:tabular-nums}:focus-visible{outline:2px solid var(--accent);outline-offset:1px}@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}:root{--hover-row:var(--bg-subtle)}*{scrollbar-width:thin;scrollbar-color:transparent transparent}::-webkit-scrollbar{width:12px;height:12px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-corner{background:0 0}::-webkit-scrollbar-thumb{transition:background var(--motion-base) var(--ease);background:padding-box padding-box;border:3px solid #0000;border-radius:8px}[data-scrolling]::-webkit-scrollbar-thumb{background:var(--scroll-thumb);background-clip:padding-box}[data-scrolling]{scrollbar-color:var(--scroll-thumb) transparent}::-webkit-scrollbar-thumb:hover{background:var(--scroll-thumb-hover);background-clip:padding-box}@property --fade-top{syntax:"<length>";inherits:false;initial-value:0}@property --fade-bottom{syntax:"<length>";inherits:false;initial-value:0}.scroll-fade{--fade:24px;--fade-top:0px;--fade-bottom:0px;-webkit-mask-image:linear-gradient(to bottom, transparent, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent);mask-image:linear-gradient(to bottom, transparent, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent);transition:--fade-top var(--motion-fast) var(--ease), --fade-bottom var(--motion-fast) var(--ease)}.scroll-fade[data-fade=top],.scroll-fade[data-fade=both]{--fade-top:var(--fade)}.scroll-fade[data-fade=bottom],.scroll-fade[data-fade=both]{--fade-bottom:var(--fade)}.scroll-fade.at-bottom-only{--fade-top:0px}.field{min-width:0;height:var(--control-h);padding:0 var(--control-px);border:1px solid var(--border);border-radius:var(--control-radius);background:var(--bg);color:var(--text);font-size:var(--text-sm)}.field::placeholder{color:var(--text-tertiary)}.field:focus-visible{outline:2px solid var(--accent);outline-offset:-1px}.btn{justify-content:center;align-items:center;gap:var(--control-gap);height:var(--control-h);padding:0 var(--control-px);border-radius:var(--control-radius);color:var(--text-secondary);font-size:var(--text-sm);white-space:nowrap;transition:background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);background:0 0;border:0;line-height:1;display:inline-flex}.btn:hover,.btn.is-on{background:var(--bg-muted);color:var(--text)}.btn[aria-disabled=true],.btn:disabled{opacity:.45;pointer-events:none}.btn-icon{width:var(--control-h);padding:0}.btn-accent{background:var(--accent);color:var(--accent-fg)}.btn-float{width:var(--foot-h);height:var(--foot-h);pointer-events:auto;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--float-bg);box-shadow:var(--shadow-float-sm);-webkit-backdrop-filter:var(--float-blur);backdrop-filter:var(--float-blur);padding:0}.btn-float:hover{background:var(--bg-muted);color:var(--text)}.btn-accent:hover{background:var(--accent);color:var(--accent-fg);filter:brightness(1.08)}.btn-pill{border-radius:var(--radius-xs);background:var(--bg-muted);height:18px;color:var(--text);font-size:var(--text-xs);align-items:center;padding:0 7px;line-height:1;display:inline-flex}.btn:hover .btn-pill,.btn.is-on .btn-pill{background:var(--bg)}.ico{width:var(--icon);height:var(--icon);stroke:currentColor;stroke-width:1.6px;stroke-linecap:round;stroke-linejoin:round;fill:none;flex-shrink:0;display:block}.chip{align-items:center;gap:var(--space-1);padding:2px var(--space-2);border-radius:var(--radius-sm);color:var(--text-tertiary);font-size:var(--text-xs);transition:background var(--motion-fast) var(--ease);background:0 0;display:inline-flex}.chip.is-on{background:var(--bg-muted);color:var(--text-secondary)}.tag{--tone:var(--border-strong);align-items:baseline;gap:var(--space-2);max-width:100%;padding:3px var(--space-3);border-radius:var(--radius-sm);background:var(--bg-subtle);color:var(--text-secondary);font-size:var(--text-sm);line-height:1.4;display:inline-flex}.tag-strong{color:var(--text)}.tag-key{font-size:var(--text-xs);letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}.tag-note{color:var(--text-tertiary)}.tag-ev{border:1px solid var(--border-strong);border-radius:var(--radius-sm);color:var(--text-secondary);font-size:var(--text-xs);align-items:center;gap:4px;padding:1px 6px 1px 5px;text-decoration:none;display:inline-flex}.tag-ev:hover{border-color:var(--accent);color:var(--accent)}.tag-ev .ico{width:11px;height:11px}.tag[data-tone=pass]{--tone:var(--ok)}.tag[data-tone=fail]{--tone:var(--danger)}.tag[data-tone=attention]{--tone:var(--warn)}.tag[data-tone=working]{--tone:var(--accent)}.tag[data-tone=pass]{background:var(--ok-soft);color:var(--ok)}.tag[data-tone=fail]{background:var(--danger-soft);color:var(--danger)}.tag[data-tone=attention]{background:var(--warn-soft);color:var(--warn)}.tag[data-tone=working]{background:var(--accent-soft);color:var(--accent)}.tag[data-tone] .tag-strong{color:inherit;font-weight:500}.tag[data-tone] .tag-note,.tag[data-tone] .tag-ev{color:var(--text-secondary)}.tag.is-claude-code{background:var(--brand-claude-soft);color:var(--brand-claude)}.tag.is-codex{background:var(--brand-codex-soft);color:var(--brand-codex)}.link{color:var(--text-tertiary);text-decoration:underline;-webkit-text-decoration-color:var(--border-strong);text-decoration-color:var(--border-strong);text-underline-offset:2px;transition:color var(--motion-fast) var(--ease)}.link:hover{color:var(--accent)}.pop{display:inline-flex;position:relative}.pop-panel.is-up{--rise:4px;top:auto;bottom:calc(100% + 6px)}.pop-panel.is-narrow{min-width:0}.pop-panel{z-index:var(--z-drawer);width:max-content;min-width:236px;max-width:320px;padding:var(--space-2);border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg);--rise:-4px;position:absolute;top:calc(100% + 6px);right:0;box-shadow:0 4px 16px #0000001f,0 1px 3px #00000014}.pop-head{align-items:center;gap:var(--space-2);padding:2px var(--space-2) var(--space-2);font-size:var(--text-xs);letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary);font-weight:500;display:flex}.pop-help{border:1px solid var(--border-strong);width:20px;height:20px;color:var(--text-tertiary);transition:background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);border-radius:50%;place-items:center;margin-left:auto;font-size:10px;line-height:1;display:grid}.pop-help:hover,.pop-help.is-on{background:var(--bg-muted);color:var(--text)}.pop-help .ico{width:12px;height:12px}.pop-note{margin:0 0 var(--space-2);padding:var(--space-2);border-radius:var(--radius-sm);background:var(--bg-subtle);font-size:var(--text-xs);color:var(--text-secondary);line-height:1.45}.opts{border:0;flex-direction:column;margin:0;padding:0;display:flex}.opt{cursor:pointer;gap:var(--space-2);padding:var(--space-2);border-radius:var(--control-radius);text-align:left;transition:background var(--motion-fast) var(--ease);display:flex}.opt:hover{background:var(--bg-muted)}.opt:has(:focus-visible){outline:2px solid var(--accent);outline-offset:-2px}.opt-mark{border:1px solid var(--border-strong);width:12px;height:12px;transition:border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease);border-radius:50%;flex-shrink:0;margin-top:2px}.opt.is-on .opt-mark{border-color:var(--accent);box-shadow:inset 0 0 0 3px var(--accent)}.opt-text{min-width:0;display:block}.opt-name{font-size:var(--text-sm);color:var(--text-secondary);display:block}.opt.is-on .opt-name{color:var(--text);font-weight:500}.opt-adds{font-size:var(--text-xs);color:var(--text-tertiary);margin-top:1px;line-height:1.4;display:block}.toolbar{align-items:center;gap:2px;display:flex}.toolbar-sep{width:1px;height:16px;margin:0 var(--space-2);background:var(--border)}.app{grid-template-columns:288px minmax(0,1fr);height:100%;display:grid;overflow:hidden}.app.no-side{grid-template-columns:minmax(0,1fr)}.side{--hover-row:var(--bg-muted);border-right:1px solid var(--border);background:var(--bg-subtle);flex-direction:column;min-height:0;display:flex}@media (min-width:901px){.side{transition:none}.side:not(.is-open){position:absolute}}.side-backdrop,.mobile-side-trigger{display:none}.raw-layer,.side-head>[data-modal-initial]{display:contents}.side-head{align-items:center;gap:var(--space-2);min-height:calc(var(--control-h) + var(--space-4));padding:var(--space-2) var(--space-2) var(--space-2) var(--space-3);display:flex}.side-mark{color:var(--text);flex-shrink:0}.side-head-title{font-size:var(--text-sm);color:var(--text);white-space:nowrap;text-overflow:ellipsis;margin-right:auto;font-weight:500;overflow:hidden}.side-head .pop-panel{max-width:236px}.usage{padding:var(--space-1) var(--space-2) var(--space-2);margin:0}.usage>div{justify-content:space-between;align-items:baseline;gap:var(--space-3);display:flex}.usage dt{font-size:var(--text-xs);color:var(--text-tertiary)}.usage dd{font-size:var(--text-xs);color:var(--text-secondary);margin:0}.side-find{padding:0 var(--space-2) var(--space-2);align-items:center;display:flex}.side-find-mark{z-index:1;width:var(--control-h);height:var(--control-h);margin-right:calc(var(--control-h) * -1);color:var(--text-tertiary);pointer-events:none;place-items:center;display:grid;position:relative}.side-find-field{padding-left:var(--control-h);flex:1}.side-find-clear{margin-left:var(--space-1);flex-shrink:0}.side-scroll{min-height:0;padding:0 var(--space-2) var(--space-4);flex:1;overflow-y:auto}.side-none,.side-hidden{margin:var(--space-3) 0 0;padding:0 var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary)}.side-none{color:var(--text-secondary)}.side-section+.side-section{margin-top:var(--space-4)}.side-section-h{font-size:inherit;font-weight:inherit;letter-spacing:normal;margin:0}.side-section-head{align-items:center;gap:var(--space-2);width:100%;padding:var(--space-1) var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);text-align:left;margin:0;font-weight:500;display:flex}.side-section-head .num{font-variant-numeric:tabular-nums;margin-left:auto}.side-section-head.is-toggle:hover{color:var(--text-secondary)}.session-list{margin:0;padding:0;list-style:none}.session-list.is-quiet .session-title{font-family:var(--font-mono);color:var(--text-tertiary)}.side-empty{padding:var(--space-4);font-size:var(--text-sm);color:var(--text-secondary)}.side-empty p{margin:0 0 var(--space-2)}.side-error{margin:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);background:var(--danger-soft);color:var(--danger);font-size:var(--text-sm)}.side-error p{margin:0}.side-error p+p{margin-top:var(--space-1)}.side-error-title{font-weight:500}.marks-key dl{gap:var(--space-1);padding:0 var(--space-2) var(--space-1);margin:0;display:grid}.marks-key dl>div{align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);display:flex}.marks-key dt{place-items:center;width:22px;margin:0;display:grid}.marks-key dd{margin:0}.marks-key .attn-badge{height:15px;padding:0 5px 0 3px;font-size:10px}.marks-key .attn-badge .ico{width:9px;height:9px}.main{min-width:0;height:100%;overflow:hidden}.main-empty{height:100%;color:var(--text-secondary);place-items:center;display:grid}.main-empty-first{gap:var(--space-3);max-width:42ch;padding:0 var(--space-4);text-align:center;flex-direction:column;display:flex}.main-empty-first p{margin:0}.main-empty-first .link{align-self:center}.session-row{align-items:center;gap:var(--space-2);width:100%;height:var(--row-h);padding:0 var(--space-2);border-radius:var(--radius-md);color:var(--text-secondary);text-align:left;transition:background var(--motion-fast) var(--ease);display:flex}.session-row:hover{background:var(--hover-row)}.session-row.is-selected{background:var(--bg-muted);color:var(--text)}.session-title{min-width:0;font-size:var(--text-sm);white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.is-selected .session-title{font-weight:500}.session-when{font-size:var(--text-xs);color:var(--text-tertiary);font-variant-numeric:tabular-nums;flex-shrink:0}.attn-badge{border-radius:var(--radius-xs);background:var(--bg-muted);height:18px;color:var(--text-tertiary);font-size:var(--text-xs);flex-shrink:0;align-items:center;gap:3px;padding:0 6px 0 4px;line-height:1;display:inline-flex}.attn-badge .ico{width:11px;height:11px}.session-row:hover .attn-badge,.session-row.is-selected .attn-badge{background:var(--bg)}.attn-badge.is-live{background:var(--warn-soft);color:var(--warn)}.status-dot{font-size:9px;line-height:1}.status-dot.st-working{color:var(--ok)}.status-dot.st-waiting{color:var(--warn)}.status-dot.st-idle,.status-dot.st-ended,.status-dot.st-unknown{color:var(--text-tertiary)}.session{grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr);height:100%;display:grid;overflow:hidden}.session.has-inspector{grid-template-columns:minmax(0,1fr) 320px}.session-foot{--rise:var(--space-2);z-index:var(--z-float);padding:0 var(--page-gutter) var(--space-4);pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;bottom:0;left:0;right:0}.session-foot:empty{padding:0}.session-main{--page-gutter:var(--space-6);--foot-space:0px;flex-direction:column;min-width:0;min-height:0;display:flex;position:relative}.session-content{min-width:0;min-height:0;padding:0 var(--page-gutter) calc(var(--space-10) + var(--foot-space));scrollbar-gutter:stable both-edges;flex:1;overflow-y:auto}.session-actions{z-index:var(--z-toolbar);align-items:center;gap:var(--space-4);padding:var(--space-2) var(--space-3);background:var(--bg);display:flex;position:sticky;top:0}.session-actions-end{flex-wrap:wrap;justify-content:flex-end;margin-left:auto}.page{max-width:var(--page-w);padding-top:var(--space-10);margin:0 auto;container:page/inline-size}.masthead{text-align:center}.masthead-mark{color:var(--text-tertiary);margin:0 auto;display:block}.brand-lockup{color:inherit;font-family:var(--font-sans);align-items:center;gap:.48em;display:inline-flex}.brand-lockup-mark{flex-shrink:0}.brand-lockup-name{line-height:1}.masthead-title{max-width:42ch;margin:var(--space-4) auto 0;font-size:var(--text-display);letter-spacing:-.02em;text-wrap:balance;font-weight:300;line-height:1.12}.masthead-meta{justify-content:center;gap:var(--space-2);margin:var(--space-4) 0 0;flex-wrap:wrap;display:flex}.tag.is-status{align-items:center;gap:5px}.masthead-path{text-overflow:ellipsis;white-space:nowrap;direction:rtl;max-width:46ch;overflow:hidden}.masthead-badges{justify-content:center;align-items:stretch;gap:var(--space-2);margin-top:var(--space-3);padding-bottom:var(--space-8);border-bottom:1px solid var(--border);flex-wrap:wrap;display:flex}.fresh{height:var(--control-h);margin-right:var(--space-2);padding:0 var(--control-px);border-radius:var(--control-radius);background:var(--accent-soft);color:var(--accent);font-size:var(--text-xs);white-space:nowrap;transition:background var(--motion-fast) var(--ease);align-items:center;display:inline-flex}.fresh:hover{background:var(--accent);color:var(--accent-fg)}.timeline{--tl-lane:9px;max-width:var(--measure);margin-bottom:var(--space-8)}.tl-track{height:18px;margin-bottom:var(--tl-lane);border-bottom:1px solid var(--border);position:relative}.tl-marks{position:absolute;inset:0}.tl-mark{background:var(--text-tertiary);border-radius:0 0 1px 1px;width:2px;height:5px;margin-left:-1px;position:absolute;top:100%}.tl-mark.tl-pass{background:var(--ok);height:5px}.tl-mark.tl-fail{width:3px;height:var(--tl-lane);background:var(--danger);margin-left:-1.5px}.tl-mark.tl-commit{background:var(--text-secondary);border-radius:0;width:5px;height:5px;margin-left:-2.5px;top:calc(100% + 1px);transform:rotate(45deg)}.tl-future{background:var(--bg);opacity:.66;pointer-events:none;position:absolute;top:0;bottom:0;right:0}.tl-range{appearance:none;cursor:ew-resize;background:0 0;width:100%;height:100%;margin:0;position:absolute;inset:0}.tl-range::-webkit-slider-thumb{border:3px solid var(--bg);background:var(--text);width:16px;height:16px;box-shadow:var(--shadow-float-sm);appearance:none;cursor:grab;border-radius:50%}.tl-range::-moz-range-thumb{border:3px solid var(--bg);background:var(--text);width:16px;height:16px;box-shadow:var(--shadow-float-sm);cursor:grab;border-radius:50%}.tl-range:active::-webkit-slider-thumb{cursor:grabbing}.tl-range:focus-visible{outline:none}.tl-range:focus-visible::-webkit-slider-thumb{outline:2px solid var(--accent);outline-offset:2px}.tl-range:focus-visible::-moz-range-thumb{outline:2px solid var(--accent);outline-offset:2px}.tl-stem{background:var(--text-tertiary);pointer-events:none;width:2px;position:absolute;top:0;bottom:0}.tl-legend{justify-content:space-between;align-items:baseline;gap:var(--space-3);margin-top:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);display:flex}.tl-legend-mid{align-items:baseline;gap:var(--space-2);display:flex}.tl-legend-mid strong{color:var(--text-secondary);font-weight:400}.viz-title{align-items:baseline;gap:var(--space-2);margin-bottom:var(--space-3);font-size:var(--text-xs);letter-spacing:.09em;text-transform:uppercase;color:var(--text-tertiary);font-weight:500;display:flex}.viz-count{font-family:var(--font-mono);letter-spacing:0;color:var(--text-secondary)}.viz-foot{margin:var(--space-2) 0 0;font-size:var(--text-xs);color:var(--text-tertiary)}.flow{margin:0;padding:0;list-style:none}.flow-turn+.flow-turn{margin-top:var(--space-4)}.flow-ask{align-items:baseline;gap:var(--space-2);width:100%;margin:0 0 var(--space-2);padding:2px var(--space-2) 2px 0;border-radius:var(--radius-sm);text-align:left;transition:background var(--motion-fast) var(--ease);display:flex}.flow-ask:hover{background:var(--hover-row)}.flow-sum{font-size:var(--text-xs);color:var(--text-tertiary);white-space:nowrap;flex-shrink:0;margin-left:auto}.flow-sum-ok{color:var(--ok)}.flow-sum-bad{color:var(--danger)}.flow-sum-gap{color:var(--warn)}.flow{--flow-marker:18px;--flow-indent:calc(var(--flow-marker) / 2);--flow-gutter:var(--space-5)}.flow-ask-n{width:var(--flow-marker);height:var(--flow-marker);background:var(--bg-muted);color:var(--text-secondary);font-family:var(--font-mono);font-size:var(--text-xs);line-height:var(--flow-marker);text-align:center;border-radius:50%;flex-shrink:0}.flow-ask-text{font-size:var(--text-sm);color:var(--text-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.flow-steps{margin:0 0 0 var(--flow-indent);padding:var(--space-1) 0 var(--space-1) var(--flow-gutter);border-left:1px solid var(--border);list-style:none}.flow-step{align-items:baseline;gap:var(--space-2);padding:2px 0;display:flex;position:relative}.flow-node{left:calc((var(--flow-gutter) + var(--flow-indent)) * -1);box-sizing:border-box;width:var(--flow-marker);height:var(--flow-marker);border:1px solid var(--border);background:var(--bg);color:var(--text-tertiary);font-family:var(--font-mono);font-size:10px;line-height:calc(var(--flow-marker) - 2px);text-align:center;border-radius:50%;position:absolute}.flow-label{font-size:var(--text-sm);color:var(--text-secondary);text-align:left}:is(.flow-step:has(.flow-label.as-button:hover),.flow-step:has(.flow-label.as-button:focus-visible)){background:var(--hover-row)}.flow-label.as-button:focus-visible{outline:none}.flow-detail{margin-left:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary)}.flow-at{padding-left:var(--space-3);font-size:var(--text-xs);color:var(--text-tertiary);margin-left:auto}.flow-derived{margin-left:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);border-bottom:1px dashed var(--border-strong)}.flow-step.is-check .flow-label{color:var(--text)}.flow-step.is-check.v-pass .flow-node{border-color:var(--ok);color:var(--ok)}.flow-step.is-check.v-fail .flow-node{border-color:var(--danger);color:var(--danger)}.flow-step.is-check.v-unknown .flow-node,.flow-step.is-check.v-partial .flow-node{border-color:var(--warn);color:var(--warn)}.flow-step.is-commit .flow-node{border-color:var(--accent);color:var(--accent)}.flow-step.is-gap .flow-node{border-style:dashed;border-color:var(--warn);color:var(--warn)}.flow-step.is-gap .flow-label{color:var(--warn)}.flow-step.is-quiet .flow-label,.flow-step.is-quiet .flow-node{color:var(--text-tertiary);border-color:var(--border)}.flow-more{margin-bottom:var(--space-3);font-size:var(--text-xs);color:var(--text-tertiary);text-underline-offset:2px;text-decoration:underline}.churn{margin:0;padding:0;list-style:none}.churn-main{align-items:center;gap:var(--space-3);text-align:left;grid-template-columns:minmax(0,15ch) minmax(0,1fr) 11ch 1ch;width:100%;padding:3px 0;display:grid}.churn-main:hover .churn-name{color:var(--text)}.churn-name{align-items:baseline;gap:var(--space-2);font-size:var(--text-xs);color:var(--text-secondary);white-space:nowrap;display:flex;overflow:hidden}.churn-dir{min-width:0;color:var(--text-tertiary);text-overflow:ellipsis;overflow:hidden}.churn-bar{background:var(--bg-subtle);border-radius:2px;height:9px;display:flex;overflow:hidden}.churn-add{background:var(--ok)}.churn-del{background:var(--danger)}.churn-num{justify-content:flex-end;gap:var(--space-2);font-family:var(--font-mono);font-size:var(--text-xs);display:flex}.churn-flag{color:var(--warn);font-family:var(--font-mono);font-size:var(--text-xs);text-align:center}.lane{align-items:center;gap:var(--space-3);grid-template-columns:9ch minmax(0,1fr) 2ch;height:22px;display:grid}.viz-root{margin:0 0 var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);overflow-wrap:anywhere}.sections{max-width:var(--measure);margin-top:var(--space-8);border-top:1px solid var(--border)}.sec{border-bottom:1px solid var(--border)}.chev{color:var(--text-tertiary);transition:transform var(--motion-base) var(--ease);display:inline-block}.chev.is-open{transform:rotate(90deg)}.sec-review{max-width:var(--measure);margin-bottom:var(--space-8);border-bottom:0}.rows{margin:0;padding:0;list-style:none}.row{gap:var(--space-2);padding:var(--space-2) 0 var(--space-2) var(--space-3);border-left:2px solid var(--border);display:flex}.row+.row{border-top:1px solid var(--bg-muted)}.row.ep-inferred{background-position:0 0;background-repeat:no-repeat;background-size:2px 100%;background-image:repeating-linear-gradient(var(--border-strong) 0 8px, transparent 8px 13px);border-left-color:#0000}.row-flat{font-size:var(--text-sm);align-items:baseline}.bad{color:var(--danger)}.bad-title{margin:0 0 var(--space-1);font-weight:500}.bad p+button{margin-top:var(--space-3)}.running{color:var(--accent)}.disclosure{margin-top:var(--space-3)}.disclosure-toggle{align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);display:flex}.disclosure-toggle:hover{color:var(--text)}.disclosure-body{visibility:hidden;transition:grid-template-rows var(--motion-base) var(--ease), visibility var(--motion-base) var(--ease);grid-template-rows:0fr;display:grid}.disclosure-body>*{overflow:hidden}.disclosure-body.is-open{visibility:visible;grid-template-rows:1fr}.turns{margin:0;padding:0;list-style:none}.turn{padding:var(--space-3) 0;border-top:1px solid var(--bg-muted)}.turn:first-child{border-top:0}.activities{margin:var(--space-2) 0 0;padding:0;list-style:none}.activity{border-left:2px solid var(--border)}.inspector{border-left:1px solid var(--border);background:var(--bg-subtle);flex-direction:column;min-width:0;min-height:0;display:flex}.inspector-head{align-items:center;gap:var(--space-1);min-height:calc(var(--control-h) + var(--space-4));padding:var(--space-2) var(--space-3) var(--space-2) var(--space-2);display:flex}.inspector-title{font-size:var(--text-sm);color:var(--text);white-space:nowrap;margin-left:auto;font-weight:500}.inspector-title .num{margin-left:var(--space-1);color:var(--text-tertiary);font-weight:400}.htab{min-height:0;padding:0 var(--space-6) calc(var(--space-4) + var(--foot-space));flex-direction:column;flex:1;display:flex}.htab-head{align-items:center;gap:var(--space-3);padding:var(--space-5) 0 var(--space-4);flex-wrap:wrap;display:flex}.htab-title{font-size:var(--text-lg);margin-right:auto;font-weight:500}.htab-title .num{margin-left:var(--space-2);color:var(--text-tertiary);font-weight:400}.htab-grid{flex-direction:column;flex:1;min-height:0;display:flex}.htab-row{align-items:baseline;gap:var(--space-4);padding:var(--space-2) var(--space-3);border-radius:var(--control-radius);text-align:left;grid-template-columns:64px 112px minmax(0,1fr) 96px 64px;display:grid}.htab-row.is-head{border-bottom:1px solid var(--border);font-size:var(--text-xs);letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary);border-radius:0;flex-shrink:0;font-weight:500}.htab-scroll{flex:1;min-height:0;overflow-y:auto}.htab-row:not(.is-head):hover{background:var(--hover-row)}.htab-row.is-scrub{background:var(--accent-soft)}.htab-row.is-future{opacity:.4}.htab-when{color:var(--text-tertiary);font-size:var(--text-sm);text-align:left}.htab-when:hover{color:var(--accent)}.htab-kind{align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);display:flex}.htab-text{font-size:var(--text-sm);overflow-wrap:anywhere;line-height:1.45}.htab-prov{font-size:var(--text-xs);color:var(--text-tertiary)}.history-scope{gap:var(--space-1);padding:0 var(--space-2) var(--space-2);flex-wrap:wrap;display:flex}.pop-panel.is-left{left:0;right:auto}.opt-swatch{border:1.5px solid var(--facet,var(--border-strong));border-radius:var(--radius-xs);width:12px;height:12px;transition:background var(--motion-fast) var(--ease);background:0 0;flex-shrink:0}.opt.is-on .opt-swatch{background:var(--facet,var(--border-strong))}.opt.is-tight{align-items:center;gap:var(--space-3);padding:5px var(--space-2)}.history-list{min-height:0;padding:0 var(--space-2) var(--space-4);overscroll-behavior:contain;flex:1;overflow-y:auto}.facet-status{--facet:var(--gray-6)}.facet-what{--facet:var(--accent)}.facet-why{--facet:var(--facet-why)}.facet-how{--facet:var(--facet-how)}.facet-verified{--facet:var(--ok)}.facet-left{--facet:var(--gray-7)}.facet-review{--facet:var(--warn)}.hist-item{padding:2px 0;position:relative}.hist-item.is-future{opacity:.35}.hist-main{width:100%;padding:var(--space-2);border-radius:var(--control-radius);text-align:left;transition:background var(--motion-fast) var(--ease);display:block}.hist-main:hover{background:var(--bg-muted)}.hist-item.is-scrub .hist-main{background:var(--accent-soft)}.hist-meta{align-items:center;gap:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);white-space:nowrap;display:flex}.hist-prov{border-bottom:1px dashed var(--border-strong);line-height:1.2}.hist-dot{background:var(--facet,var(--border-strong));border-radius:50%;flex-shrink:0;width:6px;height:6px}.hist-facet{color:var(--text-secondary)}.hist-time{font-variant-numeric:tabular-nums;margin-left:auto}.hist-text{font-size:var(--text-sm);color:var(--text);overflow-wrap:anywhere;margin-top:3px;line-height:1.4;display:block}.hist-record{right:var(--space-2);bottom:var(--space-2);font-size:var(--text-xs);opacity:0;transition:opacity var(--motion-fast) var(--ease);position:absolute}.hist-item:hover .hist-record,.hist-record:focus-visible{opacity:1}.prov{font-size:var(--text-xs);color:var(--text-tertiary)}.gate{height:100%;padding:var(--space-8) var(--space-6);flex-direction:column;justify-content:safe center;align-items:center;display:flex;overflow-y:auto}.gate-inner{width:100%;max-width:46ch}.gate-mark{margin:0 0 var(--space-6);font-size:var(--text-xl);font-weight:300}.gate-lede{margin:0 0 var(--space-2);font-size:var(--text-md)}.gate-note{margin:0 0 var(--space-6);color:var(--text-secondary)}.gate-cmd{align-items:center;gap:var(--space-2);padding:var(--space-2);padding-left:var(--space-4);border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-muted);display:flex}.gate-cmd code{min-width:0;font-family:var(--font-mono);font-size:var(--text-sm);white-space:nowrap;scrollbar-width:none;flex:1;overflow-x:auto}.gate-cmd code::-webkit-scrollbar{display:none}.gate-hint{margin:var(--space-2) 0 0;font-size:var(--text-sm);color:var(--text-tertiary)}.gate-paste{gap:var(--space-2);margin-top:var(--space-6);padding-top:var(--space-6);border-top:1px solid var(--border);display:flex}.gate-field{font-family:var(--font-mono);flex:1}.gate-field::placeholder{font-family:var(--font-sans)}.gate-bad{margin:var(--space-3) 0 0;color:var(--danger);font-size:var(--text-sm)}.gate-bad code{font-family:var(--font-mono)}.session-error{text-align:left;max-width:52ch}.session-error-title{margin:0 0 var(--space-2);font-size:var(--text-md)}.session-error-actions{gap:var(--space-2);margin-top:var(--space-4);display:flex}.drawer{z-index:var(--z-drawer);border-top:1px solid var(--border);border-left:1px solid var(--border);background:var(--bg);width:min(760px,100vw);max-height:70vh;box-shadow:var(--shadow);flex-direction:column;display:flex;position:fixed;bottom:0;right:0}.drawer-head{justify-content:space-between;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3) var(--space-2) var(--space-4);border-bottom:1px solid var(--border);display:flex}.drawer-title{min-width:0;font-size:var(--text-md);white-space:nowrap;text-overflow:ellipsis;margin:0;overflow:hidden}.drawer-acts{align-items:center;gap:var(--space-1);flex-shrink:0;display:flex}.drawer-panes{align-items:center;gap:var(--space-1);padding:var(--space-2) var(--space-4);border-bottom:1px solid var(--border);display:flex}.drawer-id{min-width:0;font-size:var(--text-xs);color:var(--text-tertiary);white-space:nowrap;text-overflow:ellipsis;margin-left:auto;overflow:hidden}.drawer-body{padding:var(--space-4);overflow:auto}.drawer-sub{margin:var(--space-5) 0 var(--space-2);font-size:var(--text-xs);letter-spacing:.06em;color:var(--text-tertiary);text-transform:uppercase;font-weight:500}.facts{margin:0}.fact{gap:var(--space-3);grid-template-columns:8.5em minmax(0,1fr);padding:3px 0;display:grid}.fact.is-block{padding:var(--space-2) 0;grid-template-columns:minmax(0,1fr);gap:2px}.fact-label{font-size:var(--text-xs);color:var(--text-tertiary);margin:0}.fact-value{align-items:baseline;gap:2px var(--space-2);min-width:0;font-size:var(--text-sm);overflow-wrap:anywhere;flex-wrap:wrap;margin:0;display:flex}.fact.is-block .fact-value{padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);background:var(--bg-subtle);white-space:pre-wrap;overflow-wrap:anywhere;display:block}.fact-note{font-size:var(--text-xs);color:var(--text-tertiary)}.fact.is-block .fact-note{margin-top:var(--space-1);font-family:var(--font-sans);display:block}.fact-copy{opacity:0;transition:opacity var(--motion-fast) var(--ease);flex-shrink:0}.fact:hover .fact-copy,.fact-copy:focus-visible{opacity:1}.fact.is-block .fact-copy{float:right;margin-left:var(--space-2)}@media (max-width:560px){.fact{padding:var(--space-2) 0;grid-template-columns:minmax(0,1fr);gap:0}}.drawer-full{margin-top:var(--space-5);padding-top:var(--space-3);border-top:1px solid var(--border)}.drawer-full summary{font-size:var(--text-xs);color:var(--text-tertiary);cursor:pointer}.drawer-full summary:hover{color:var(--text-secondary)}.drawer-full pre{margin-top:var(--space-3)}.drawer-body pre{font-size:var(--text-xs);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;line-height:1.5}.diff{margin:var(--space-2) 0;border:1px solid var(--border);border-radius:var(--radius-sm);font-size:var(--text-xs);line-height:1.5;overflow-x:auto}.diff-add{background:var(--diff-add-bg)}.diff-del{background:var(--diff-del-bg)}.muted{color:var(--text-tertiary)}.sr-only{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;margin:-1px;padding:0;position:fixed;top:0;left:0;overflow:hidden}@media (max-width:900px){.app{grid-template-columns:minmax(0,1fr)}.side{z-index:calc(var(--z-panel) + 1);width:min(320px,100vw - 48px);box-shadow:var(--shadow-float);--slide:-12px;display:flex;position:fixed;inset:0 auto 0 0}.side-backdrop{z-index:var(--z-panel);cursor:default;background:#00000052;border:0;width:100%;height:100%;padding:0;display:block;position:fixed;inset:0}.mobile-side-trigger{top:var(--space-2);left:var(--space-2);z-index:calc(var(--z-toolbar) + 1);display:block;position:fixed}.app.no-side .session-actions>.btn:first-child{display:none}.app.no-side .session-actions{padding-left:calc(var(--space-2) + var(--control-h) + var(--space-2))}.session.has-inspector{grid-template-rows:minmax(0,1fr) minmax(0,min(320px,40%));grid-template-columns:minmax(0,1fr)}.inspector{border-top:1px solid var(--border);border-left:0}.session-main{--page-gutter:var(--space-4)}.page{padding-top:var(--space-6)}.masthead-title{font-size:var(--text-xl)}.masthead-path{max-width:30ch}}.report{margin-top:var(--space-10)}.rp-cols{gap:var(--space-6) var(--space-8);grid-template-columns:repeat(3,minmax(0,1fr));align-items:start;display:grid}.rp-cols>*{min-width:0}.rp-title{margin-bottom:var(--space-3);font-size:var(--text-xs);letter-spacing:.1em;text-transform:uppercase;color:var(--text-tertiary);font-weight:500}.rp-statement{margin:0 0 var(--space-3);padding-left:var(--space-3);border-left:2px solid var(--border);font-size:var(--text-sm);color:var(--text)}.rp-statement-body{-webkit-line-clamp:4;line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.rp-statement-body.is-open{display:block;overflow:visible}.rp-ev{margin-left:var(--space-2);font-size:var(--text-xs)}.rp-none{font-size:var(--text-sm);color:var(--text-tertiary);margin:0}.rp-status{margin:0 0 var(--space-3);font-size:var(--text-sm);padding:0;list-style:none}.rp-status li{gap:var(--space-2);flex-wrap:wrap;align-items:baseline;padding:2px 0;display:flex}.rp-mark{width:1em;font-family:var(--font-mono);color:var(--text-tertiary);text-align:center;flex-shrink:0}.rp-status .v-pass .rp-mark{color:var(--ok)}.rp-status .v-fail .rp-mark{color:var(--danger)}.rp-status .is-attn .rp-mark{color:var(--warn)}.rp-status-label{text-align:left;color:inherit}.rp-counts{margin-left:var(--space-2);color:var(--text-tertiary)}.rp-derived{font-size:var(--text-xs);color:var(--text-tertiary);border-bottom:1px dashed var(--border-strong)}.rp-subset,.rp-caveat{font-size:var(--text-xs);color:var(--text-tertiary)}.rp-stale{margin-left:var(--space-2);font-size:var(--text-xs);color:var(--warn);white-space:nowrap}.rp-group{align-items:start}.rp-group-body{flex:1;min-width:0}.rp-group-head{align-items:baseline;gap:var(--space-3);text-align:left;width:100%;color:inherit;display:flex}.rp-group-label{min-width:0}.rp-group-count{color:var(--text-tertiary);white-space:nowrap;margin-left:auto}.rp-group-distinct{font-family:var(--font-sans)}.rp-group-head:hover{background:var(--hover-row)}.rp-group-items{margin:var(--space-1) 0 0;padding:0 0 0 var(--space-1);border-left:1px solid var(--border);list-style:none}.rp-group-items li{align-items:baseline;gap:var(--space-2);padding:2px 0 2px var(--space-3);color:var(--text-secondary);display:flex}.rp-group-items .mono{overflow-wrap:break-word;min-width:0}.rp-group-repeats{color:var(--text-tertiary);flex-shrink:0}.rp-empty{max-width:var(--measure);margin:var(--space-6) 0;font-size:var(--text-sm);color:var(--text-tertiary)}.quantities-body{min-height:0;padding:var(--space-2) var(--space-5) var(--space-6);overflow-y:auto}.tm{min-width:0}.tm-group{padding:var(--space-5) 0}.tm-group:first-child{padding-top:var(--space-3)}.tm-group+.tm-group{border-top:1px solid var(--border)}.tm-title{margin:0 0 var(--space-4);font-size:var(--text-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--text-tertiary);font-weight:500}.tm-grid{gap:var(--space-2);grid-template-columns:repeat(3,minmax(0,1fr));margin:0;display:grid}.tm-grid.is-wide{grid-template-columns:repeat(2,minmax(0,1fr))}.tm-grid.is-wide dd{white-space:nowrap;overflow-x:auto}.tm-quantity{flex-direction:column;min-width:0;display:flex}.tm-quantity dt{margin-top:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary)}.tm-quantity dd{font-family:var(--font-mono);font-size:var(--text-lg);color:var(--text);overflow-wrap:anywhere;order:-1;margin:0;line-height:1.15}.tm .num{font-family:var(--font-mono);color:var(--text)}.tm-unknown{font-family:var(--font-sans);font-size:var(--text-sm);color:var(--text-tertiary)}.tm-add{color:var(--ok);font-family:var(--font-mono)}.tm-del{color:var(--danger);font-family:var(--font-mono)}.tm .v-pass{color:var(--ok)}.tm .v-fail{color:var(--danger)}.tm .v-partial,.tm .v-unknown{color:var(--warn)}.tm-check{align-items:baseline;gap:var(--space-2);font-size:var(--text-sm);color:var(--text);grid-template-columns:minmax(0,1fr) auto auto;display:grid}.tm-check-method{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.tm-check>[class^=v-]{padding:2px var(--space-2);border-radius:var(--radius-xs);background:var(--bg-muted);font-size:var(--text-xs);letter-spacing:.05em;text-transform:uppercase;font-weight:600}.tm-note-list{gap:var(--space-2);font-size:var(--text-xs);color:var(--text-tertiary);margin:0;padding:0;list-style:none;display:grid}.tm-note-list li{padding-left:var(--space-4);position:relative}.tm-note-list li:before{content:"";background:currentColor;border-radius:50%;width:4px;height:4px;position:absolute;top:.55em;left:0}.ex{margin:var(--space-8) calc(var(--page-gutter) * -1) 0;padding:var(--space-6) var(--page-gutter) var(--space-5);background:var(--bg-subtle)}.ex-what{max-width:56ch;font-size:var(--text-xl);letter-spacing:-.01em;text-align:center;text-wrap:pretty;margin:0 auto;font-weight:300;line-height:1.35}.ex-now{gap:var(--space-2);max-width:68ch;margin:var(--space-4) auto 0;font-size:var(--text-sm);color:var(--text-secondary);text-align:center;justify-content:center;align-items:baseline;display:flex}.ex-now-key{font-size:var(--text-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--text-tertiary);flex-shrink:0}.ex-block{margin-top:var(--space-10)}.ex-head{margin-bottom:var(--space-6)}.ex-title{padding-bottom:var(--space-2);border-bottom:1px solid var(--border-strong);font-size:var(--text-xs);letter-spacing:.14em;text-transform:uppercase;color:var(--text-secondary);font-weight:500}.ex-summary{max-width:var(--measure);margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--text-secondary)}.ex-foot{justify-content:space-between;align-items:baseline;gap:var(--space-3) var(--space-6);margin-top:var(--space-8);padding-top:var(--space-3);border-top:1px dashed var(--border-strong);flex-wrap:wrap;display:flex}.ex-mark{font-size:var(--text-xs);color:var(--text-tertiary);margin:0}.ex-pending{max-width:var(--measure);margin:var(--space-8) auto 0;padding:var(--space-5);border:1px dashed var(--border-strong);border-radius:var(--radius-md);font-size:var(--text-sm);color:var(--text-tertiary);text-align:center}.ex-pending p{margin:0}.ex-pending-main{font-size:var(--text-md);color:var(--text-secondary);margin-bottom:var(--space-2)!important}.ex-pending.is-running{border-style:solid;border-color:var(--border)}.shimmer{background:linear-gradient(90deg, var(--text-tertiary) 0%, var(--text-tertiary) 35%, var(--text) 50%, var(--text-tertiary) 65%, var(--text-tertiary) 100%) 0 0 / 300% 100%;color:#0000;-webkit-background-clip:text;background-clip:text;animation:2.4s linear infinite shimmer}@keyframes shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media (prefers-reduced-motion:reduce){.shimmer{color:var(--text-secondary);background:0 0}.ex-pending.is-running{border-color:var(--border-strong)}}.fd-node{padding:var(--space-3) var(--space-3);border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--bg);font-size:var(--text-sm);text-wrap:pretty;line-height:1.35;position:relative}.fd-chain{gap:var(--space-6);margin:0;padding:0;list-style:none;display:flex}.fd-chain>.fd-node{flex:1 1 0;min-width:0}.fd-chain>.fd-node+.fd-node:before{content:"";top:50%;left:calc(var(--space-6) * -1 - 1px);width:var(--space-6);background:var(--border-strong);height:1px;position:absolute}.fd-chain>.fd-node+.fd-node:after{content:"";border-left:7px solid var(--border-strong);border-top:4px solid #0000;border-bottom:4px solid #0000;margin-top:-4px;position:absolute;top:50%;left:-8px}.fd-chain.end-accent>.fd-node:last-child{border-color:var(--accent);color:var(--text)}.fd-chain.end-good>.fd-node:last-child{border-color:var(--ok)}.fd-chain.end-bad>.fd-node:last-child{border-color:var(--danger)}.fd-converge,.fd-tree{--fd-pad:var(--space-3)}.fd-lanes{margin:0 calc(var(--fd-pad) * -1);display:grid}.fd-lane{min-width:0;margin:0 var(--fd-pad);padding:var(--space-3) var(--space-4) var(--space-4);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg)}.fd-lane-title{margin:0 0 var(--space-3);padding-bottom:var(--space-2);border-bottom:1px solid var(--border);font-size:var(--text-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);font-weight:500}.fd-lane-steps{margin:0;padding:0;list-style:none}.fd-lane-steps li{padding:var(--space-1) 0 var(--space-1) var(--space-4);font-size:var(--text-sm);color:var(--text-secondary);position:relative}.fd-lane-steps li:before{content:"";background:var(--border);width:1px;position:absolute;top:0;bottom:0;left:3px}.fd-lane-steps li:first-child:before{top:50%}.fd-lane-steps li:last-child:before{bottom:50%}.fd-lane-steps li:after{content:"";background:var(--border-strong);border-radius:50%;width:7px;height:7px;margin-top:-3px;position:absolute;top:50%;left:0}.fd-junction{height:52px;margin:var(--space-2) calc(var(--fd-pad) * -1);position:relative}.fd-trunk,.fd-bus{background:var(--border-strong);position:absolute}.fd-trunk{width:1px;top:26px;bottom:0;left:50%}.fd-trunk:after{content:"";border-top:7px solid var(--border-strong);border-left:4px solid #0000;border-right:4px solid #0000;position:absolute;bottom:0;left:-4px}.fd-bus{top:26px;left:calc(50% / var(--lanes));right:calc(50% / var(--lanes));height:1px}.fd-stems{grid-template-columns:repeat(var(--lanes), minmax(0, 1fr));height:26px;display:grid;position:absolute;top:0;left:0;right:0}.fd-stems>span{background:var(--border-strong);justify-self:center;width:1px;position:relative}.fd-junction.is-fan .fd-trunk{height:26px;top:0;bottom:auto}.fd-junction.is-fan .fd-trunk:after{display:none}.fd-junction.is-fan .fd-stems{top:26px}.fd-junction.is-fan .fd-stems>span:after{content:"";border-top:7px solid var(--border-strong);border-left:4px solid #0000;border-right:4px solid #0000;position:absolute;bottom:0;left:-4px}.fd-root{width:-moz-fit-content;width:fit-content;max-width:100%;padding:var(--space-2) var(--space-4);border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--bg);font-size:var(--text-sm);color:var(--text);overflow-wrap:anywhere;text-align:center;margin:0 auto;display:block}.fd-branches{margin:0 calc(var(--fd-pad) * -1);padding:0;list-style:none;display:grid}.fd-branches>.fd-node{margin:0 var(--fd-pad)}.fd-ba-label{margin:0 0 var(--space-1);font-size:var(--text-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--text-tertiary)}.fd-ba-head{margin:0 0 var(--space-3);font-size:var(--text-md)}.fd-ba-side.is-was .fd-ba-head{color:var(--text-tertiary)}.fd-ba-side.is-was .fd-node{color:var(--text-tertiary);border-style:dashed}.fd-ba-why{gap:var(--space-3);margin:var(--space-5) 0;padding-left:var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);align-items:center;display:flex}.fd-ba-pivot{background:linear-gradient(var(--warn), var(--warn)) 4px 0 / 1px 20px no-repeat;flex-shrink:0;width:9px;height:26px;position:relative}.fd-ba-pivot:after{content:"";border-top:7px solid var(--warn);border-left:4px solid #0000;border-right:4px solid #0000;position:absolute;bottom:0;left:0}.fd-legend{gap:var(--space-2) var(--space-5);font-size:var(--text-xs);color:var(--text-tertiary);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.fd-legend li{align-items:center;gap:var(--space-2);display:flex}.fd-key{flex-shrink:0;width:22px;height:10px;position:relative}.fd-key-junction{background:linear-gradient(var(--border-strong), var(--border-strong)) 3px 0 / 1px 5px no-repeat, linear-gradient(var(--border-strong), var(--border-strong)) 18px 0 / 1px 5px no-repeat, linear-gradient(var(--border-strong), var(--border-strong)) 3px 5px / 16px 1px no-repeat, linear-gradient(var(--border-strong), var(--border-strong)) 11px 5px / 1px 5px no-repeat}.fd-key-outcome{background:linear-gradient(var(--danger), var(--danger)) 0 4px / 8px 2px no-repeat, linear-gradient(var(--ok), var(--ok)) 14px 4px / 8px 2px no-repeat}@container page (width<=860px){.fd-chain{gap:var(--space-5);flex-direction:column}.fd-chain>.fd-node+.fd-node:before{top:calc(var(--space-5) * -1 - 1px);left:var(--space-5);width:1px;height:var(--space-5)}.fd-chain>.fd-node+.fd-node:after{top:-8px;left:calc(var(--space-5) - 3px);border-left:4px solid #0000;border-right:4px solid #0000;border-top:7px solid var(--border-strong);border-bottom:0;margin-top:0}.fd-lanes,.fd-branches{gap:var(--space-4);margin:0;grid-template-columns:minmax(0,1fr)!important}.fd-lane,.fd-branches>.fd-node{margin:0}.fd-tree.has-root .fd-root{text-align:left;width:100%;margin:0}.fd-tree.has-root .fd-junction{display:none}.fd-tree.has-root .fd-branches{margin:var(--space-4) 0 0 var(--space-6)}.fd-tree.has-root .fd-branches>.fd-node:before{content:"";top:calc(var(--space-4) * -1);bottom:50%;left:calc(var(--space-6) * -1);background:var(--border-strong);width:1px;position:absolute}.fd-tree.has-root .fd-branches>.fd-node:after{content:"";top:50%;left:calc(var(--space-6) * -1);width:var(--space-6);background:var(--border-strong);height:1px;position:absolute}.fd-junction{height:30px}.fd-stems,.fd-bus{display:none}.fd-junction .fd-trunk,.fd-junction.is-fan .fd-trunk{height:auto;top:0;bottom:0}.fd-junction.is-fan .fd-trunk:after{display:block}.rp-cols{grid-template-columns:minmax(0,1fr)}}.panel-scrim{z-index:var(--z-panel);padding:var(--space-5);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.panel-backdrop{cursor:default;background:#00000052;border:0;position:absolute;inset:0}.panel-card{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg);width:min(880px,100%);max-height:min(80vh,720px);box-shadow:var(--shadow-float);-webkit-backdrop-filter:var(--float-blur);backdrop-filter:var(--float-blur);flex-direction:column;display:flex;position:relative}.panel-head{justify-content:space-between;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--border);display:flex}.panel-title{font-size:var(--text-sm);letter-spacing:.04em;text-transform:uppercase;color:var(--text-secondary);margin:0;font-weight:600}.panel-close{width:var(--control-h);height:var(--control-h);border-radius:var(--control-radius);color:var(--text-secondary);cursor:pointer;background:0 0;border:0;place-items:center;display:grid}.panel-close:hover{background:var(--bg-muted);color:var(--text)}.panel-body{padding:var(--space-5);overflow:auto}.panel-split{grid-template-columns:168px minmax(0,1fr);min-height:0;display:grid}.panel-rail{padding:var(--space-3);border-right:1px solid var(--border);flex-direction:column;gap:2px;display:flex;overflow:auto}.panel-rail-item{justify-content:space-between;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--control-radius);color:var(--text-secondary);font:inherit;font-size:var(--text-sm);text-align:left;cursor:pointer;background:0 0;border:0;display:flex}.panel-rail-item:hover{background:var(--bg-muted);color:var(--text)}.panel-rail-item.is-on{background:var(--bg-muted);color:var(--text);font-weight:600}.panel-rail-count{color:var(--text-tertiary);font-size:var(--text-xs)}.panel-body>.viz>.viz-title,.panel-body>.report .viz-title{display:none}.panel-body>.viz{margin:0}@media (max-width:720px){.panel-split{grid-template-columns:minmax(0,1fr)}.panel-rail{border-right:0;border-bottom:1px solid var(--border);flex-direction:row}}.rp-cols.is-single{display:block}.rp-cols.is-single>.rp-block>.rp-title{display:none}.tag.is-open{cursor:pointer;font:inherit;text-align:left}.tag.is-open:hover{border-color:var(--border-strong)}.cov-lead{margin:0 0 var(--space-2)}.cov-held{margin:0 0 var(--space-2);color:var(--danger)}.cov-scope{margin:0 0 var(--space-3);color:var(--text-tertiary)}.cov-grid{margin:0 0 var(--space-4);flex-wrap:wrap;gap:5px;padding:0;list-style:none;display:flex}.cov-cell{border-radius:var(--radius-xs);background:var(--ok);border:1px solid #0000;width:22px;height:22px;padding:0;display:block}button.cov-cell{cursor:pointer}button.cov-cell:hover{border-color:var(--text)}.cov-cell.is-stale{border-color:var(--warn);background:var(--warn-soft)}.cov-key{gap:var(--space-2) var(--space-4);color:var(--text-secondary);font-size:var(--text-sm);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}.cov-key li{align-items:center;gap:var(--space-2);display:flex}.cov-key .cov-cell{width:14px;height:14px}.cov-key-n{color:var(--text-tertiary)}.cov-more{margin:0 0 var(--space-3);color:var(--text-tertiary);font-size:var(--text-sm)}.checks-rows{align-items:baseline;gap:var(--space-2) var(--space-4);margin:0 0 var(--space-4);grid-template-columns:max-content max-content minmax(0,1fr) max-content max-content;padding:0;list-style:none;display:grid}.checks-row{display:contents}.checks-method{font-weight:600}.checks-now{align-items:baseline;gap:var(--space-1);white-space:nowrap;display:inline-flex}.checks-now.v-pass{color:var(--ok)}.checks-now.v-fail{color:var(--danger)}.checks-counts,.checks-runs,.checks-when{color:var(--text-tertiary);font-size:var(--text-sm);white-space:nowrap}.checks-strip{flex-wrap:wrap;gap:4px;display:flex}.checks-tick{border-radius:var(--radius-xs);background:var(--gray-4);cursor:pointer;border:1px solid #0000;width:14px;height:14px;padding:0}.checks-tick.v-pass{background:var(--ok)}.checks-tick.v-fail{background:var(--danger)}.checks-tick.is-derived{background:0 0}.checks-tick.is-derived.v-pass{border-color:var(--ok)}.checks-tick.is-derived.v-fail{border-color:var(--danger)}.checks-tick.is-derived:not(.v-pass):not(.v-fail){border-color:var(--border-strong)}.checks-tick:hover{outline:2px solid var(--text);outline-offset:1px}.checks-key{color:var(--text-tertiary);font-size:var(--text-xs);margin:0}.flow-ask-caret{width:1em;color:var(--text-tertiary);text-align:center;transition:transform var(--motion-fast) var(--ease);flex-shrink:0}.flow-turn.is-open .flow-ask-caret{transform:rotate(90deg)}.flow-ask-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.flow-sum{gap:var(--space-1);flex-wrap:wrap;flex-shrink:0;justify-content:flex-end;margin-left:auto;display:flex}.flow-chip{padding:1px var(--space-2);border-radius:var(--radius-xs);background:var(--bg-muted);color:var(--text-tertiary);font-size:var(--text-xs);white-space:nowrap}.rewind{align-items:center;gap:var(--space-2);width:100%;max-width:var(--page-w);pointer-events:auto;display:flex}.rewind:not(.is-open){position:absolute}.rewind .timeline{min-width:0;max-width:none;padding:var(--space-3) var(--space-4);border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg);box-shadow:var(--shadow-float);-webkit-backdrop-filter:var(--float-blur);backdrop-filter:var(--float-blur);flex:1;margin:0}.loading{align-items:center;gap:var(--space-2);display:inline-flex}.orb{width:1em;height:1em;color:inherit;flex-shrink:0}.orb-dot{transform-origin:50%;animation:1.4s ease-in-out infinite orb-pulse}.orb-dot:nth-child(2){animation-delay:.18s}.orb-dot:nth-child(3){animation-delay:.36s}@keyframes orb-pulse{0%,to{opacity:.25;transform:scale(.7)}40%{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion:reduce){.orb-dot{animation:none}.orb-dot:first-child{opacity:1}.orb-dot:nth-child(2){opacity:.6}.orb-dot:nth-child(3){opacity:.3}}