Repository navigation
Apply the execution-desk design system across Mail, RFQ and the app shell - #1
Merged
Merged
Conversation
The reading pane repeated what the rest of the screen already said. Every letter carried a boxed "recipient identities are sealed and absent from MessagePosted" note between its header and its body, so a letter opened with boilerplate instead of prose — while "What the chain sees" states the same absence directly under the public record, and the message meta row already carries the recipient count. The letter sheet's "N recipients · count public" stamp said the count a third time. Both are gone; nothing is lost, because the chain panel and the meta row keep the facts where they are decidable. The per-message reply row and the rail's standing note are cut to one line each for the same reason. The thread head was the constant "Correspondence" under a kicker that duplicated the reading toolbar's, so the pane never said which record was open. It now names the counterparty and its detail, reusing the rail's own conversationCorrespondent so neither pane can call the same counterparty something different; the helper moves to correspondent.ts, which both panes import. The section keeps a stable "Correspondence" accessible name, and the localnet spec addresses the focused heading by id rather than by prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Compose on a disconnected desk set an error notice and called setSidebarOpen(true) — a no-op on desktop — so the screen's primary action failed and pointed at a control in the app header, the weakest affordance on the page. The welcome CTA now offers the step that is actually available: SelectWallet, restyled to the desk's square orange control instead of the shared rounded CTA. The compose gate carries the same control inside its notice, so a Compose click from the sidebar still resolves where the user is looking. Only one of the two asks at a time: while the gate notice is up, the welcome sheet drops its own connect block and its copy, which would otherwise repeat the notice sentence 60px below it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The design system assigns orange to what you are doing, teal to public on-chain evidence, mauve to negotiated terms. The mail module had drifted on all three, so none of them carried information any more: - mauve had become a second severity colour (Forget this device, restore warnings, wallet errors, preflight errors, Remove attachment, Delete draft) and a generic caption colour (every rail row's provenance line). Severity is --shell-danger; the row captions are plain secondary text. Mauve is now left only on deal surfaces: the deal sheet and fields, the terms sentence, the claimed payment address, the deal-kind toggle. - teal headed the rail's "local browser index" note and stamped the welcome sheet "LOCAL ONLY" — the opposite of public evidence — and tinted the selected row. Selection is accent, and both local-state labels are neutral. - orange sat on the inert rail total and on every "decrypted on this device" footer, competing with the controls that are actually actionable. A standing caveat is neither a deal nor an error, so .actionWarning takes the design system's risk frame instead of the deal frame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
… send You wrote at ~110ch and read at 68ch: the letter body is capped at --measure-reading in the reading pane, while the composer's fields spanned the whole 880px pane. The composer's prose now takes the same measure. The recipient field is exempt and sized to fit a full felt on one line, because an address is an identifier rather than prose. Delete draft was a full-width control directly under Send encrypted message — a destructive action at the primary action's weight and adjacency. It moves below a rule, right-aligned at its own size. Two hints were mono at 9-10px, which is the desk's label face, and one of them restated the PUBLIC column of the privacy strip two rows above. Both are now body text at a readable size, and the attachment note says what happens to the document instead of describing the UI's own design decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
FOLDERS was a single-column list with boxed counts; SHOW was a two-column grid with a smaller face, plain counts, an orphaned last cell and a solid orange chip flush to the window edge — two controls doing the same job, in two geometries. SHOW now uses the folder list's rows, counts and active treatment, and both inset their text to the same 12px as Compose, which no longer spans the pane edge-to-edge like a title bar. "Encrypted mailbox recovery" and "Device safety" were collapsed sections with no disclosure affordance at all, so they read as headings for empty regions; they take the design system's +/- marker. Checking for mail is the sidebar's most-used action and was two unequal buttons crammed into "1fr auto", the primary one reading as a muted box. Both now fill the panel, and the section heading matches the labels above it instead of being a third heading style. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Every row carried the same fact three times and its identity twice:
- "Sealed sender" and "Sealed counterparty" differed only by envelope type,
which says nothing about who sent a record — no incoming record carries a
public sender. One name now covers all of them, and it drops the caption
that restated it; the rail's standing note already says sealed rows have no
public sender.
- The preview line repeated the type badge directly under it ("Invoice ·
12,400 USDC" over INVOICE). Previews now spend themselves on terms — both
legs of an offer or escrow, the amount requested, the reason a deal was
declined — and say plainly when a payload cannot be read instead of falling
back to the badge's own word.
- Read state and provenance shared one slot, so POSTED ON-CHAIN read as a
third value of unread/opened. Provenance is a chip beside the type badge,
and the state slot belongs to incoming mail.
- A 66-character felt was the row's primary identity and wrapped to two lines,
breaking the row rhythm; the rail uses the same shortened headline as the
thread head, with the full felt on hover.
In the thread, consecutive records had no boundary, so one message's "sealed
on this device" footer read as a heading for the next. They are separated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
repeating themselves Between 1024px and 1180px the fixed 236px sidebar and 320px rail left the reading pane under 470px, so a letter wrapped at roughly 40 characters while the sidebar sat mostly empty. The sidebar becomes a drawer at 1180px instead, and the JS breakpoint that marks the drawer modal now matches the CSS one — they were 900px and 1023px apart, which left a closed drawer focusable off-screen in between. Back to messages is scoped to the phone layout, where the list is actually off-screen. On phones the top bar said "APP20 Mail" directly under a tab bar whose current tab says MAILBOX, above a rail heading that repeated the folder name — three rows of chrome for one fact, in a viewport that was already 40% chrome before the first message. The bar now names what the pane is showing, with the active type filter beside it, and the rail heading stands down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Replying lived only inside a thread record, so on a long correspondence it scrolled out of reach. The reading toolbar carries it while a record is open, and it stops spending its title line on the folder name that the rail already shows two panes to the left. Drafts: the recipient felt was the row's primary identity at full length, the same wrap that broke the message rail's rhythm, and every row wore a DRAFT chip inside a pane titled Drafts, under a note that named the active type filter for no reason. Key setup: the button that registers a mailbox is disabled without a wallet and said nothing about why. The consequence of declining device encryption — the raw seed sits in the clear, and anyone with the profile can send payment requests that display as verified from you — was 11px fine print under an optional-looking checkbox; it takes the risk frame the design system has for exactly this. Prose across the module keeps a measure instead of running the full pane. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Every incoming record in a thread carried an open address-book form — label, book select, input and a submit button — between its meta line and the letter, for an act (naming a sealed sender on this device) that is occasional and deliberate. It collapses behind a disclosure, like the public record below it. The conversation note and the button that continues the conversation shared a stacked grid, so a record spent three lines on chrome before its first word. They share one line, and only the longer notes wrap onto their own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Every amount in the product printed ungrouped: a desk reads 25,000 STRK at a
glance and has to count the digits of 25000. formatBaseUnits groups the
integer part with literal commas — never the fraction, and never through a
locale format that could turn a decimal point into a group mark.
Inside a deal card, the protocol truth ("value moves now, this is not an
atomic swap") and the procedural note ("accepting takes two approvals") wore
the same frame, so neither ranked. The consequence keeps the frame; the note
takes a rule, which also stops the card from nesting a box in a box in a box.
A receipt printed its transaction hash twice at full length — once inside the
sentence, where it broke the line, and once as the copyable record below — and
ended with the raw protocol constant "one_sided_v1" under the word "warning".
The sentence carries a shortened hash and says what the constant means.
Prose in the module keeps a measure; the sheet type and meta line keep the
mono label face that the desk is built on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The dismissal shared the stretch treatment of the pay actions, so on an invoice with nothing payable from this rail it took the whole action row and looked like the card's primary button. It keeps its size and sits at the end of the row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Connecting a wallet gates every module, and it was the weakest affordance on the page: a rounded legacy pill that resolved to bare orange text beside a bordered Pay link and a solid accent network chip. It becomes the desk's square accent button, and a connected wallet turns into a quiet bordered status chip, because a connected wallet is state rather than an action. The active network then sat as a second solid accent block flush against it, so the two read as one control. Which network is current is state too, and the header already marks current with the tab's inset accent rule; the chip uses that instead. On phones both wallet controls keep the accent and lose the width they do not need. Checked for horizontal overflow at 320, 360, 390, 768, 1024, 1180 and 1440. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The dialog that the mailbox's own accent button opens arrived as a rounded pastel card with a soft drop shadow and a circular close button — the one surface in the flow that still looked like a different product. It takes the desk's dialog: square, accent hairline, hard offset shadow, square controls. The mailbox connect wrapper also styled every descendant button, which repainted that dialog's close button and wallet rows in solid accent; it styles its own trigger only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The view's own navigation was 8px mono — the smallest type on the page, for the control that switches between its four screens. It reads at the size the app header and the mail sidebar use. View titles were set in the reading serif at regular weight, the face the design system reserves for what a human wrote; New request, Active, Activity and a record's own heading are UI, so they take the body face at display weight. Activity had no container: its heading and two paragraphs of storage prose ran from the window edge to the far side of a 1440px viewport, beside cards that were inset. It gets the same 16px inset as Active, and that prose keeps a measure. On the operations route the router marked /rfq current as well, so both chips in the sub-nav claimed to be the open page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
A disabled rail said the same thing three times — the environment banner, a wallet-recovery card, and the ticket's own notice — and the only button among them was a grey "Review wallet connection" that re-read storage which cannot answer without a wallet. Where the rail cannot execute an RFQ at all, the recovery card stands down (connecting a wallet does not enable Sepolia) and the reason leads the view at full width instead of trailing it at a third. Where a wallet genuinely is the missing piece, the card carries the wallet control itself, styled as the accent action rather than a browser-default box. The public market board also stopped pretending: PUBLIC SPOT read "OPT IN" in the slot where a price goes, and the opt-in that loads third-party data sat inside the 1D/7D/30D group as though it were a range. The price cell reads "not loaded", and the opt-in moved to the empty chart it fills, next to the disclosure of what opting in discloses. The chart's placeholder no longer reserves 300px of black to say it is empty. Labels in the densest panel of the product were 8px mono; they are 9px, and the eight info dots are muted until hovered so the accent belongs to controls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The screen where a person commits value had two buttons that fell back to the browser's grey default, so "Accept and fund" — the commit action — looked disabled next to an equally weighted decline. Accept is the accent action, decline is a ghost, and the details disclosure uses the desk's +/- instead of a stray triangle. Amounts read 25000 STRK; they now read 25,000 STRK, from one shared helper rather than two copies of the same function in two files. The review's fact labels join the mono label language of the tiles above them, its values and its privacy list keep a measure instead of running the width of the sheet, and the invited-maker panel's directory facts became a hairline grid rather than six unaligned lines. A maker excluded because the whole directory is stale was labelled "Excluded" beside its own positive rationale — "Excluded · Signed a verified quote for this exact request". The card now gives the binding reason, and keeps the maker's own words for the makers that actually refused. The operations dashboard painted an empty cell whenever a section had an odd number of facts, and set its labels in body sans; the last fact spans the row and the labels are mono. On phones the workspace sub-nav went back to 8px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
… it is An in-flight RFQ led with "25000000000000000000000 base units STRK → 12400000000 base units USDC" — a 23-digit integer as the headline of a trade card. The record now reads 25,000 STRK → 12,400 USDC, with the exact integers kept one rank below in muted mono, so nothing is lost and the line can be read. The action on that card was another browser-default grey button, with the sentence explaining it pinned to the far end of a 1000px row; they sit together, and the button uses the desk's outline treatment with a real disabled state. Every disclosure in the workspace now carries the same +/- marker: sharing a selector with the final review's summary had left three of them with no affordance at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
.scratch-shot.mjs is a throwaway Playwright screenshotter used while reviewing the RFQ views; it was swept into the frame commit by a directory-wide add and is not part of the app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Counterparties imported .masthead, .mastheadCopy and .eyebrow from the RFQ module and none of them existed, so the page title, its eyebrow and its intro resolved to no class at all: they rendered unstyled and ran from the window edge while the panel beneath them was inset. They get the desk's masthead — a gutter, a display-weight headline, and a measure on the intro — and the fine print reads at the same text column instead of the full window. The empty book said a wallet was required without offering one; it carries the wallet control, like the mail and RFQ gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
An undefined CSS-module class resolves to `undefined`: the element renders with no styling and the literal word "undefined" in its class list, and nothing catches it — not the type checker, not the bundler. Counterparties lost its whole masthead that way, and the Privy vault's recipient field carried a modifier that was never written. The guard scans every component that imports a stylesheet and asserts each styles.x it uses is defined there. The vault's dead reference is removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The funding gate was the last panel written outside the design system: its eyebrow was a plain paragraph, its four readiness facts were body-sans label and value pairs floating in a gap-12 grid with no rules, its section headings were browser h3s, and every sentence ran the full width of a 1280px window. It now reads like the rest of the desk — a mono eyebrow, a display-weight headline, a hairline fact grid with mono labels, mono section labels, and a measure on the prose — without changing a single statement it makes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
…arched Read state lived in React state alone, so a refresh marked an entire mailbox unread again and the rail's own note admitted it in small print. It now persists per chain and account in this browser, bounded so a long-lived mailbox cannot grow it without limit, cleared by Forget this device along with every other local record, and written at the moment a record is opened rather than from an effect that could race an account switch and file one account's history under another's key. The mailbox also had no search at all: with sealed senders, felt addresses and fourteen record types, fifty records were unnavigable. The rail searches what this device has already decrypted — correspondent, preview, record type, index and device-local recipients — and says so, since a query can only reach the plaintext this browser holds. Nothing about a search leaves the device. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
…cords across two tabs The public market board led the New request view: a CoinGecko chart and eight metrics, above the ticket, in a panel whose own labels read "Not an RFQ quote" and "NON-EXECUTABLE", behind an opt-in that discloses the visitor's IP. It is reference, so it collapses into a disclosure under the ticket and opens when someone asks for it. The view is now what a person came to fill in, and the page it renders is half the height it was. Active and Activity were two tabs over the same records, differing by a filter, which left "which one am I in?" as a question with no good answer. They are one Records view with an explicit scope — in flight first, because a record you can still act on is why you opened it — and #active and #activity still resolve there so existing bookmarks keep working. Operations is a status page for one endpoint, mostly reading Unavailable; it leaves the workspace tab bar for the row of separate operations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Naming a sealed sender was stored against a single envelope and shown nowhere: the rail, the thread head and search all still said "Sealed sender", so the work of identifying who you were talking to died at every record boundary. A name now belongs to the conversation. It carries to every record that shares it, including ones decrypted later, and it is what the rail row, the thread head and search show — as the address-book label when there is one. The caveat travels with it rather than being dropped: "Named on this device · not authenticated", because a local label is not authentication and this product does not pretend otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Four caveats were ambient rather than situated, and repetition was making the ones that matter easier to skip: - The signal bar carried a sentence that never changed on any route or in any state. The bar keeps live session state; each view states its own boundary where that boundary applies. - Every record in a thread ended with where its plaintext lives, under a pane whose head already says LOCAL PLAINTEXT / CARBON COPY. Only the two provenances a reader cannot infer from that head — an imported payment link, and a Sent copy matched to on-chain evidence — still say so. - Every record without a conversation tag said so, on every record, when the fact only bears on replying. - The mail sidebar footer restated that ciphertext is public on-chain, which the record's own "What the chain sees" panel says in context. The three that earn their words are untouched: the final value review, the send confirmation, and the choice to leave a mailbox seed unencrypted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
…sation An empty mailbox gave three instructions for one state: the welcome sheet asked for a wallet, the rail said to check for mail, and the scan panel said to set up a key. One derived gate — wallet, then key, then neither — now answers in all three places. Search and the local-index note also stop rendering above a mailbox that has no records to search. Replying and naming a sender are acts on a conversation but rendered per record, so a five-record thread carried five reply buttons and five naming forms, under two different verbs. Reply lives in the reading toolbar, once; naming lives under the thread head, once, with the reason a reply cannot prefill a recipient beside it. A record keeps only what is its own: its conversation tag, its sender proof, its unbound auth claim. The decorative CLIP / 01 stamp in the thread head is gone. The signal bar named the RFQ workspace on every route, Mailbox included; it names the module you are in. Pay was a fourth destination styled as a wallet utility, sitting among the account controls with an accent border; it joins the tabs it belongs with. On a disabled RFQ rail the banner states the rail and the notice below it restated the same sentence; the notice explains instead. The Elsewhere row no longer links to a public send whose own label said it was unavailable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
…osure before using it The UI specs still navigated Active/Activity tabs, looked for a "Public send" link and expected the market board open. They now use New/Records, the In flight/All scope buttons and the collapsed "Public market context" board. The mailbox recovery panel is a closed disclosure, so its backup buttons are not visible until opened; the two localnet specs open it first. The ticket also left-aligns under the view heading at 560px instead of floating centred in an empty pane. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
…d as two plain steps
The one-time privacy briefing opened as a modal the moment the ticket
mounted, so anyone landing on /rfq could not reach the wallet picker or
the navigation until they acknowledged it. It now renders in the flow at
the top of the ticket, waits for a connected wallet before its first
showing, and leaves the page usable; the ticket's own actions stay
disabled until it is acknowledged, which is the gate that matters.
The ticket also drops the three "nothing has happened yet" chips in its
idle state, names its two steps plainly ("Review what makers will see",
"Request quotes"), gives the invitation consent a proper checkbox row,
and leads each maker card with the verdict, keeping key and capacity
facts behind a disclosure.
Records: the empty state links straight to a new request, and the
storage caveats fold into one "What this browser stores" disclosure.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The compose footer now says how many recipients a letter can carry and no longer repeats the "recipient count is public" caveat that the what-is-public list above it already states. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
The rail row now reads "Unread. Sealed sender. Memo. 0.1 USDC to settle" instead of the old "Settlement memo · 0.1 USDC" label. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
#activity was the history tab, so someone arriving on it expects every record, not only the ones still in flight. The Records view now opens in the All scope for that hash. The swap-routes spec still walked the pair review route, which the app removed; that block is gone and the test says what it still covers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Restoring and reconciling never resubmits fund, fill, claim, or refund for any record, so the All scope's storage disclosure states it as the In flight scope already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Twenty-six commits taking the product from "correct but hard to act in" to a desk you can work at. Every change was verified in the running app at 320–1440px; typecheck and the unit suite (1390 tests) pass on every commit.
Behaviour
Information architecture
#activeand#activitystill resolve there.Design system
25,000 STRK, not25000, from one shared helper per module.Bugs found on the way
.masthead,.mastheadCopyand.eyebrowfrom a module where none existed, so its title and intro rendered unstyled and edge-to-edge. A new test now fails the build on any undefined CSS-module class; it also caught a dead modifier in the Privy vault./rfq/operationsmarked both sub-nav chips as the current page.Not covered
The localnet-only surfaces — the private-intent ticket and the quote-comparison screen — could not be exercised without a devnet, so they are reviewed by code only.
tests/ui/app20.localnet.spec.tshas three assertions updated for renamed UI and wants a run against a live localnet before merge.🤖 Generated with Claude Code
https://claude.ai/code/session_016c1PxPnJjZfByfqUjYgvKB
Generated by Claude Code