diff --git a/DESIGN.md b/DESIGN.md
index 65c03aa0..7b040c13 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -4,6 +4,24 @@ Version: 0.2
Status: active working design contract
Last updated: 2026-07-23
+> **Colour and type tokens are superseded** by
+> `atrib-web/designs/fable5-authored-territories-20260721/DESIGN-SYSTEM.md`,
+> read alongside `ANTI-REFERENCES.md` beside it. Protocol language, explorer IA
+> and the asset contract are unaffected.
+>
+> The explorer keeps its dark instrument mode. Its canvas moves from neutral
+> `#0a0a0a` to the water column's own `#050914`: amber measures 9.07 there
+> against 9.02 today, so the hue costs nothing in contrast. The rest of the
+> chrome this file pins in the asset contract moves with it, at the same
+> contrast steps and hue 220 to 230: surface `#0a1027`, raised `#101929`, border
+> `#212946`, muted `#7e879a`. `foreground` `#f5f4ee` and `accent` `#e8a04f` are
+> unchanged, and cobalt `#7d9bd6` joins as the primary at depth.
+>
+> `Inter` is replaced by Literata for headings and Libre Franklin for reading.
+> IoskeleyMono is unchanged. All three are SIL OFL and served from the site's own
+> origin, so nothing in the brand depends on a third party staying up.
+> Superseded 2026-07-28.
+
## Purpose
atrib makes agent activity verifiable. Productized atrib is a verifiable action layer for agents: control what runs, coordinate what carries forward, and prove what happened. The design system should make that idea feel concrete: not a vague AI platform, not a crypto dashboard, not an observability clone. The interface should feel like signed evidence entering a chain and becoming usable by the next agent, team, or verifier.
diff --git a/apps/dashboard/index.html b/apps/dashboard/index.html
index 1a9f5f35..a4a6a7c5 100644
--- a/apps/dashboard/index.html
+++ b/apps/dashboard/index.html
@@ -18,8 +18,8 @@
-
-
+
+
@@ -48,25 +48,35 @@
*/
:root {
- /* Product token aliases, kept in sync with the public website. */
- --color-background: #0a0a0a;
- --color-surface: #131210;
- --color-surface-raised: #1c1a17;
+ /* Product token aliases, kept in sync with the public website. The ground
+ is the water column's own floor rather than a neutral near-black, so the
+ instrument sits on the same hue axis as the painting the site opens with.
+ Amber measures 9.07 here against 9.02 on the old #0a0a0a: the hue costs
+ nothing in contrast. Every neutral below solves
+ contrast(x, floor) == contrast(old, #0a0a0a) at hue 220 to 232, so every
+ step of the previous palette survives to two decimals.
+ Canonical: atrib-web/designs/fable5-authored-territories-20260721/DESIGN-SYSTEM.md */
+ --color-background: #050914;
+ --color-surface: #0a1027;
+ --color-surface-raised: #101929;
--color-foreground: #f5f4ee;
- --color-muted: #8a877e;
- --color-muted-foreground: #b8b5ac;
- --color-border: #2d2a25;
- --color-border-strong: #423e36;
+ --color-muted: #7e879a;
+ --color-muted-foreground: #afb4c9;
+ --color-border: #212946;
+ --color-border-strong: #343d5b;
--color-accent: #e8a04f;
--color-accent-strong: #f0b46c;
+ /* Cobalt is the brand. The explorer had no primary at all, which is why
+ amber was carrying headings as well as evidence. */
+ --color-primary: #7d9bd6;
/* Surface */
--bg: var(--color-background);
--surface: var(--color-surface);
--surface-2: var(--color-surface-raised);
- --surface-hover: #24211d;
+ --surface-hover: #17203a;
--border: var(--color-border);
- --border-subtle: #211f1b;
+ --border-subtle: #191d36;
/* Text */
--text: var(--color-foreground);
@@ -80,21 +90,31 @@
--accent-subtle: rgba(232, 160, 79, 0.12);
--accent-border: rgba(232, 160, 79, 0.3);
- /* Status */
- --ok: #34d399;
- --ok-subtle: rgba(52, 211, 153, 0.12);
- --ok-border: rgba(52, 211, 153, 0.3);
+ /* Status. These were Tailwind's default 400 ramp, a palette belonging to
+ no part of this product. Four of them now come from the design system's
+ deep ramp, at the same contrast they had before.
+
+ --warn is the exception and is deliberately unchanged. The system's
+ "needs review" colour is #965800, hue 35, and the evidence mark is
+ #e8a04f, hue 32. On paper they are told apart by lightness, 5.3 against
+ 2.0. Lifted to this ground they land two degrees apart and become the
+ same colour, which would break the rule that amber marks signed things
+ and nothing else. That is a design decision, not a mapping, so it is
+ left open rather than guessed at. See DESIGN-SYSTEM.md. */
+ --ok: #99e0c3;
+ --ok-subtle: rgba(153, 224, 195, 0.12);
+ --ok-border: rgba(153, 224, 195, 0.3);
--warn: #fbbf24;
--warn-subtle: rgba(251, 191, 36, 0.12);
--warn-border: rgba(251, 191, 36, 0.3);
- --err: #f87171;
- --err-subtle: rgba(248, 113, 113, 0.12);
- --err-border: rgba(248, 113, 113, 0.3);
- --info: #60a5fa;
- --info-subtle: rgba(96, 165, 250, 0.12);
- --info-border: rgba(96, 165, 250, 0.3);
- --muted: #9ca3af;
- --muted-subtle: rgba(156, 163, 175, 0.1);
+ --err: #ffb4a3;
+ --err-subtle: rgba(255, 180, 163, 0.12);
+ --err-border: rgba(255, 180, 163, 0.3);
+ --info: #7d9bd6;
+ --info-subtle: rgba(125, 155, 214, 0.12);
+ --info-border: rgba(125, 155, 214, 0.3);
+ --muted: #7e879a;
+ --muted-subtle: rgba(126, 135, 154, 0.1);
/* Per event_type chip colors */
--et-tool-call: var(--info);
@@ -108,7 +128,7 @@
--et-observation-border: rgba(192, 132, 252, 0.3);
--et-extension: var(--muted);
--et-extension-subtle: var(--muted-subtle);
- --et-extension-border: rgba(156, 163, 175, 0.25);
+ --et-extension-border: rgba(126, 135, 154, 0.25);
/* atrib-system event types (e.g. directory_anchor, byte 0x04 per D056).
Pink sits well outside both the agent-action triad (blue tool_call,
amber transaction, purple observation) and the verification-badge
@@ -124,10 +144,10 @@
pretending the upstream is recoverable here. Muted-warning style
(warm grey) so the dangling marker reads as "incomplete" without
competing with the err-red used for verification failures. */
- --et-dangling: #92400e;
- --et-dangling-subtle: rgba(146, 64, 14, 0.12);
- --et-dangling-border: rgba(146, 64, 14, 0.3);
- --ref-external: #9ca3af;
+ --et-dangling: #bd7d57;
+ --et-dangling-subtle: rgba(189, 125, 87, 0.12);
+ --et-dangling-border: rgba(189, 125, 87, 0.3);
+ --ref-external: #7e879a;
--ref-missing: var(--err);
--ref-unresolved: var(--warn);
/* Annotation event_type (D058, byte 0x05): the recall-fidelity
@@ -148,9 +168,14 @@
semantics are different: annotation comments, revision supersedes.
Indigo signals "structural change of stance" and reads as deeper /
weightier than annotation's commentary tone. */
- --et-revision: #6366f1;
- --et-revision-subtle: rgba(99, 102, 241, 0.12);
- --et-revision-border: rgba(99, 102, 241, 0.3);
+ /* Lifted from #6366f1 and #92400e, which read 3.76 and 2.50 against their
+ own 12% chip tint and so were unreadable at 11px. Both keep their hue to
+ the degree; only lightness moved, to the dimmest value that clears 4.5
+ with headroom. The categorical scheme itself is unchanged: these hues
+ encode protocol event types per D056 and D058, not brand. */
+ --et-revision: #7e81f7;
+ --et-revision-subtle: rgba(126, 129, 247, 0.12);
+ --et-revision-border: rgba(126, 129, 247, 0.3);
/* Edge-type colors (graph viz), organized in two semantic tiers.
Tier 1 (vibrant): the edges that carry meaning beyond
@@ -281,7 +306,7 @@
gap: 16px;
padding: 14px max(20px, calc((100vw - var(--container)) / 2 + 20px));
border-bottom: 1px solid var(--border);
- background: rgba(10, 11, 14, 0.85);
+ background: rgba(5, 9, 20, 0.85);
backdrop-filter: saturate(180%) blur(12px);
-webkit-backdrop-filter: saturate(180%) blur(12px);
position: sticky; top: 0; z-index: 50;
@@ -303,21 +328,38 @@
padding-left: 10px; border-left: 1px solid var(--border);
margin-left: 2px;
}
- body > header nav {
+ body > header nav, nav.local {
display: flex; gap: 4px; align-items: center;
min-width: 0;
overflow-x: auto;
scrollbar-width: none;
}
- body > header nav::-webkit-scrollbar { display: none; }
- body > header nav a {
+ body > header nav::-webkit-scrollbar, nav.local::-webkit-scrollbar { display: none; }
+ /* The estate ladder is quiet and monospaced, matching every other surface.
+ You cannot navigate to where you already are, so the current surface is a
+ label that reads brighter rather than a link. */
+ body > header nav.ladder a, body > header nav.ladder span {
+ font: 400 12px/1 var(--font-mono); padding: 8px 10px;
+ border-radius: var(--radius-sm); color: var(--text-3); flex-shrink: 0;
+ }
+ body > header nav.ladder a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
+ body > header nav.ladder [aria-current] { color: var(--text); }
+ nav.local {
+ position: sticky; top: 53px; z-index: 49;
+ padding: 8px max(20px, calc((100vw - var(--container)) / 2 + 20px));
+ border-bottom: 1px solid var(--border);
+ background: rgba(5, 9, 20, 0.85);
+ backdrop-filter: saturate(180%) blur(12px);
+ -webkit-backdrop-filter: saturate(180%) blur(12px);
+ }
+ body > header nav a, nav.local a {
color: var(--text-2); font: 500 13px/1 var(--font-sans);
padding: 8px 12px; border-radius: var(--radius-sm);
flex-shrink: 0;
transition: background 120ms ease, color 120ms ease;
}
- body > header nav a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
- body > header nav a.active { color: var(--accent); background: var(--accent-subtle); }
+ body > header nav a:hover, nav.local a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
+ body > header nav a.active, nav.local a.active { color: var(--accent); background: var(--accent-subtle); }
@media (max-width: 520px) {
body > header {
gap: 10px;
@@ -848,12 +890,20 @@
table.feed tbody td .mono {
font: 12.5px/1 var(--font-mono); color: var(--text-2);
}
- /* New-row highlight: a sustained amber wash (not a flicker). The cell-level
- * animation matches the row-level so striped table backgrounds don't
- * override on tr. Holds full color for ~3.5s, fades over ~3s. Total
- * ~6.5s, long enough to actually catch the eye scanning the table. */
- table.feed tbody tr.new,
- table.feed tbody tr.new td {
+ /* New-row highlight: a sustained cobalt rail, held ~3.5s then faded over ~3s,
+ * long enough to catch the eye scanning the table.
+ *
+ * It was a wash, and a wash cannot work here. Every chip in this table is its
+ * own colour on a 12% tint of that same colour, which leaves the dimmest of
+ * them at 4.56 against the surface: one notch above the floor. Tinting the
+ * row underneath pushes it below, and the old rule applied its tint twice, on
+ * the row and again on the cell, so during the flash all six chip colours
+ * fell under 4.5 and the worst reached 2.00. The doubling was deliberate, to
+ * stop the striped row background overriding it, which is a real problem that
+ * a rail does not have.
+ *
+ * A rail costs nothing: it sits in the gutter rather than behind the text. */
+ table.feed tbody tr.new td:first-child {
animation: rowEnter 6500ms ease-out;
}
.evidence-authority {
@@ -900,14 +950,13 @@
}
}
@keyframes rowEnter {
- 0% { background-color: rgba(252, 211, 77, 0.18); }
- 50% { background-color: rgba(252, 211, 77, 0.18); }
- 100% { background-color: transparent; }
+ 0% { box-shadow: inset 3px 0 0 var(--color-primary); }
+ 50% { box-shadow: inset 3px 0 0 var(--color-primary); }
+ 100% { box-shadow: inset 3px 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
.hero .eyebrow .pulse, .feed-head .live-tag .dot { animation: none; }
- table.feed tbody tr.new,
- table.feed tbody tr.new td { animation: none; }
+ table.feed tbody tr.new td:first-child { animation: none; }
}
/* ─── Chips & badges ─────────────────────────────────────────────────── */
@@ -948,7 +997,7 @@
.badge.warn { background: var(--warn-subtle); color: var(--warn); border-color: var(--warn-border); }
.badge.err { background: var(--err-subtle); color: var(--err); border-color: var(--err-border); }
.badge.info { background: var(--info-subtle); color: var(--info); border-color: var(--info-border); }
- .badge.dim { background: var(--muted-subtle); color: var(--muted); border-color: rgba(156, 163, 175, 0.2); }
+ .badge.dim { background: var(--muted-subtle); color: var(--muted); border-color: rgba(126, 135, 154, 0.2); }
.verify-badges { display: inline-flex; gap: 4px; flex-wrap: wrap; }
@@ -1053,7 +1102,7 @@
}
.proof-step.dim .mark {
background: var(--muted-subtle);
- border-color: rgba(156, 163, 175, 0.2);
+ border-color: rgba(126, 135, 154, 0.2);
color: var(--muted);
}
.proof-step strong {
@@ -1105,6 +1154,29 @@
display: grid; grid-template-columns: 1fr 1fr; gap: 32px;
color: var(--text-3); font-size: 13px;
}
+ /* The surface map, generated from atrib-web/lib/surfaces.json by
+ scripts/render-nav.mjs. Every atrib surface, grouped by what the reader
+ would be doing when they want it. The working proof used to be reachable
+ from nowhere at all. */
+ footer .surface-map {
+ grid-column: 1 / -1;
+ border-top: 1px solid var(--border-subtle); padding-top: 28px;
+ display: grid; gap: 28px 24px;
+ grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
+ }
+ footer .surface-map h2 {
+ margin: 0 0 12px; font: 400 11px/1 var(--font-mono);
+ text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-3);
+ }
+ footer .surface-map ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 14px; }
+ footer .surface-map a, footer .surface-map [aria-current] {
+ font: 400 12px/1 var(--font-mono); color: var(--accent-strong);
+ }
+ footer .surface-map [aria-current] { color: var(--text); }
+ footer .surface-map p {
+ margin: 5px 0 0; max-width: 28ch; font-size: 12px; line-height: 1.5;
+ color: var(--text-3);
+ }
footer h3 {
margin: 0 0 12px; font: 500 11px/1 var(--font-sans);
text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-2);
@@ -1761,19 +1833,30 @@
atrib keeps goals, decisions, artifacts and handoffs coherent across the agents and tools you already use.
atrib protocol: the open substrate for portable verifiable records.
The public log. Every signed action, checkable by anyone.
A recorded session, replayed against the real log.
How the log is anchored, and how to verify an anchor.
The story version: one goal, followed end to end.