Payment Declined
The most expensive error in any product, handled properly: the card stays, the reason is named, and two next steps are offered.
Your bank declined this card
Nothing was charged. This usually means a limit or a block on online payments, not a wrong number.
Build a payment declined state for a checkout. Structure: a card row showing the payment method, then an explanation panel and two actions that appear beneath it. This is the most expensive error in any product, and the default version says "payment failed" and stops. Do these instead: 1. Do NOT clear the card details. Keep the masked number and expiry exactly where they were - wiping the field makes the buyer retype sixteen digits to change one thing. 2. Say "nothing was charged" early. It is the question every declined buyer has, and leaving it unanswered is what produces the second attempt and the duplicate charge. 3. Translate the reason instead of quoting it: "Your bank declined this card", plus a line on what that usually means (a limit or a block on online payments, not a wrong number). A raw gateway code tells the buyer nothing they can act on. 4. Offer TWO actions - retry this card, and use another card. A single retry button ignores the most common fix. Motion: on decline, shake the card row once through 0, -6, 6, -4, 4, 0 over 0.36s and stop. A looping shake reads as an error stuck on rather than as a refusal. The explanation panel opens by animating its HEIGHT from 0 (spring, stiffness ~380, damping ~32), so the buttons are pushed into view as part of the same movement rather than appearing under a panel that was already there. The card border transitions to red over 0.3s. Accessibility: the reason line is aria-live="polite". Under prefers-reduced-motion, render the declined state with no shake and no expansion. Colors as tokens: paper card, hairline border, a reserved status red for the declined border, icon and reason. Build the component, not this demo of it. - Ambient motion that IS the component may loop: a drifting background, a marquee, a spinner while something is genuinely loading. - Do not add any timer, interval, loop or script that performs a person's action by itself. No auto-hover, no auto-click, no cycling through states, no pretend submitting, no replaying the interaction on a schedule. - Every state that answers a person waits for a real event from that person. - Placeholder copy and sample content are welcome. Simulated behaviour is not.