public views · member · concept · v3.5 neutral-first

Support form

The one place a person lands when Relay tells them to talk to a human — relayctx.com/support. Public, no session, arrived-at-from-a-failure: a textbook public view. This concept puts it on the shared .shell and works through the states the shipped page has to survive — including the ones nobody designs for, like the ticket system being down.

arriving — what the URL prefills
outcomes — after they press send
relayctx.com/support
support · no sign-in needed

Contact support

Tell us what you need and we'll follow up by email. You don't need to be signed in.

Deep-link builder — the product requirement, made touchable

The form's real job isn't the form. It's that any URL can pre-compose a ticket: the app hands someone a link that's already filled in, and all they do is read it and press send. Build one and apply it to the demo above.

The prefill contract

ParamBehaviour
topicSelects the dropdown. Known set: account-merge · billing · access · bug · other. An unknown value falls back to "other" and the raw string rides along in a hidden field, so a topic we invent later still reaches the ticket instead of being silently dropped.
refThe 4-hex correlation code minted when the server logged the error that sent them here. Rendered read-only — it's evidence, not input — and carried into the ticket body. Support resolves it in the console Error Log by [ref=XXXX].
emailPrefills reply-to, still editable. The person may want a different inbox than the one that failed.
subject · messageOptional full compose. This is what makes a link "a ticket waiting for a signature".

Every one of these is reflected into HTML from an attacker-controlled query string on an unauthenticated page, so all five are escaped on render — the shipped implementation has a test per param asserting it.

What this adds to the shell — one new slot

The .shell was drawn for terminal states: a message, at most two buttons, done. This is the category's first surface that asks the person for something, so it needs one addition — and only one:

SlotContract
.formNew. Sits between .body and .actions. Left-aligns (centred labels are unreadable); inherits every token otherwise. Mono micro-labels, one field per row, prefilled fields ring in --rly-accent-ui so a person can see at a glance what the link filled in for them.
.codeOmitted. Empty ⇒ hidden, per the anchor's rule — there's no HTTP status to shout.
.status-stripNeutral on entry (support · no sign-in needed), accent on success, --rly-error on a rejected submit. Same tint rules as every other member.
.supportEmpty on entry and on a clean send — there's nothing to quote. It earns its place on the degraded outcome, where the ref is the only thread the person has back to us. Same slot, same mono treatment, same lookup vocabulary as the error members.

The state nobody designs — ticket system down

This person already hit one failure; that's why they're here. If Freshdesk is unreachable when they press send, showing them an error is a second dead end in a row, and they leave. So the degraded path still thanks them, still gives them a ref, and tells them what to do if it goes quiet — while the failure itself lands in the console Error Log for us. Flip to ticket system down above: the person's experience barely changes, and that's deliberate.

Status — concept vs. what's live

The shipped page does not use this chrome yet. /support went live to kill a 404 that the app was already linking into, and it was styled from the /feedback page's inline token block — so it reads as Relay, but it's a bespoke chrome, not a member of this category. This concept is the proposal to fold it in. Adopting it is a reskin of the render layer only: the routing, prefill parsing, escaping, Turnstile, rate limit, and Freshdesk call all stay exactly as they are.

Category rules honoured