Governing design document for the web/ Next.js dashboard at proofgate-nu.vercel.app.
This is a REFINEMENT of an existing product, not a new design system. Existing tokens,
theme, typography, and the green/red semantic language are preserved. What changes is
macrostructure, state design, and hierarchy. Source precedence per 01-ui-ux-design.md:
Lee's brief, then existing rendered product evidence, then catalog entries.
Evidence inspected before writing: live route at 1440px (populated), live route mobile
capture (unreliable, headless crop), rendered direction preview at 1440 / 768 / 375
(preview.html, direction mock, populated states only), and the current source
(web/app/page.tsx, web/app/components/*, web/app/globals.css).
Supplied media: ProofGate_ The Protocol Room ... 2026-08-31.mp4 recorded by Lee,
status: reference, unverified (frames not extracted, no ffmpeg on this machine).
Not used as design evidence.
Empty, loading, and error states do not exist in the current build
(page.tsx swallows the /api/state failure with an empty catch; HistorySection
renders an empty list). This document specifies them; the build must add them.
- Surface mode: Operate. A live demo console that runs real on-chain actions and shows real evidence. Not a landing page. No marketing hero.
- Design Read: Reading this as: a live protocol demo console for hackathon judges and crypto builders, in a dark control-room language with a two-tone proof semantics (green means proven, red means caught), leaning toward the existing Protocol Room system refined into a task-led two-rail console.
- Mood: quiet, instrument-like, dense but calm. One loud moment only: the caught-cheat alarm panel.
- Product type match (01 catalog): Fintech/Crypto for the decision rules (security-first, dark because it is a protocol console used in low-light demo settings). The catalog palette for that entry (gold plus purple) is NOT used: existing product evidence outranks it, and the existing green/red semantics carry the product's whole argument. The existing language is closest in character to the Developer Tool / IDE entry's "code dark plus run green" reasoning.
- Style (01 catalog, for the record): Dark Mode (OLED), midnight-blue variant, with Data-Dense discipline for the evidence lists. Existing product evidence is binding.
- Anti-reference compliance: the current page partially matches Lee's rejected cluster (dark shell plus repeated identical rounded panels plus muted mono text plus generic top bar). This refinement breaks that cluster: panels are used only for the three interactive task blocks, evidence lives in borderless hairline lists, the masthead carries real information (network, repo), and secondary text is raised to a measured contrast.
Existing tokens, unchanged hex, now with explicit roles. No new colors.
| Token | Hex | Role |
|---|---|---|
| room-bg | #0A1128 | page background, input background |
| room-panel | #14213D | surface for the three interactive task panels only |
| room-border | #1F2B4D | hairline rules, panel borders, link underlines |
| room-green | #2EE6A8 | proven / true / released / success. Never decorative |
| room-red | #FF4D6D | caught / rejected / revert. Never decorative |
| room-text | #EDF2F7 | primary text |
| room-mono | #8DA2C4 | secondary text and data labels. Measured 7.2:1 on room-bg |
Rules: green and red are semantic only, never accents for decoration. One accent per screen: green on the task rail, red only inside the alarm panel. No gradients. No glow except the single alarm panel (the signature). Palette is 2 core neutrals plus 2 semantic colors plus 1 muted, within the R-29 budget.
Existing families, unchanged. Catalog nearest pairing: Modern Dark Cinema (Inter System). Existing product evidence is binding.
- Display and body: Inter (next/font, subsets latin, variable).
- Data, hashes, addresses, labels, numerals: JetBrains Mono (next/font, subsets latin). Mono is for data and measurement only, never for headings or prose (R-06).
Production load line (web/app/layout.tsx, already present, keep):
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const jetbrains = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains" });
Never a Google Fonts <link> in production.
| Role | Family | Size desktop / mobile | Weight | Tracking |
|---|---|---|---|---|
| page title h1 | Inter | 30 / 25 | 700 | -0.02em |
| section title h2 | Inter | 17 / 16 | 600 | -0.01em |
| aside title h3 | Inter | 13 | 600 | 0 |
| body / lede | Inter | 14 / 16 | 400 | 0 |
| panel copy | Inter | 13.5 / 16 | 400 | 0 |
| data label | JetBrains Mono | 11.5 | 400 | 0 |
| data value | JetBrains Mono | 12.5 | 400 | 0, tabular-nums |
| status value | JetBrains Mono | 22 / 20 | 500 | 0, tabular-nums |
| released figure | JetBrains Mono | 34 / 28 | 500 | 0, tabular-nums |
Mobile rule: body and panel copy rise to 16px below 768px. All numerals in data
contexts use font-variant-numeric: tabular-nums. Headings use text-wrap: balance.
No eyebrow or kicker above any heading (absolute ban). No section numbers 01/02/03:
the task rail order is expressed by position and by the proving/thinking/executing
stepper, not by decorative numerals.
Only the components this surface needs.
- Masthead: one row, 64px desktop, wraps to two rows below 768px. Left: logo SVG plus wordmark. Right: plain mono text "Creditcoin CC3 testnet" and the repo link with a hairline underline. No capsule badges, no glow.
- Status strip: three real protocol facts (policies committed, precompile
verifications with proven/rejected split, PGC released to borrower) read from
/api/stateand deployments constants. Mono values, tabular-nums, sentence-case mono labels, hairline vertical separators desktop, stacked with hairline top borders mobile. States: loading shows three mono placeholder dashes; error shows "chain state unavailable" plus a Retry button; never blank. - Task panel (used exactly three times: Commit a policy, Run a decision, Try to
cheat): room-panel background, 1px room-border, radius 12px, padding 18px. States:
- default, hover n/a (container), focus-within border shifts to green 60 percent.
- loading: the action button disables and shows its verb plus an ellipsis; the result region reserves space with a hairline skeleton block, not a spinner.
- error: red text under the control naming cause and fix, plus Retry where the action is retryable.
- success: result region reveals with the single room-in fade.
- Button: outlined voice. Primary green outline, green label, hover fills green 10 percent. Danger red outline, red label, hover fills red 10 percent. Radius 8px. Disabled: opacity 0.4, cursor not-allowed. Busy: disabled plus aria-busy plus label ending in an ellipsis. Focus-visible: 2px green ring, offset 2px. Minimum height 44px on mobile.
- Text input and textarea: room-bg fill, 1px room-border, radius 8px, label above in mono 11.5px, focus border green 60 percent, never outline-none without replacement. Read-only facts (policy id, borrower) render as wrapping mono chips, not truncated inputs, below 768px.
- Key-value rows: mono label column 190px desktop, full width mobile; mono value with
overflow-wrap: anywhereandmin-width: 0so hashes never widen the layout. - Stepper (proving / thinking / executing): mono 11.5px, wraps, completed steps green with a check SVG, current step room-text, pending muted, separator is a slash. Announced to assistive tech with a single polite live region.
- Evidence list (proven history, on-chain record): borderless, hairline row dividers, authored one-stroke SVG check in green, mono amount and block, hairline-underlined explorer link. Empty state: "No proven repayments loaded yet. Run a decision to produce one." Error state: cause plus Retry. Never a blank panel.
- Claimed vs proven block: three hairline rows (AI claimed, proofs supported, released). Claimed value muted, proven and released values green tabular. One line of plain explanation underneath, sourced to a real recorded release.
- Alarm panel (signature): 1px room-red border, red 8 percent fill, radius 12px, pulsing red box-shadow loop 2.4s. Contains: one-stroke SVG cross, uppercase red title, status code in muted mono, plain-language cause, the on-chain revert string in a nested room-bg box, and the reverted tx link. Under prefers-reduced-motion the pulse collapses to a static border and fill.
- Icons: authored inline SVG only, one stroke weight, 15 to 16px, aria-hidden when beside visible text. No unicode glyphs, no emoji.
- Container: max-width 1160px, 20px gutters, 16px gutters below 768px.
- Page order: masthead, page head (title, one-sentence sub, status strip), then the two-rail grid, then a one-line footer.
- Two-rail grid at 1024px and up:
grid-template-columns: minmax(0,1.55fr) minmax(0,1fr), gap 28px,align-items: start. Left rail is the task in execution order: commit a policy, run a decision, try to cheat. Right rail is evidence: proven history, claimed vs proven, on-chain record. Below 1024px the grid collapses to one column in the same order, declared in the same component. - The policy coupling is explicit: the Run a decision panel shows which policy it will
run. If a policy was committed in this session it reads "policy #N, committed in
this session". Otherwise it reads "latest committed policy #N" sourced from
/api/state. Compiling or committing in the first panel updates that chip. A judge can never run a policy they were not shown. - Vertical rhythm: 28px between rail blocks, 14px between a section title and its lede, 18px panel padding, hairline separators inside panels at 14 to 16px offsets. More space above a heading than below it.
- Cards are not the default unit. Only the three interactive task blocks are panels. Evidence is lists and rows. No nested cards except the revert box inside the alarm, which is a deliberate inset.
- Elevation is declared once: borders, not shadows. Panels and inputs use 1px room-border. No drop shadows anywhere.
- The single exception is the alarm panel's pulsing red halo, which is a state signal (the protocol caught something), not elevation.
- Surface hierarchy: room-bg page, room-panel task panels, room-bg insets inside panels and inside the alarm.
Do:
- Keep green for proven and red for caught, everywhere, forever.
- Show the claimed-versus-proven contrast above the fold on desktop.
- Use real hashes, real addresses, real counts from deployments and
/api/state. - Give every data-bearing block an empty, loading, and error state with a next action.
- Wrap long hashes with
overflow-wrap: anywhereinside amin-width: 0child. - Respect prefers-reduced-motion: the alarm pulse and the room-in fade collapse.
Don't (catalog anti-patterns plus craft floor, verbatim intent):
- No marketing hero, no eyebrow or kicker above a heading, no section numbers.
- No identical repeated card grid as the page structure; no nested cards.
- No gradient text, no glass or blur, no colored border-left stripes, no glow on more than the one alarm panel.
- No mono as costume: mono is for data, hashes, labels, numerals only.
- No emoji or unicode glyphs as icons.
- No invented metrics, testimonials, or counts. Every number on the page has a source
in deployments.md, the README evidence table, or
/api/state. - No purple, no gold, no new accent. No light theme and no theme toggle.
- No clickable divs; buttons for actions, anchors for navigation.
- No
transition: all, no outline-none without a focus-visible replacement.
- Breakpoints: 375, 768, 1024, 1440. Verified at all four before delivery.
- 1024 and up: two rails as specified.
- Below 1024: single column, task rail first, evidence rail after, same order.
- Below 768: masthead wraps to two rows; status strip stacks with hairline top borders; key-value label column goes full width; read-only facts become wrapping chips; body and panel copy rise to 16px; button row stacks full width; released figure drops to 28px; touch targets at least 44px.
- No horizontal scroll at any width. Long hashes wrap, never widen.
color-scheme: darkon html and a matchingtheme-colormeta.
- Motion tier: Subtle. MOTION dial 1 to 2. VARIANCE 5. DENSITY 7. ENERGY 2, RHYTHM 2, MOTION 1 (antislop dials).
- Allowed animations, each with one reason:
- room-in fade and 8px rise on a result region when it first appears: feedback that an async on-chain step completed.
- alarm-glow pulse on the caught-cheat panel only: the page's single alarm state.
- button background-color transition 180ms: hover feedback.
- Everything else is static. No scroll reveals, no stagger, no parallax, no marquee.
- Signature element: the alarm panel. It is the one bold thing and it is subject grounded (a caught cheat is an alarm in a dark room). Magic UI and 21st.dev additions: None. Reason: this is an Operate surface whose identity already lives in the green/red proof semantics and the alarm; landing-page spectacle would compete with the task and with the low motion dial.
- prefers-reduced-motion: the pulse and the fade collapse to their final static state.
- Kill the marketing hero and the eyebrow; title becomes a compact page head with the status strip directly under it.
- Replace the uniform stacked single column with the two-rail grid above.
- Make the policy-to-decision coupling visible in the Run a decision panel.
- Demote Proven History from a numbered fake step to a labelled evidence list in the evidence rail.
- Promote claimed-versus-proven into a persistent evidence block, and fix number
formatting (tabular, no trailing
.0on the balance). - Add real empty, loading, and error states to every data-bearing block.
- Replace unicode check and cross glyphs with authored one-stroke SVGs.
- Tighten vertical rhythm from gap-16 to the 28px rail rhythm.