/* Express checkout card.  Layers on top of checkout.css, which supplies the
   shared primitives (Inter, the error row, the palette these rules quote).

   Every unit here is `cqi` rather than `vw`: the card renders inside a
   cross-origin iframe, where `vw` reads the host page's viewport and not the
   width the card actually has. */
.express-page {
  margin: 0;
  padding: 8px;
}

.express-card {
  container-type: inline-size;
  padding: clamp(12px, 3cqi, 20px);
  background: #f8faf7;
  box-shadow:
    0px 2px 4px rgba(58, 68, 69, 0.08),
    0px 4px 12px rgba(58, 68, 69, 0.12);
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 3cqi, 18px);
  border-radius: 8px;
  font-family: 'Inter', sans-serif;
  color: #1b5035;
}

/* ── Tabs ─────────────────────────────────────────────────────────────── */

/* Equal-width segments fill the row when multiple frequencies are offered. */
.express-tabs:not([hidden]) {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 8px;
}

/* Reserve room for the badge overhang without moving either cadence label. */
.express-tabs:not([hidden]):has(.express-yearly-savings-badge:not([hidden])) {
  padding-top: 16px;
}

/* Both tabs grow equally, keeping their labels aligned below the badge. */
.express-tabs:has(.express-yearly-savings-badge:not([hidden])) .express-tab-label {
  padding-block: 20px;
}

.express-tabs input[type='radio'] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.express-tab-label {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: clamp(8px, 2cqi, 11px) 12px;
  border: 1px solid #d5e5dc;
  border-radius: 6px;
  background: #ffffff;
  color: #4a6b5a;
  font-size: clamp(13px, 3.4cqi, 15px);
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  user-select: none;
  transition:
    background-color 120ms ease-in-out,
    color 120ms ease-in-out,
    border-color 120ms ease-in-out;
}

.express-yearly-savings-badge:not([hidden]) {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max-content;
  padding: 2px 7px;
  border-radius: 999px;
  background: #e5eee3;
  color: #1b5035;
  font-size: clamp(10px, 2.6cqi, 12px);
  font-weight: 600;
  line-height: 1.3;
  max-width: calc(100% - 12px);
  overflow-wrap: anywhere;
  outline: 1px solid transparent;
  transition: outline-color 120ms ease-in-out;
}

.express-tabs:has(#express-tab-year:checked) .express-yearly-savings-badge {
  outline-color: #1b5035;
}

/* Monthly reads as selected in yearly mode too, because yearly is a mode
   inside the monthly ask rather than a third tab. */
.express-tabs:has(#express-tab-month:checked) .express-tab-label[for='express-tab-month'],
.express-tabs-yearly-callout:has(#express-tab-year:checked) .express-tab-label[for='express-tab-month'],
.express-tabs:has(#express-tab-year:checked) .express-tab-label[for='express-tab-year'],
.express-tabs:has(#express-tab-one-time:checked) .express-tab-label[for='express-tab-one-time'] {
  background: #1b5035;
  border-color: #1b5035;
  color: #ffffff;
}

.express-tabs input[type='radio']:focus-visible + .express-tab-label {
  outline: 2px solid #2c8658;
  outline-offset: 2px;
}

/* Yearly has no tab label of its own, so an arrow key onto it would move focus
   with nothing to show for it.  Its indicator lands on the Monthly tab, which
   is the control that reads as selected in yearly mode. */
.express-tabs-yearly-callout:has(#express-tab-year:focus-visible) .express-tab-label[for='express-tab-month'] {
  outline: 2px solid #2c8658;
  outline-offset: 2px;
}

/* The first tab names the cadence actually in force: entering yearly through
   the callout re-labels it rather than adding a third tab, and X-ing out of
   yearly puts "Monthly" back. */
.express-tab-year-copy {
  display: none;
}

.express-tabs-yearly-callout:has(#express-tab-year:checked) .express-tab-month-copy {
  display: none;
}

.express-tabs-yearly-callout:has(#express-tab-year:checked) .express-tab-year-copy {
  display: inline;
}

/* Amount cards come from partials/amountCards.ejs and are styled by
   checkout.css.  Only the stack's spacing is set here: the ribbon on a badged card overhangs its top edge, so
   the row above it has to leave room. */
.sliding-scale-cards-stack {
  padding-top: 10px;
  min-width: 0;
}

/* ── Yearly callout ───────────────────────────────────────────────────── */

/* Swaps the cards to yearly pricing in place.  Navigating to the standard
   yearly form instead would drop the visitor out of express mode and
   discard the wallet buttons, which is the opposite of the intent. */
.express-yearly-callout {
  position: relative;
  border: 1px solid #e4e0d4;
  border-radius: 8px;
  background: #faf8f2;
  transition:
    border-color 120ms ease-in-out,
    background-color 120ms ease-in-out;
}

.express-card:has(#express-tab-one-time:checked) .express-yearly-callout {
  display: none;
}

.express-yearly-enter {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: clamp(9px, 2.4cqi, 12px);
  cursor: pointer;
}

.express-yearly-icon {
  flex: 0 0 auto;
  display: flex;
  color: #d98b3a;
}

.express-yearly-icon svg {
  width: 20px;
  height: 20px;
}

.express-yearly-copy {
  flex: 1 1 auto;
  min-width: 0;
}

.express-yearly-title {
  display: block;
  font-size: clamp(11px, 3cqi, 13px);
  font-weight: 600;
}

.express-yearly-body {
  display: block;
  font-size: clamp(10px, 2.8cqi, 12px);
  font-weight: 400;
  color: #4a6b5a;
}

.express-yearly-chevron {
  flex: 0 0 auto;
  color: #4a6b5a;
  font-size: 16px;
}

/* Inactive by default; the `:has()` rules below trade the two halves. */
.express-yearly-title-active,
.express-yearly-body-active,
.express-yearly-exit {
  display: none;
}

.express-card:has(#express-tab-year:checked) .express-yearly-callout {
  border-color: #2c8658;
  background: #eef7f1;
}

.express-card:has(#express-tab-year:checked) .express-yearly-title-inactive,
.express-card:has(#express-tab-year:checked) .express-yearly-body-inactive,
.express-card:has(#express-tab-year:checked) .express-yearly-chevron {
  display: none;
}

.express-card:has(#express-tab-year:checked) .express-yearly-title-active,
.express-card:has(#express-tab-year:checked) .express-yearly-body-active {
  display: block;
}

/* Top left, per the design, so it reads as dismissing the mode rather than
   as another action on the row. */
.express-card:has(#express-tab-year:checked) .express-yearly-exit {
  position: absolute;
  top: -8px;
  left: -8px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 1px solid #2c8658;
  border-radius: 50%;
  background: #ffffff;
  color: #1b5035;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.express-yearly-exit-hint {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── Custom amount ────────────────────────────────────────────────────── */

.express-custom-amount {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.express-custom-amount-copy {
  min-width: 0;
}

.express-custom-amount-label {
  display: block;
  font-size: clamp(11px, 3cqi, 13px);
  font-weight: 600;
}

.express-custom-amount-hint {
  display: block;
  font-size: clamp(10px, 2.8cqi, 12px);
  color: #4a6b5a;
}

.express-custom-amount-hint-one-time,
.express-trust-one-time {
  display: none;
}

.express-card:has(#express-tab-one-time:checked) .express-custom-amount-hint-recurring,
.express-card:has(#express-tab-one-time:checked) .express-trust-recurring {
  display: none;
}

.express-card:has(#express-tab-one-time:checked) .express-custom-amount-hint-one-time {
  display: block;
}

.express-card:has(#express-tab-one-time:checked) .express-trust-one-time {
  display: inline;
}

/* The symbol is painted inside the field's box so the contributor types a
   bare number and still sees the currency they are giving in.  Which side it
   sits on is the locale's call, not ours — `en-US` writes "$22", `es-ES`
   writes "22 €" — so the server resolves the side and these two rules order
   the same markup either way. */
.express-custom-amount-field-suffix .express-custom-amount-symbol {
  order: 2;
}

/* Digits hug the symbol rather than stranding it across an empty field. */
.express-custom-amount-field-suffix .express-custom-amount-input {
  text-align: right;
}

.express-custom-amount-field {
  flex: 0 1 clamp(96px, 34cqi, 150px);
  display: flex;
  align-items: center;
  gap: 4px;
  padding: clamp(7px, 2cqi, 10px) 10px;
  border: 1px solid #d5e5dc;
  border-radius: 6px;
  background: #ffffff;
  box-sizing: border-box;
}

.express-custom-amount-field:focus-within {
  border-color: #2c8658;
  outline: 2px solid #2c8658;
  outline-offset: -1px;
}

/* A typed amount is the selected amount — it unselects whichever card it
   displaced — so the field wears the same ring a chosen card does, and keeps
   it after focus moves on.  Reads "has a value" off the whitespace placeholder
   the markup carries for exactly this purpose. */
.express-custom-amount-field:has(.express-custom-amount-input:not(:placeholder-shown)) {
  border-color: #2c8658;
  box-shadow: 0 0 0 2px #2c8658 inset;
}

.express-custom-amount-symbol {
  flex: 0 0 auto;
  font-size: clamp(12px, 3.2cqi, 14px);
  color: #4a6b5a;
}

.express-custom-amount-input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: 'Inter', sans-serif;
  font-size: clamp(12px, 3.2cqi, 14px);
  font-weight: 600;
  color: #1b5035;
}

/* ── Payment element + actions ────────────────────────────────────────── */

.express-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* A full-width rounded rectangle rather than checkout.css's pill, and
   deliberately shaped like the wallet buttons directly above it: same 44px
   height, same radius as `stripeAppearance`'s `borderRadius`, same sentence
   case and weight.  The two sit adjacent and offer the same thing by
   different means, so a different type treatment on each read as two
   unrelated controls. */
.express-continue {
  width: 100%;
  /* 44px to the pixel, matching the wallet element's `buttonHeight`.  The
     padding is horizontal inset plus room for a label that wraps on a narrow
     phone; a single line is centred by flex and leaves the height at 44. */
  min-height: 44px;
  padding: 8px 20px;
  border: none;
  border-radius: 8px;
  background: #ef7047;
  color: #ffffff;
  font-family: 'Inter', sans-serif;
  font-size: clamp(14px, 5cqi, 20px);
  font-weight: 600;
  cursor: pointer;
  transition:
    filter 120ms ease-in-out,
    opacity 120ms ease-in-out;
}

/* Reuses checkout.css's `.checkout-loading` spinner, which paints into the
   closest positioned ancestor, so this button has to be a positioned flex
   centre and hide its own label spans while it spins. */
.express-continue {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.express-continue.checkout-loading > span {
  visibility: hidden;
  opacity: 0;
}

.express-continue:hover:not(:disabled) {
  filter: brightness(94%);
}

.express-continue:disabled {
  cursor: default;
}

.express-trust {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 5px;
  font-size: clamp(9px, 2.6cqi, 11px);
  color: #4a6b5a;
}

.express-trust-icon {
  flex: 0 0 auto;
}

/* The badge sits on the trust line rather than in its own row, so it scales
   with that line instead of the 24px checkout.css's `.stripe-badge` sets. */
.express-trust-stripe {
  display: inline-flex;
  align-items: center;
}

.express-trust .stripe-badge {
  height: clamp(14px, 4cqi, 18px);
}

/* ── Payment step ─────────────────────────────────────────────────────── */

/*
 * Revealed by "continue with another payment method"; empty until then.
 *
 * Guarded by `:not([hidden])` rather than declaring `display` outright: a bare
 * author-level `display` outranks the UA's `[hidden] { display: none }`, so the
 * consent rows would stand open through the amount step and setting
 * `paymentSlot.hidden` would do nothing.
 */
.express-payment-slot:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 2.6cqi, 14px);
}

.express-consent {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/*
 * `:not([hidden])` because a bare `display` here would outrank the UA's
 * `[hidden] { display: none }`, and the fractional row is hidden through the
 * property — so it would stand open for every amount, including the ones that
 * fund whole trees and need no acknowledgement at all.
 */
.express-consent-row:not([hidden]) {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.express-consent-label {
  font-size: clamp(10px, 2.8cqi, 12px);
  line-height: 1.4;
  color: #1b5035;
}

/* A one-time contribution renews nothing, so its consent row goes away. */
.express-card:has(#express-tab-one-time:checked) .express-consent-recurring {
  display: none;
}

.express-amount-error {
  margin: -4px 0 0;
  font-size: clamp(10px, 2.8cqi, 12px);
  font-weight: 500;
  color: #c33;
}

/* ── Wallet row ───────────────────────────────────────────────────────── */

/* Same `:not([hidden])` guard, and for the same reason. */
.express-wallets:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 2.2cqi, 12px);
  /* Positioning context for the consent shield below. */
  position: relative;
}

/* The divider waits for `ready`, so an unavailable wallet row collapses
   without having flashed a rule with nothing above it. */
.express-wallets:not(.express-wallets-ready) .express-wallet-divider {
  display: none;
}

/* A labelled rule, so the primary button below reads as the alternative to
   the wallets rather than a competing second CTA. */
.express-wallet-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: clamp(9px, 2.6cqi, 11px);
  color: #4a6b5a;
}

.express-wallet-divider::before,
.express-wallet-divider::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: #d5e5dc;
}

/* Unusable until consent is given.  Dimmed and inert rather than unmounted,
   so the payer can see the option exists at all. */
/* All four children share one grid cell, so the row's height is the tallest
   of them and switching tabs cannot reflow what sits below. */
.express-wallet-stack {
  display: grid;
  min-width: 0;
}

.express-wallet-stack > * {
  grid-area: 1 / 1;
  min-width: 0;
}

/* Hidden but laid out, so an inactive mount stays measurable. */
.express-wallet-element,
.express-wallet-skeleton {
  visibility: hidden;
}

.amount-carousel-scope:has([data-interval-radio='month']:checked) .express-wallet-element[data-wallet-interval='month'],
.amount-carousel-scope:has([data-interval-radio='year']:checked) .express-wallet-element[data-wallet-interval='year'],
.amount-carousel-scope:has([data-interval-radio='one_time']:checked)
  .express-wallet-element[data-wallet-interval='one_time'] {
  visibility: visible;
}

/* Only while the showing tab has no element ready yet. */
.express-wallets-loading .express-wallet-skeleton {
  visibility: visible;
}

.express-wallet-skeleton {
  height: 44px;
  border-radius: 8px;
  background: linear-gradient(90deg, #eef1ee 25%, #e3e8e4 50%, #eef1ee 75%);
  background-size: 200% 100%;
  animation: express-wallet-shimmer 1.4s ease-in-out infinite;
}

@keyframes express-wallet-shimmer {
  from {
    background-position: 200% 0;
  }

  to {
    background-position: -200% 0;
  }
}

/* Motion is decoration here; the reserved space is the point. */
@media (prefers-reduced-motion: reduce) {
  .express-wallet-skeleton {
    animation: none;
  }
}

.express-wallets-blocked .express-wallet-element {
  pointer-events: none;
  opacity: 0.45;
}

/* Only present while blocked, and only to catch the tap: the wallet buttons
   live in a cross-origin iframe, so this is the one place a click on them can
   be intercepted and turned into the form's own validation. */
.express-wallet-shield {
  display: none;
}

/* Covers the whole row, divider included: while consent is outstanding every
   tap in here means the same thing. */
/* Covers the whole row, divider included: while consent is outstanding every
   tap in here means the same thing. */
.express-wallets-blocked .express-wallet-shield {
  display: block;
  position: absolute;
  inset: 0;
  cursor: pointer;
}

/* Required reading, per the Checkout Sessions API, and the only place the card
   states the actual charge for a typed amount. */
.express-total {
  margin: 0;
  text-align: center;
  font-family: 'Heebo', sans-serif;
  font-size: clamp(12px, 3.2cqi, 14px);
  font-weight: 600;
  color: #1b5035;
}

.express-viewport {
  position: relative;
  overflow: clip;
}

.express-viewport-sized {
  height: var(--express-panel-height);
  transition: height 180ms ease;
}

.express-panel,
.express-amount-controls {
  --express-controls-gap: clamp(12px, 3cqi, 18px);
  display: flex;
  flex-direction: column;
  gap: var(--express-controls-gap);
}

.express-panel {
  width: 100%;
  transition:
    transform 240ms ease,
    opacity 240ms ease;
}

.express-viewport-sized .express-panel,
.express-payment-panel {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
}

.express-payment-panel {
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
}

.express-payment-step .express-amount-panel {
  transform: translateX(-100%);
  opacity: 0;
  pointer-events: none;
}

.express-payment-step .express-payment-panel {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

.express-card:dir(rtl) .express-payment-panel {
  transform: translateX(-100%);
}

.express-card:dir(rtl).express-payment-step .express-amount-panel {
  transform: translateX(100%);
}

.express-card:dir(rtl).express-payment-step .express-payment-panel {
  transform: translateX(0);
}

.express-summary {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid #d5e5dc;
}

.express-summary .express-total {
  text-align: start;
  font-size: clamp(16px, 4cqi, 20px);
}

.express-summary-caption {
  margin: 5px 0 0;
  font-size: 13px;
  color: #4a7160;
}

.express-summary-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  min-height: 44px;
  padding: 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #1b5035;
  font: inherit;
  cursor: pointer;
}

.express-summary-back:disabled {
  opacity: 0.5;
  cursor: default;
}

.express-card:dir(rtl) .express-back-arrow {
  transform: scaleX(-1);
}

.express-summary-one-time,
.express-card:has([data-interval-radio='one_time']:checked) .express-summary-recurring {
  display: none;
}

.express-card:has([data-interval-radio='one_time']:checked) .express-summary-one-time {
  display: inline;
}

@media (prefers-reduced-motion: reduce) {
  .express-panel,
  .express-viewport-sized {
    transition: none;
  }
}

/* Collapses before the element mounts, so the slot's gap does not open a hole
   above the payment fields. */
#express-contact-details:empty {
  display: none;
}

/* Marks the element Stripe named in its first validation error. */
.express-element-invalid {
  outline: 2px solid #c33;
  outline-offset: 3px;
  border-radius: 6px;
}

/* Dimmed on the same condition as the wallet buttons, and by the same amount,
   so the two read as one set of options rather than one live and one not.
   `.express-continue-blocked` is deliberately not `disabled`: pressing it is
   how the payer is told which field is blocking them.

   One rule for both states because they overlap while the card loads, and two
   dimming treatments would not match. */
.express-continue:disabled,
.express-continue-blocked {
  opacity: 0.45;
}

/* ── More options ─────────────────────────────────────────────────────── */

/* A disclosure, so the default view keeps its two decisions — how much, and
   how to pay — and these sit one deliberate click away. */
/* Sits in the link row, so it sheds the button chrome and leaves the
   typography to `.billing-help-link` — which it also carries.  Only the
   family and weight are inherited: `font: inherit` would reset the font-size
   that class sets, and the button would end up a different size from the
   links either side of it. */
.express-more-options-toggle {
  border: none;
  background: none;
  padding: 0;
  font-family: inherit;
  font-weight: inherit;
  line-height: inherit;
  cursor: pointer;
}

.express-more-options-body:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 10px;
  font-size: clamp(10px, 2.8cqi, 12px);
}

.express-option-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.express-option-label {
  flex: 0 0 auto;
  color: #4a6b5a;
}

.express-option-select,
.express-option-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 8px;
  border: 1px solid #d5e5dc;
  border-radius: 6px;
  background: #ffffff;
  font-family: 'Inter', sans-serif;
  font-size: inherit;
  color: #1b5035;
}

.express-option-button {
  flex: 0 0 auto;
  padding: 6px 12px;
  border: 1px solid #2c8658;
  border-radius: 6px;
  background: #ffffff;
  font-family: 'Inter', sans-serif;
  font-size: inherit;
  font-weight: 600;
  color: #2c8658;
  cursor: pointer;
}

.express-option-button:disabled,
.express-option-select:disabled,
.express-option-input:disabled {
  opacity: 0.5;
  cursor: default;
}

.express-option-link {
  border: none;
  background: none;
  padding: 0;
  font-family: 'Inter', sans-serif;
  font-size: inherit;
  color: #2c8658;
  text-decoration: underline;
  cursor: pointer;
}

/* Amber rather than red: nothing has gone wrong, the visitor is being asked.
   `:not([hidden])` because a bare `display` outranks the UA's `[hidden]` rule
   and the prompt would stand there on load asking about nothing. */
.express-confirm-discard:not([hidden]) {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.express-confirm-discard {
  margin: 0;
  color: #8a6d3b;
}

.express-discount-error {
  margin: 0;
  font-weight: 500;
  color: #c33;
}

.express-wallet-retry-notice {
  margin: 0;
  padding: 12px 16px;
  border-left: 3px solid #43735b;
  border-radius: 4px;
  background: #eef5f0;
  color: #254734;
  font-size: 14px;
  line-height: 1.5;
}

.express-wallet-retry-notice:empty {
  display: none;
}

/* ── Gift intro ───────────────────────────────────────────────────────── */

/* One gap from the heading down to the custom amount.  Gift cards are
   one-time rungs with no ribbon or bonus badge, so the card list keeps only
   the selected card's 4px focus outline of its reserved padding. */
.express-amount-controls:has(> .express-gift-intro) {
  --express-gift-gap: calc(var(--express-controls-gap) + 4px);
}

.express-amount-controls:has(> .express-gift-intro) .sliding-scale-cards-stack {
  padding-top: 0;
}

.express-amount-controls:has(> .express-gift-intro) .sliding-scale-cards {
  padding-block: 4px;
}

.express-gift-intro {
  display: grid;
  gap: var(--express-gift-gap);
  /* The cards' faint borders read as lower than they sit, so the gap below needs more space to look even. */
  margin-block-end: 4px;
}

.express-gift-heading {
  margin: 0;
  font-family: 'Heebo', sans-serif;
  font-size: 1.25em;
  font-weight: 700;
  line-height: 1.25;
  color: #1b5035;
}

.express-gift-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2cqi;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: gift-step;
}

.express-gift-steps li {
  position: relative;
  counter-increment: gift-step;
  display: grid;
  justify-items: center;
  gap: 1cqi;
  text-align: center;
  font-size: 0.875em;
  font-weight: 500;
  color: #1b5035;
}

.express-gift-steps li::before {
  content: counter(gift-step);
  display: grid;
  place-items: center;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  background: #1b5035;
  color: #fff;
  font-weight: 600;
}

/* Connector runs from this badge to the next one, at badge mid-height. */
.express-gift-steps li:not(:last-child)::after {
  content: '';
  position: absolute;
  inset-block-start: 1em;
  inset-inline-start: calc(50% + 1.5em);
  width: calc(100% + 2cqi - 3em);
  height: 1px;
  background: rgb(27 80 53 / 25%);
}

.express-gift-explainer {
  margin: 0;
  padding: 2.5cqi 3cqi;
  border-radius: 12px;
  background: rgb(27 80 53 / 6%);
  font-size: 0.875em;
  color: #1b5035;
}

@container (max-width: 420px) {
  .express-gift-steps {
    grid-template-columns: 1fr;
  }

  .express-gift-steps li:not(:last-child)::after {
    display: none;
  }

  .express-gift-steps li {
    grid-template-columns: auto 1fr;
    justify-items: start;
    align-items: center;
    column-gap: 0.75em;
    text-align: left;
  }
}
