/* ==========================================================================
   Zeetius Payments — shared design system
   --------------------------------------------------------------------------
   One stylesheet, two surfaces:

     PAYER  (default)  light, calm, single-focus. Someone is mid-transaction
                       on a phone and anxious about money. Clarity over flair.
     ADMIN  (.zp-admin on <html>) dark navy. Long-session monitoring tool.

   Both share the same brand, scale, motion and component vocabulary, so a
   button, a pill or a spinner means the same thing on either surface.

   Mobile-first throughout: base styles target the phone, min-width queries
   scale up. Do not add max-width queries here.
   ========================================================================== */

/* ---------------------------------------------------------------- 1. Reset */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
    font-family: var(--zp-font);
    background: var(--zp-bg);
    color: var(--zp-ink);
    font-size: 1rem;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    min-height: 100vh;
    min-height: 100dvh;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }

/* --------------------------------------------------------------- 2. Tokens */

:root {
    /* Type ------------------------------------------------------------- */
    --zp-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
    --zp-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* Fixed rem scale, ratio ~1.2. Product UI reads at consistent DPI;
       fluid clamp headings shrink badly inside panels and sidebars. */
    --zp-text-xs:   0.75rem;   /* 12px — meta, table headers          */
    --zp-text-sm:   0.8125rem; /* 13px — dense UI, table cells        */
    --zp-text-base: 0.9375rem; /* 15px — body                         */
    --zp-text-md:   1.0625rem; /* 17px — lead, card titles            */
    --zp-text-lg:   1.375rem;  /* 22px — page titles                  */
    --zp-text-xl:   1.75rem;   /* 28px — KPI values                   */
    --zp-text-2xl:  2.25rem;   /* 36px — the amount. The focal point. */

    /* Space — 4px base, varied deliberately for rhythm ------------------ */
    --zp-1: 0.25rem;  --zp-2: 0.5rem;   --zp-3: 0.75rem;  --zp-4: 1rem;
    --zp-5: 1.25rem;  --zp-6: 1.5rem;   --zp-8: 2rem;     --zp-10: 2.5rem;
    --zp-12: 3rem;    --zp-16: 4rem;

    /* Radius ----------------------------------------------------------- */
    --zp-r-sm: 8px;  --zp-r: 12px;  --zp-r-lg: 16px;  --zp-r-xl: 22px;
    --zp-r-pill: 999px;

    /* Motion — 150-250ms. Users are in flow; no choreography. ----------- */
    --zp-ease: cubic-bezier(0.22, 1, 0.36, 1);        /* ease-out-quint  */
    --zp-ease-soft: cubic-bezier(0.33, 1, 0.68, 1);   /* ease-out-cubic  */
    --zp-fast: 150ms;
    --zp-mid: 220ms;
    --zp-slow: 380ms;

    /* Z-index — semantic scale. Never write a raw 9999. ---------------- */
    --zp-z-base: 1;    --zp-z-sticky: 100;  --zp-z-backdrop: 200;
    --zp-z-modal: 300; --zp-z-toast: 400;   --zp-z-tooltip: 500;

    /* ---- PAYER surface (default) ------------------------------------- */
    /* Near-white with a whisper of the brand hue — not cream, not gray. */
    --zp-bg:      oklch(0.990 0.004 227);
    --zp-surface: #ffffff;
    --zp-sunken:  oklch(0.968 0.006 227);
    --zp-line:    oklch(0.912 0.008 227);
    --zp-line-strong: oklch(0.858 0.012 227);

    --zp-ink:     oklch(0.255 0.028 254);   /* headings, amounts        */
    --zp-ink-2:   oklch(0.400 0.022 254);   /* body                     */
    --zp-muted:   oklch(0.552 0.018 254);   /* meta — 4.9:1 on white    */

    /* Brand. #22a8e0 is the identity, but it only hits 2.4:1 on white,
       so actions use a darkened sibling and #22a8e0 stays the glow. */
    --zp-brand:      oklch(0.560 0.108 232);  /* #0a76a8 — 5.0:1 on white */
    --zp-brand-lift: oklch(0.505 0.105 232);  /* hover/active             */
    --zp-brand-glow: oklch(0.700 0.130 227);  /* #22a8e0 — rings, fills   */
    --zp-brand-wash: oklch(0.965 0.022 227);  /* tinted backgrounds       */
    --zp-accent:     oklch(0.760 0.150 68);   /* #f5a623 — amber, sparing */

    --zp-ok:    oklch(0.545 0.135 158);
    --zp-ok-wash: oklch(0.955 0.035 158);
    --zp-bad:   oklch(0.545 0.190 25);
    --zp-bad-wash: oklch(0.960 0.030 25);
    --zp-warn:  oklch(0.620 0.140 62);
    --zp-warn-wash: oklch(0.962 0.038 68);
    --zp-info:  oklch(0.560 0.130 258);

    --zp-on-brand: #ffffff;

    --zp-shadow-sm: 0 1px 2px oklch(0.255 0.028 254 / 0.06),
                    0 1px 3px oklch(0.255 0.028 254 / 0.05);
    --zp-shadow:    0 2px 4px oklch(0.255 0.028 254 / 0.04),
                    0 8px 24px oklch(0.255 0.028 254 / 0.08);
    --zp-shadow-lg: 0 4px 8px oklch(0.255 0.028 254 / 0.05),
                    0 20px 48px oklch(0.255 0.028 254 / 0.12);
    --zp-ring: 0 0 0 3px oklch(0.700 0.130 227 / 0.32);
}

/* ---- ADMIN surface ---------------------------------------------------- */
/* Existing committed identity: #0b1020 / #22a8e0 / #f5a623. Preserved. */
.zp-admin {
    --zp-bg:      #0b1020;
    --zp-surface: #161f42;
    --zp-sunken:  #111834;
    --zp-panel:   #111834;
    --zp-line:    #25315f;
    --zp-line-strong: #33427a;

    --zp-ink:     #eaf0ff;
    --zp-ink-2:   #c3cdec;
    --zp-muted:   #9aa7d0;   /* 6.4:1 on --zp-surface */

    --zp-brand:      #22a8e0;
    --zp-brand-lift: #43bcef;
    --zp-brand-glow: #22a8e0;
    --zp-brand-wash: rgba(34, 168, 224, 0.12);
    --zp-accent:     #f5a623;

    --zp-ok:    #2fd598;  --zp-ok-wash:   rgba(47, 213, 152, 0.14);
    --zp-bad:   #ff6b6b;  --zp-bad-wash:  rgba(255, 107, 107, 0.14);
    --zp-warn:  #ffa94d;  --zp-warn-wash: rgba(255, 169, 77, 0.14);
    --zp-info:  #8a6bff;

    --zp-on-brand: #04121b;

    --zp-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
    --zp-shadow:    0 8px 28px rgba(0, 0, 0, 0.38);
    --zp-shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.50);
    --zp-ring: 0 0 0 3px rgba(34, 168, 224, 0.38);
}

/* ----------------------------------------------------------- 3. Typography */

h1, h2, h3, h4 { line-height: 1.22; font-weight: 700; text-wrap: balance; }
h1 { font-size: var(--zp-text-lg); letter-spacing: -0.018em; }
h2 { font-size: var(--zp-text-md); letter-spacing: -0.012em; }
h3 { font-size: var(--zp-text-base); letter-spacing: -0.008em; }

p { text-wrap: pretty; }

.zp-lead    { font-size: var(--zp-text-md); color: var(--zp-ink-2); }
.zp-body    { font-size: var(--zp-text-base); color: var(--zp-ink-2); max-width: 68ch; }
.zp-meta    { font-size: var(--zp-text-sm); color: var(--zp-muted); }
.zp-fine    { font-size: var(--zp-text-xs); color: var(--zp-muted); }
.zp-mono    { font-family: var(--zp-mono); font-size: var(--zp-text-sm); }
.zp-nums    { font-variant-numeric: tabular-nums; }

/* The amount. On a payment screen this is the one thing the eye must find. */
.zp-amount {
    font-size: var(--zp-text-2xl);
    font-weight: 700;
    letter-spacing: -0.028em;
    line-height: 1.1;
    color: var(--zp-ink);
    font-variant-numeric: tabular-nums;
}
.zp-amount .zp-cur {
    font-size: 0.52em;
    font-weight: 600;
    color: var(--zp-muted);
    letter-spacing: 0;
    vertical-align: 0.34em;
    margin-right: 0.14em;
}

/* -------------------------------------------------------------- 4. Layout */

/* Centered single-focus stage — every payer screen uses this. */
.zp-stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
    padding: var(--zp-5) var(--zp-4);
    padding-top: max(var(--zp-5), env(safe-area-inset-top));
    padding-bottom: max(var(--zp-5), env(safe-area-inset-bottom));
}
@media (min-width: 640px) { .zp-stage { padding: var(--zp-10) var(--zp-6); } }

.zp-sheet {
    width: 100%;
    max-width: 25rem;          /* 400px — a payment sheet, not a page */
    background: var(--zp-surface);
    border: 1px solid var(--zp-line);
    border-radius: var(--zp-r-xl);
    box-shadow: var(--zp-shadow);
    padding: var(--zp-8) var(--zp-5) var(--zp-6);
    text-align: center;
}
@media (min-width: 640px) { .zp-sheet { padding: var(--zp-10) var(--zp-8) var(--zp-8); } }

.zp-sheet--wide { max-width: 32rem; }

.zp-stack > * + * { margin-top: var(--zp-3); }
.zp-stack-lg > * + * { margin-top: var(--zp-6); }

/* Merchant / product logo above the sheet content */
.zp-logo { max-height: 2.5rem; width: auto; margin: 0 auto var(--zp-6); }

/* Key/value receipt rows — flex, not a table, so they wrap on narrow phones */
.zp-rows {
    border-top: 1px solid var(--zp-line);
    margin-top: var(--zp-6);
    padding-top: var(--zp-4);
    text-align: left;
}
.zp-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--zp-4);
    padding: var(--zp-2) 0;
    font-size: var(--zp-text-sm);
}
.zp-row dt { color: var(--zp-muted); flex: none; }
.zp-row dd {
    color: var(--zp-ink);
    font-weight: 600;
    text-align: right;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/* Trust footer — small, honest, never a fake badge */
.zp-secured {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--zp-2);
    margin-top: var(--zp-6);
    font-size: var(--zp-text-xs);
    color: var(--zp-muted);
}
.zp-secured svg { width: 0.875rem; height: 0.875rem; flex: none; }

/* ---------------------------------------------------------- 5. Components */

.zp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--zp-2);
    min-height: 3rem;                /* 48px — comfortable primary thumb target */
    padding: var(--zp-3) var(--zp-5);
    border: 1px solid transparent;
    border-radius: var(--zp-r-lg);   /* 16px — softer, modern */
    background: var(--zp-brand);
    color: var(--zp-on-brand);
    font-size: var(--zp-text-base);
    font-weight: 600;
    letter-spacing: -0.006em;
    cursor: pointer;
    text-align: center;
    box-shadow: 0 1px 2px oklch(0 0 0 / 0.10),
                0 6px 16px var(--zp-brand-wash);
    transition: background var(--zp-fast) var(--zp-ease),
                box-shadow var(--zp-fast) var(--zp-ease),
                transform var(--zp-fast) var(--zp-ease),
                opacity var(--zp-fast) var(--zp-ease);
}
/* Icons inside buttons stay glyph-sized — without this, an unsized <svg> in a
   full-width flex button expands to fill (the "giant lock" bug). */
.zp-btn svg { width: 1.125rem; height: 1.125rem; flex: none; }
.zp-btn:hover {
    background: var(--zp-brand-lift);
    transform: translateY(-1px);
    box-shadow: 0 2px 4px oklch(0 0 0 / 0.12),
                0 10px 24px var(--zp-brand-wash);
}
.zp-btn:focus-visible { outline: none; box-shadow: var(--zp-ring); }
.zp-btn:active { transform: translateY(0); box-shadow: 0 1px 2px oklch(0 0 0 / 0.12); }
.zp-btn[disabled], .zp-btn[aria-disabled="true"] {
    opacity: 0.5; cursor: not-allowed; pointer-events: none;
}
.zp-btn--block { width: 100%; }
.zp-btn--ghost {
    background: transparent;
    border-color: var(--zp-line-strong);
    color: var(--zp-ink);
}
.zp-btn--ghost:hover { background: var(--zp-sunken); }
/* Quiet button — a calm text action (e.g. Cancel). Self-resetting so it looks
   right even when used standalone, without the base .zp-btn class. */
.zp-btn--quiet {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--zp-1);
    min-height: 2.25rem;
    padding: var(--zp-2) var(--zp-4);
    -webkit-appearance: none;
    appearance: none;
    border: 0;
    border-radius: var(--zp-r-pill);
    background: transparent;
    color: var(--zp-muted);
    font-size: var(--zp-text-sm);
    font-weight: 600;
    letter-spacing: -0.006em;
    cursor: pointer;
    transition: background var(--zp-fast) var(--zp-ease),
                color var(--zp-fast) var(--zp-ease);
}
.zp-btn--quiet:hover { background: var(--zp-brand-wash); color: var(--zp-brand); }
.zp-btn--quiet:focus-visible { outline: none; box-shadow: var(--zp-ring); }
.zp-btn--quiet:active { background: var(--zp-sunken); }

/* Button loading state — label stays, spinner replaces it in place */
.zp-btn[data-loading="true"] { pointer-events: none; opacity: 0.85; }
.zp-btn[data-loading="true"] .zp-btn-label { visibility: hidden; }
.zp-btn[data-loading="true"]::after {
    content: "";
    position: absolute;
    width: 1.125rem; height: 1.125rem;
    border-radius: 50%;
    border: 2px solid currentColor;
    border-top-color: transparent;
    animation: zp-spin 700ms linear infinite;
}
.zp-btn { position: relative; }

.zp-link {
    color: var(--zp-brand);
    font-weight: 600;
    border-bottom: 1px solid transparent;
    transition: border-color var(--zp-fast) var(--zp-ease);
}
.zp-link:hover { border-bottom-color: currentColor; }
.zp-link:focus-visible { outline: none; box-shadow: var(--zp-ring); border-radius: 3px; }

/* Status pill */
.zp-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--zp-1);
    font-size: var(--zp-text-xs);
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: var(--zp-r-pill);
    border: 1px solid currentColor;
    line-height: 1.5;
    white-space: nowrap;
}
.zp-pill--ok   { color: var(--zp-ok);   background: var(--zp-ok-wash); }
.zp-pill--bad  { color: var(--zp-bad);  background: var(--zp-bad-wash); }
.zp-pill--warn { color: var(--zp-warn); background: var(--zp-warn-wash); }
.zp-pill--info { color: var(--zp-info); background: var(--zp-brand-wash); }

/* Inline notice — full border + tinted bg. Never a side stripe. */
.zp-notice {
    display: flex;
    gap: var(--zp-3);
    text-align: left;
    padding: var(--zp-3) var(--zp-4);
    border: 1px solid var(--zp-line);
    border-radius: var(--zp-r);
    background: var(--zp-sunken);
    font-size: var(--zp-text-sm);
    color: var(--zp-ink-2);
}
.zp-notice--bad  { border-color: var(--zp-bad);  background: var(--zp-bad-wash);  color: var(--zp-ink); }
.zp-notice--warn { border-color: var(--zp-warn); background: var(--zp-warn-wash); color: var(--zp-ink); }
.zp-notice--info { border-color: var(--zp-brand-glow); background: var(--zp-brand-wash); color: var(--zp-ink); }

/* Form field */
.zp-field { text-align: left; }
.zp-field label {
    display: block;
    font-size: var(--zp-text-sm);
    font-weight: 600;
    color: var(--zp-ink);
    margin-bottom: var(--zp-2);
}
.zp-input {
    width: 100%;
    min-height: 2.75rem;
    padding: var(--zp-2) var(--zp-3);
    background: var(--zp-surface);
    border: 1px solid var(--zp-line-strong);
    border-radius: var(--zp-r-sm);
    font-size: 1rem;                  /* 16px — stops iOS zoom-on-focus */
    transition: border-color var(--zp-fast) var(--zp-ease),
                box-shadow var(--zp-fast) var(--zp-ease);
}
.zp-input::placeholder { color: var(--zp-muted); }
.zp-input:hover { border-color: var(--zp-brand-glow); }
.zp-input:focus { outline: none; border-color: var(--zp-brand); box-shadow: var(--zp-ring); }
.zp-input[disabled] { background: var(--zp-sunken); color: var(--zp-muted); cursor: not-allowed; }
.zp-input[aria-invalid="true"] { border-color: var(--zp-bad); }

/* ------------------------------------------------- 6. Status marks (SVG) */
/* Drawn with stroke-dashoffset. Base state is fully drawn and visible, so
   a headless renderer or a paused tab still shows a complete mark. */

.zp-mark {
    width: 4.5rem; height: 4.5rem;
    margin: 0 auto var(--zp-5);
    border-radius: 50%;
    display: grid;
    place-items: center;
    position: relative;
}
.zp-mark svg { width: 2.25rem; height: 2.25rem; overflow: visible; }
.zp-mark svg path, .zp-mark svg circle, .zp-mark svg line {
    fill: none;
    stroke: currentColor;
    stroke-width: 3.25;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: var(--zp-len, 60);
    stroke-dashoffset: 0;                       /* visible by default */
    animation: zp-draw var(--zp-slow) var(--zp-ease) backwards;
}
.zp-mark--ok   { color: var(--zp-ok);   background: var(--zp-ok-wash); }
.zp-mark--bad  { color: var(--zp-bad);  background: var(--zp-bad-wash); }
.zp-mark--warn { color: var(--zp-warn); background: var(--zp-warn-wash); }
.zp-mark--info { color: var(--zp-brand); background: var(--zp-brand-wash); }

/* Second stroke of an X trails the first */
.zp-mark svg line:nth-of-type(2) { animation-delay: 120ms; }

/* A soft ring that expands once on arrival — confirmation, not decoration */
.zp-mark::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid currentColor;
    animation: zp-ring-out 900ms var(--zp-ease) forwards;
}

@keyframes zp-draw {
    from { stroke-dashoffset: var(--zp-len, 60); }
    to   { stroke-dashoffset: 0; }
}
@keyframes zp-ring-out {
    from { opacity: 0.55; transform: scale(1); }
    to   { opacity: 0;    transform: scale(1.45); }
}

/* --------------------------------------------------------- 7. Loaders */
/* All loaders inherit currentColor so they read correctly on any
   merchant-supplied background — never hardcode a track color. */

/* Ring — the default "working on it" */
.zp-spinner {
    width: var(--zp-size, 2.75rem);
    height: var(--zp-size, 2.75rem);
    border-radius: 50%;
    border: var(--zp-stroke, 3px) solid;
    border-color: color-mix(in oklch, currentColor 18%, transparent);
    border-top-color: currentColor;
    animation: zp-spin 800ms linear infinite;
    margin: 0 auto;
    color: var(--zp-brand);
}
.zp-spinner--lg { --zp-size: 4rem; --zp-stroke: 4px; }
.zp-spinner--sm { --zp-size: 1.25rem; --zp-stroke: 2px; }

@keyframes zp-spin { to { transform: rotate(360deg); } }

/* Payment mark — a ring that orbits a steady center. Reads as
   "your money is being handled", not "the page is stuck". */
.zp-processing {
    width: 5rem; height: 5rem;
    margin: 0 auto var(--zp-5);
    position: relative;
    color: var(--zp-brand);
}
.zp-processing::before,
.zp-processing::after {
    content: "";
    position: absolute;
    border-radius: 50%;
}
.zp-processing::before {
    inset: 0;
    border: 3px solid color-mix(in oklch, currentColor 16%, transparent);
    border-top-color: currentColor;
    animation: zp-spin 1100ms cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
.zp-processing::after {
    inset: 1.375rem;
    background: currentColor;
    opacity: 0.16;
    animation: zp-pulse 1800ms var(--zp-ease-soft) infinite;
}
.zp-processing svg {
    position: absolute;
    inset: 0; margin: auto;
    width: 1.75rem; height: 1.75rem;
    stroke: currentColor; fill: none; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
@keyframes zp-pulse {
    0%, 100% { transform: scale(1);    opacity: 0.16; }
    50%      { transform: scale(1.18); opacity: 0.06; }
}

/* Waiting dots — for "confirming with your bank", where duration is unknown */
.zp-dots { display: inline-flex; gap: 0.3rem; align-items: center; }
.zp-dots i {
    width: 0.4rem; height: 0.4rem;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.3;
    animation: zp-dot 1200ms var(--zp-ease-soft) infinite;
}
.zp-dots i:nth-child(2) { animation-delay: 160ms; }
.zp-dots i:nth-child(3) { animation-delay: 320ms; }
@keyframes zp-dot {
    0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
    30%           { opacity: 1;   transform: translateY(-3px); }
}

/* Indeterminate bar — for redirects, where the wait is short and bounded */
.zp-bar {
    height: 3px;
    width: 100%;
    border-radius: var(--zp-r-pill);
    background: color-mix(in oklch, currentColor 14%, transparent);
    overflow: hidden;
    color: var(--zp-brand);
}
.zp-bar::after {
    content: "";
    display: block;
    height: 100%;
    width: 40%;
    border-radius: inherit;
    background: currentColor;
    animation: zp-slide 1400ms var(--zp-ease-soft) infinite;
}
@keyframes zp-slide {
    from { transform: translateX(-100%); }
    to   { transform: translateX(350%); }
}

/* Determinate bar — set --zp-pct */
.zp-bar--set::after {
    width: var(--zp-pct, 0%);
    animation: none;
    transform: none;
    transition: width var(--zp-mid) var(--zp-ease);
}

/* Skeletons — content placeholders. Product rule: skeletons, not spinners,
   inside content that already has a known shape. */
.zp-skel {
    background: linear-gradient(90deg,
        var(--zp-sunken) 0%,
        color-mix(in oklch, var(--zp-sunken) 60%, var(--zp-line)) 50%,
        var(--zp-sunken) 100%);
    background-size: 200% 100%;
    border-radius: var(--zp-r-sm);
    animation: zp-shimmer 1400ms linear infinite;
}
.zp-skel--text { height: 0.75rem; margin: 0.4rem 0; }
.zp-skel--row  { height: 2.5rem; margin-bottom: var(--zp-2); border-radius: var(--zp-r-sm); }
@keyframes zp-shimmer { to { background-position: -200% 0; } }

/* Live indicator — a dot that actually means "receiving data" */
.zp-live { display: inline-flex; align-items: center; gap: var(--zp-2); color: var(--zp-ok); }
.zp-live b {
    width: 0.5rem; height: 0.5rem; border-radius: 50%;
    background: currentColor; position: relative; flex: none;
}
.zp-live b::after {
    content: ""; position: absolute; inset: 0; border-radius: 50%;
    background: currentColor;
    animation: zp-ping 1600ms var(--zp-ease) infinite;
}
@keyframes zp-ping {
    from { opacity: 0.7; transform: scale(1); }
    to   { opacity: 0;   transform: scale(2.6); }
}

/* Entrance for the payment sheet — one reveal, not a page sequence.
   Base state is visible; the animation only softens arrival. */
.zp-enter { animation: zp-enter var(--zp-slow) var(--zp-ease) backwards; }
@keyframes zp-enter {
    from { opacity: 0; transform: translateY(8px) scale(0.985); }
    to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------ 8. Reduced motion */
/* Not optional. Every loop becomes a static or gently-fading equivalent so
   "still working" is still legible without movement. */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* Restore a non-moving "in progress" signal for the loaders. */
    .zp-spinner,
    .zp-processing::before {
        animation: zp-fade 1.4s ease-in-out infinite alternate !important;
        animation-duration: 1.4s !important;
        animation-iteration-count: infinite !important;
    }
    .zp-bar::after {
        animation: zp-fade 1.4s ease-in-out infinite alternate !important;
        animation-duration: 1.4s !important;
        animation-iteration-count: infinite !important;
        width: 100%;
        transform: none;
    }
    .zp-dots i {
        animation: zp-fade 1.4s ease-in-out infinite alternate !important;
        animation-duration: 1.4s !important;
        animation-iteration-count: infinite !important;
    }
    .zp-live b::after { display: none; }
    .zp-mark::after { display: none; }
    .zp-skel { animation: none !important; background: var(--zp-sunken); }
}
@keyframes zp-fade { from { opacity: 0.42; } to { opacity: 1; } }

/* --------------------------------------------------------- 9. Utilities */

.zp-center { text-align: center; }
.zp-hide { display: none !important; }
.zp-sr {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.zp-mt-4 { margin-top: var(--zp-4); }
.zp-mt-6 { margin-top: var(--zp-6); }
.zp-mt-8 { margin-top: var(--zp-8); }

:where(a, button, [tabindex]):focus-visible {
    outline: none;
    box-shadow: var(--zp-ring);
    border-radius: var(--zp-r-sm);
}

@media print {
    .zp-stage { min-height: auto; padding: 0; }
    .zp-sheet { box-shadow: none; border: 1px solid #ccc; max-width: 100%; }
    .zp-btn, .zp-secured, .zp-bar, .zp-spinner { display: none !important; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Occasion campaign ribbon — festive, date-ranged theming overlaid on the
   payer screens (loading / success / failed). --zp-occ = campaign accent.
   ───────────────────────────────────────────────────────────────────────── */
.zp-occasion {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--zp-2);
    max-width: min(92vw, 30rem);
    margin: 0 auto var(--zp-5);
    padding: 0.5rem 0.95rem 0.5rem 0.5rem;
    border-radius: var(--zp-r-pill);
    font: 600 var(--zp-text-sm)/1 var(--zp-font);
    letter-spacing: 0.01em;
    color: #fff;
    overflow: hidden;
    isolation: isolate;
    background: linear-gradient(135deg,
        var(--zp-occ, #f5a623),
        color-mix(in oklab, var(--zp-occ, #f5a623) 62%, #000) 135%);
    box-shadow:
        0 8px 22px color-mix(in oklab, var(--zp-occ, #f5a623) 40%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.28);
    animation: zp-occ-in var(--zp-slow) var(--zp-ease) both;
}
.zp-occasion__img {
    width: 1.65rem;
    height: 1.65rem;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(255, 255, 255, 0.22);
    flex: none;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);
}
.zp-occasion__emoji { font-size: 1.15rem; line-height: 1; flex: none; }
.zp-occasion__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@keyframes zp-occ-in {
    from { opacity: 0; transform: translateY(-10px) scale(0.96); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .zp-occasion { animation: none; }
}

/* ─────────────────────────────────────────────────────────────────────────
   10. Checkout composition — merchant identity, order summary, trust row.
   The payer needs someone to trust, a verifiable amount, and honest signals.
   ───────────────────────────────────────────────────────────────────────── */

/* Merchant identity — logo + name above the amount. */
.zp-merchant {
    display: flex; align-items: center; justify-content: center;
    gap: var(--zp-3); margin-bottom: var(--zp-6);
}
.zp-merchant__logo {
    width: 2.5rem; height: 2.5rem; border-radius: var(--zp-r);
    object-fit: cover; flex: none;
    background: var(--zp-brand-wash); color: var(--zp-brand);
    display: grid; place-items: center; font-weight: 800; font-size: var(--zp-text-md);
    box-shadow: inset 0 0 0 1px var(--zp-line);
}
.zp-merchant__name { text-align: left; line-height: 1.25; }
.zp-merchant__name b { display: block; font-size: var(--zp-text-base); color: var(--zp-ink); font-weight: 700; }
.zp-merchant__name span { font-size: var(--zp-text-xs); color: var(--zp-muted); }

.zp-eyebrow {
    font-size: var(--zp-text-xs); color: var(--zp-muted);
    font-weight: 600; letter-spacing: 0.02em; margin-bottom: var(--zp-2);
}

/* Order summary — itemized, verifiable. A bordered block, not loose rows. */
.zp-summary {
    border: 1px solid var(--zp-line); border-radius: var(--zp-r);
    background: var(--zp-sunken); margin-top: var(--zp-6);
    padding: var(--zp-1) var(--zp-4); text-align: left;
}
.zp-summary .zp-row {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--zp-4); padding: var(--zp-3) 0; font-size: var(--zp-text-sm);
    border-bottom: 1px solid var(--zp-line);
}
.zp-summary .zp-row:last-child { border-bottom: 0; }
.zp-summary .zp-row dt { color: var(--zp-muted); flex: none; }
.zp-summary .zp-row dd {
    color: var(--zp-ink); font-weight: 600; text-align: right;
    font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.zp-row--total dd { font-size: var(--zp-text-md); }

/* Trust row — several honest signals, never one fake badge. */
.zp-trust {
    display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
    gap: var(--zp-2) var(--zp-4); margin-top: var(--zp-6);
    padding-top: var(--zp-5); border-top: 1px solid var(--zp-line);
}
.zp-trust__item { display: inline-flex; align-items: center; gap: 0.35rem; font-size: var(--zp-text-xs); color: var(--zp-muted); }
.zp-trust__item svg { width: 0.85rem; height: 0.85rem; flex: none; }
.zp-trust__item b { color: var(--zp-ink-2); font-weight: 600; }
.zp-cards { display: inline-flex; gap: 0.3rem; align-items: center; }
.zp-cards span {
    width: 1.7rem; height: 1.1rem; border-radius: 3px; background: var(--zp-surface);
    box-shadow: inset 0 0 0 1px var(--zp-line); font-size: 0.5rem; font-weight: 800;
    color: var(--zp-muted); display: grid; place-items: center; letter-spacing: -0.02em;
}
