/* ==========================================================================
   WIZI — prepaid pay-by-link micropage

   The tokens are the passenger app's, taken from the same place the app takes them: the design
   mockup's `:root` block (users/design/styles.css), which `WiziColors`/`WiziDimens`/`WiziStyles` in
   the KMP module mirror value for value. Nothing below spells out a colour that is not declared
   here.

   The page is one column on a phone and the same column, centred, on anything wider. That is the
   whole of the responsive behaviour and it is deliberate: a payment form is read top to bottom in
   one pass, and a second column would only ever be somewhere for the eye to lose its place between
   the amount and the card.
   ========================================================================== */

/* Sinkin Sans is the brand primary (grafički standardi §3.1) and is not distributed by any web font
   service, so it is served from the same files the app bundles. Body UI is the platform sans —
   Roboto on Android, San Francisco on iOS — which is what §3.2 asks for and what makes the page read
   as native to the phone it is opened on rather than as a page about an app. */
@font-face {
  font-family: "Sinkin Sans";
  src: url("../fonts/sinkinsans_600semibold.otf") format("opentype");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Sinkin Sans";
  src: url("../fonts/sinkinsans_700bold.otf") format("opentype");
  font-weight: 700;
  font-display: swap;
}

:root {
  /* Primary brand (§2.1) */
  --wizi-yellow: #e4ff00;
  --wizi-black: #000000;

  /* Neutral scale (§2.2) */
  --n-0: #ffffff;

  /* Derived surfaces */
  --bg: #f4f4f4;
  --bg-2: #ededed;
  --surface: #ffffff;
  --ink: var(--wizi-black);
  --ink-2: #1a1a1a;
  --muted: #8a8a8a;
  --muted-2: #b3b3b3;
  --line: #e4e4e4;
  --line-2: #d4d4d4;

  --lime-glow: rgba(228, 255, 0, 0.45);
  --focus-ring: rgba(228, 255, 0, 0.3);
  --error: #e53935;
  --error-ring: rgba(229, 57, 53, 0.12);
  --success: #2bb49a;
  --warning: #ff9f2a;

  /* The chrome that outranks whatever it sits on. Named by the job rather than by the colour,
     because in a dark theme the thing that stands out against the page is no longer the darkest
     thing on it. */
  --inverted-bg: var(--wizi-black);
  --inverted-fg: var(--n-0);

  /* A hairline that separates a raised surface from the page when a shadow no longer can. Invisible
     in daylight, where the shadow does the work. */
  --surface-edge: transparent;

  /* The amount's own edge. Nothing in daylight — a black panel on a white page needs no help being
     found. */
  --total-edge: transparent;

  --scrim: rgba(0, 0, 0, 0.45);

  /* Tells the browser this page is at home in either appearance, so its own furniture — scrollbars,
     the canvas behind the document, the caret, and the select this page cannot restyle from the
     inside — follows the device instead of staying daylight. */
  color-scheme: light dark;

  /* Radii */
  --r-pill: 999px;
  --r-lg: 22px;
  --r-md: 16px;
  --r-sm: 12px;
  --r-xs: 8px;

  /* Touch targets — never below 44px */
  --tt-min: 44px;
  --tt-rec: 48px;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 2px 6px rgba(0, 0, 0, 0.05);
  --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.05), 0 12px 32px rgba(0, 0, 0, 0.10);
  --shadow-bar: 0 -8px 24px rgba(0, 0, 0, 0.08);

  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --dur-1: 140ms;
  --dur-2: 220ms;
  --dur-3: 360ms;

  --font-brand: "Sinkin Sans", "Manrope", "Roboto", system-ui, sans-serif;
  --font-ui: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  /* The column. One value, read by the page and by the sticky action bar, so the button can never
     end up a different width from the form it belongs to. */
  --column: 480px;
  --gutter: 16px;

  /* The mockup indents helper text and field labels past the pill radius, so they line up with the
     first character inside the field rather than with its outer edge. */
  --field-indent: 14px;

  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top: env(safe-area-inset-top, 0px);
}

/* ==========================================================================
   Night

   Followed from the device rather than offered as a switch: this page is opened from a message,
   read once, paid, and closed. Nobody is going to set a preference on it, and they have already set
   one on their phone.

   The brand yellow does not move. It is the identity, it carries the same weight on either ground,
   and the palette it sits in is a pure grey scale in both directions (§2.2) — so night is the same
   scale read from the other end, not a different set of colours.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #121212;
    /* Lighter than `--surface`, not darker as in daylight. The inset tiles that use it are recesses
       cut into a card, and on a dark card a recess has to be found by being lighter; darker than the
       card it sits on, it simply disappears. */
    --bg-2: #272727;
    --surface: #1e1e1e;
    --ink: #f2f2f2;
    --ink-2: #e4e4e4;
    --muted: #9a9a9a;
    --muted-2: #6d6d6d;
    --line: #2b2b2b;
    --line-2: #3a3a3a;

    --error: #ff6b67;
    --error-ring: rgba(255, 107, 103, 0.2);
    --success: #43d6ba;
    --warning: #ffb44f;

    --inverted-bg: #2b2b2b;
    --inverted-fg: #f6f6f6;

    /* A shadow cast on a dark page is invisible, so a raised surface is given an edge instead. */
    --surface-edge: rgba(255, 255, 255, 0.07);

    /* The one place the brand is laid over a surface rather than on it. By day the amount is a black
       panel and unmistakable; by night `--inverted-bg` is a shade off the cards around it, and the
       figure that decides whether the form below gets filled in would read as one more card. */
    --total-edge: rgba(228, 255, 0, 0.34);

    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.4);
    --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.55), 0 12px 32px rgba(0, 0, 0, 0.5);
    --shadow-bar: 0 -8px 24px rgba(0, 0, 0, 0.5);

    --scrim: rgba(0, 0, 0, 0.66);
  }
}

*,
*::before,
*::after { box-sizing: border-box; }

/* The page hides and shows panes through the `hidden` attribute, and the browser's own rule for it
   is a bare `display: none` that any class with a `display` of its own outranks. Stated here once,
   with the weight to win. */
[hidden] { display: none !important; }

html {
  background: var(--bg);
  /* An anchor jump inside a form that ends in a sticky bar has to clear the bar. */
  scroll-behavior: smooth;
}

body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-tap-highlight-color: transparent;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   The column

   One column, gutter-wide on a phone and centred past that. `--column` is 480px because that is
   about where a line of an address stops being comfortable to read and a form starts looking like a
   spreadsheet — not because of any particular device.
   ========================================================================== */

.page {
  width: 100%;
  max-width: calc(var(--column) + var(--gutter) * 2);
  margin: 0 auto;
  padding: calc(var(--safe-top) + 18px) var(--gutter) 24px;
}

/* Room for the action bar, which floats over the foot of the form. Only where there is one — the
   three ending screens have nothing sticky and would just have a hole at the bottom. */
.page[data-state="pay"] {
  padding-bottom: calc(var(--tt-rec) + 44px + var(--safe-bottom));
}

/* ==========================================================================
   Masthead
   ========================================================================== */

.masthead {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 22px;
}

/* Set rather than drawn: the mark is two syllables of a five-letter word, and at this size the
   brand face carries it better than a bitmap would. */
.masthead__mark {
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.14em;
  line-height: 1;
  padding: 7px 10px 6px;
  border-radius: var(--r-xs);
  background: var(--wizi-yellow);
  color: var(--wizi-black);
}

.masthead__company {
  min-width: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.page__title {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: 26px;
  line-height: 32px;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.page__subtitle {
  margin: 6px 0 20px;
  font-size: 14px;
  line-height: 20px;
  color: var(--muted);
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  border: 1px solid var(--surface-edge);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  padding: 16px;
  margin-bottom: 12px;
}

.card__title {
  margin: 0 0 12px;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: 15px;
  line-height: 20px;
  letter-spacing: -0.005em;
  color: var(--ink);
}

.card__note {
  margin: -4px 0 14px;
  font-size: 13px;
  line-height: 18px;
  color: var(--muted);
}

/* The lock line under the card fields. It is the one sentence on this page that answers the question
   everybody silently asks a payment form, so it carries a mark rather than being another grey
   paragraph. */
.card__note--secure {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 14px 0 0;
}

.card__note--secure svg {
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  margin-top: 2px;
  color: var(--muted-2);
}

.rule {
  height: 1px;
  margin: 14px 0;
  border: 0;
  background: var(--line);
}

/* ==========================================================================
   The journey

   Pickup and destinations as one route rather than as two labelled rows: a dot per stop, joined by a
   line, which is the same drawing the app puts on its map and the shape anyone recognises as a
   journey without reading a word of it.
   ========================================================================== */

.journey {
  display: flex;
  gap: 12px;
}

.journey__rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  padding-top: 5px;
}

.journey__dot {
  width: 9px;
  height: 9px;
  border-radius: var(--r-pill);
  flex-shrink: 0;
}

/* Where the ride starts is the brand's own colour with an ink ring, which is how the app draws the
   passenger's own position. Where it ends is ink — a destination is a fact rather than a place
   anybody is standing. */
.journey__dot--origin {
  background: var(--wizi-yellow);
  box-shadow: 0 0 0 2px var(--ink);
}

.journey__dot--destination { background: var(--ink); }

.journey__line {
  flex: 1;
  width: 2px;
  min-height: 14px;
  margin: 4px 0;
  border-radius: var(--r-pill);
  background: var(--line-2);
}

.journey__text {
  min-width: 0;
  display: block;
  padding-bottom: 12px;
}

.journey:last-of-type .journey__text { padding-bottom: 0; }

.journey__label {
  display: block;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.journey__value {
  display: block;
  margin-top: 1px;
  font-size: 15px;
  font-weight: 500;
  line-height: 20px;
  color: var(--ink);
  /* An address is allowed to wrap. Truncating one on a page whose whole job is to say what is being
     paid for would hide the half that identifies it. */
  overflow-wrap: anywhere;
}

.detail {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.detail__label {
  font-size: 13px;
  color: var(--muted);
}

.detail__value {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  text-align: right;
}

/* ==========================================================================
   The amount

   Inverted, and the only inverted surface on the page. It is the number that decides whether any of
   the fields below get filled in, and it should be readable from the distance a phone is held at
   when someone is deciding whether the figure is the one they were told.
   ========================================================================== */

.total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 16px 18px;
  margin-bottom: 18px;
  border-radius: var(--r-lg);
  border: 1px solid var(--total-edge);
  background: var(--inverted-bg);
  color: var(--inverted-fg);
  box-shadow: var(--shadow-2);
}

.total__label {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.72;
}

.total__value {
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: 26px;
  line-height: 30px;
  letter-spacing: -0.01em;
  /* Figures of the same width, so the amount does not shuffle sideways between languages that group
     thousands differently. */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ==========================================================================
   Fields

   The app's own: a pill on the card's surface, a hairline at rest, ink and a yellow ring on focus,
   red and a red wash when the value is wrong. Label and error are indented past the pill's radius so
   they line up with the first character inside the field.
   ========================================================================== */

.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.field { min-width: 0; }
.field--wide { grid-column: 1 / -1; }

.field__label {
  display: block;
  margin: 0 0 6px var(--field-indent);
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
}

.field__input {
  display: block;
  width: 100%;
  height: var(--tt-rec);
  padding: 0 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-ui);
  /* Sixteen pixels exactly. Below it iOS zooms the page when a field takes focus, and on a form this
     long that leaves the payer typing off the side of it. */
  font-size: 16px;
  line-height: normal;
  transition: border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
  appearance: none;
}

.field__input::placeholder { color: var(--muted-2); }

.field__input:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.field[data-invalid] .field__input {
  border-color: var(--error);
  box-shadow: 0 0 0 3px var(--error-ring);
}

.field__hint,
.field__error {
  margin: 6px 0 0 var(--field-indent);
  font-size: 12.5px;
  line-height: 17px;
}

.field__hint { color: var(--muted-2); }

.field__error {
  color: var(--error);
  /* Empty until there is something wrong, and taking no room until then: a reserved line under every
     field turns a form of eight into a form of sixteen. */
  display: none;
}

.field[data-invalid] .field__error { display: block; }
.field[data-invalid] .field__hint { display: none; }

/* The country select. Its list is drawn by the platform and cannot be styled from here, which is
   correct — a native picker on a phone is a wheel the thumb already knows how to use. What is styled
   is the closed control, so it is the same pill as the fields above it. */
.field__select { position: relative; }

.field__select .field__input {
  padding-right: 44px;
  cursor: pointer;
  /* Truncated rather than wrapped: a `<select>` cannot wrap, and the longest country names would
     otherwise run under the chevron. */
  text-overflow: ellipsis;
}

.field__chevron {
  position: absolute;
  top: 50%;
  right: 16px;
  width: 20px;
  height: 20px;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--muted);
}

/* ==========================================================================
   The card field

   The gateway's own fields. Monri's is one iframe: its inside is styled through the gateway's
   `style` option in prepaid.js and nothing here reaches in; what this rule draws is the pill it sits
   in, so a field that belongs to somebody else's document still lines up with the six above it.
   Adyen's is its own form, and gets the modifier below instead.
   ========================================================================== */

.card-field {
  min-height: var(--tt-rec);
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

/* Empty until the gateway's script has drawn into it. Collapsed rather than left as an empty pill,
   which would read as a field that failed.

   `border-width` rather than `border`, which would also reset the colour to `currentColor` — and
   `currentColor` here is the page's ink. The width is what makes the border disappear; taking the
   colour with it only means the hairline comes back black for as long as it takes to repaint. */
.card-field:empty {
  min-height: 0;
  padding: 0;
  border-width: 0;
}

/* Adyen draws more than a field: three labelled inputs — number, expiry, security code — each with
   its own border, plus the brand logos. A pill around all that would be a box around boxes, so the
   wrapper steps aside and Adyen's own markup is themed instead.

   Through Adyen's design tokens, which its stylesheet only ever reads with a fallback and never
   declares — so setting them here, on the container, reaches every element inside it, and the
   night palette comes along without a rule of its own. The inside of each iframe is not reached by
   any of this; its text colour is set through the component's `styles` option in prepaid.js. */
.card-field--adyen {
  min-height: 0;
  padding: 0;
  border-width: 0;
  background: none;

  --adyen-sdk-color-background-primary: var(--surface);
  --adyen-sdk-color-background-secondary: var(--bg-2);
  --adyen-sdk-color-label-primary: var(--ink);
  --adyen-sdk-color-label-secondary: var(--muted);
  --adyen-sdk-color-label-tertiary: var(--muted);
  --adyen-sdk-color-label-critical: var(--error);
  --adyen-sdk-color-outline-primary: var(--line-2);
  --adyen-sdk-color-outline-primary-hover: var(--muted-2);
  --adyen-sdk-color-outline-primary-active: var(--ink);
  --adyen-sdk-color-outline-secondary: var(--line);
  --adyen-sdk-color-outline-tertiary: var(--muted-2);
  --adyen-sdk-color-outline-critical: var(--error);
  --adyen-sdk-color-separator-primary: var(--line);
  --adyen-sdk-focus-ring-color: var(--wizi-yellow);
  --adyen-sdk-border-radius-m: var(--r-md);
  --adyen-sdk-border-radius-s: var(--r-xs);
  font-family: var(--font-ui);
}

/* The 3-D Secure challenge is drawn into the same container, and it is the issuer's page: a white
   sheet whatever the device's appearance. Rounded to match, and nothing more. */
.card-field--adyen iframe[name="threeDSIframe"] {
  border-radius: var(--r-sm);
}

/* ==========================================================================
   Merchant

   Who the card statement will name. Quiet, at the foot, and never absent: the company billing for
   the ride is very often not the name on the door of the taxi, and this is where that is checked.
   ========================================================================== */

.merchant {
  display: block;
  padding: 0 var(--field-indent);
  margin: 18px 0 4px;
  font-size: 12.5px;
  line-height: 17px;
  color: var(--muted-2);
}

.merchant__label {
  display: block;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.merchant__name {
  display: block;
  margin-top: 2px;
  font-weight: 500;
  color: var(--ink-2);
}

.merchant__line { display: block; }

/* ==========================================================================
   Status lines and the spinner
   ========================================================================== */

.status {
  display: none;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--muted);
}

.status--on { display: flex; }

.spinner {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border: 2px solid var(--line-2);
  border-top-color: var(--ink);
  border-radius: var(--r-pill);
  animation: spin 0.8s linear infinite;
}

.spinner--lg {
  width: 30px;
  height: 30px;
  border-width: 3px;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  /* Still turning, because it is the one thing on the screen saying the page has not died — just
     slowly enough not to be what the reduced-motion setting was turned on about. */
  .spinner { animation-duration: 2.4s; }
}

/* ==========================================================================
   Errors that belong to the form rather than to one field
   ========================================================================== */

.form__error {
  display: none;
  margin: 4px 0 0;
  padding: 12px 14px;
  border-radius: var(--r-md);
  border-left: 3px solid var(--error);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 19px;
}

.form__error[data-shown] { display: block; }

/* ==========================================================================
   Actions

   Stuck to the foot of the viewport while the form is scrolled. On a phone the button is otherwise a
   page and a half below the amount it is charging, and a payment form whose payer has to go looking
   for the way to pay is a form that gets abandoned at the last step.
   ========================================================================== */

.actions {
  position: sticky;
  bottom: 0;
  z-index: 20;
  margin: 18px calc(var(--gutter) * -1) 0;
  padding: 12px var(--gutter) calc(12px + var(--safe-bottom));
  background: var(--bg);
  box-shadow: var(--shadow-bar);
}

/* The ending screens have one button and nothing to scroll past it. */
.actions--static {
  position: static;
  margin: 22px 0 0;
  padding: 0;
  background: none;
  box-shadow: none;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 50px;
  padding: 0 22px;
  background: var(--wizi-yellow);
  color: var(--wizi-black);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.005em;
  border: none;
  border-radius: var(--r-pill);
  cursor: pointer;
  box-shadow: 0 6px 14px -6px var(--lime-glow), inset 0 1px 0 rgba(0, 0, 0, 0.05);
  transition: transform var(--dur-1) var(--ease-out), opacity var(--dur-1);
}

.btn:active { transform: scale(0.98); opacity: 0.94; }
.btn:disabled { opacity: 0.4; cursor: default; }
.btn:disabled:active { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .btn:active { transform: none; }
}

/* ==========================================================================
   Endings

   The three screens that are not a form — already paid, dead link, gateway unreachable — and the
   panes of the overlay, which are the same thing arrived at from the other direction.
   ========================================================================== */

.outcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 28px 8px 8px;
}

.outcome__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: 16px;
  border-radius: var(--r-pill);
  background: var(--bg-2);
  color: var(--muted);
}

.outcome__mark svg { width: 28px; height: 28px; }

/* A payment that went through gets the brand's own colour, which on this page it has earned: it is
   the one moment the page has good news. */
.outcome__mark--done {
  background: var(--wizi-yellow);
  color: var(--wizi-black);
}

.outcome__mark--warn { color: var(--error); }

/* A wait, not a failure. The clock over "payment received" reports a card that went through and a
   confirmation still in flight, and red there would tell somebody their money is in trouble. */
.outcome__mark--wait { color: var(--warning); }

.outcome__title {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: 21px;
  line-height: 27px;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.outcome__body {
  margin: 8px 0 0;
  max-width: 34ch;
  font-size: 14.5px;
  line-height: 20px;
  color: var(--muted);
}

/* ==========================================================================
   The wait

   Over the page rather than instead of it. What is behind it — the ride, the amount, the card that
   was just approved — is exactly what somebody reads while they wait, and the card component
   underneath has to stay mounted for the gateway to finish 3-D Secure inside it.
   ========================================================================== */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  background: var(--scrim);
  animation: fade-in var(--dur-2) var(--ease-out);
  /* Nothing behind it is meant to be reached while it is up, and on a phone a form that keeps
     scrolling under a modal reads as a page that did not notice it was covered. */
  overscroll-behavior: contain;
}

.overlay__card {
  width: 100%;
  max-width: 360px;
  padding: 28px 22px;
  border: 1px solid var(--surface-edge);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  text-align: center;
  animation: rise var(--dur-3) var(--ease-out);
}

.overlay__pane {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.overlay__title {
  margin: 16px 0 0;
  font-family: var(--font-brand);
  font-weight: 600;
  font-size: 19px;
  line-height: 25px;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.overlay__body {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 20px;
  color: var(--muted);
}

.overlay__amount {
  margin: 14px 0 0;
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: 22px;
  line-height: 26px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* The reference is only shown when the confirmation has not arrived, and it is shown because it is
   the one thing that identifies this payment to whoever is asked about it afterwards. */
.overlay__reference {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 16px 0 0;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: var(--bg-2);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.overlay__reference code {
  font-family: ui-monospace, "SF Mono", "Roboto Mono", Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  overflow-wrap: anywhere;
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .overlay,
  .overlay__card { animation: none; }
}

/* ==========================================================================
   Wider screens

   The column stops growing and starts sitting in the middle, and the action bar stops being stuck to
   anything: on a desktop the whole form is on the screen at once, so a bar pinned to the bottom of
   the window would be floating a long way below the last field it belongs to.
   ========================================================================== */

@media (min-width: 620px) {
  .page {
    padding-top: 40px;
    padding-bottom: 48px;
  }

  .page[data-state="pay"] { padding-bottom: 48px; }

  .actions {
    position: static;
    margin: 22px 0 0;
    padding: 0;
    background: none;
    box-shadow: none;
  }
}

/* Tall enough to hold the form but not the whole page — a laptop in landscape. The bar goes back to
   being stuck, because here the last field really is below the fold. */
@media (min-width: 620px) and (max-height: 760px) {
  .actions {
    position: sticky;
    bottom: 0;
    margin: 18px calc(var(--gutter) * -1) 0;
    padding: 12px var(--gutter) calc(12px + var(--safe-bottom));
    background: var(--bg);
    box-shadow: var(--shadow-bar);
  }

  .page[data-state="pay"] { padding-bottom: 24px; }
}
