/* ==============================================================================
   PalestineX — Mobile-First Dark Cyberpunk Design System
   Palette: Background #070709 | Surface #111115 | Primary #149954 | Danger #e4312b
   ============================================================================== */

@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');

:root {
    --bg-main: #070709;
    --bg-surface: #111115;
    --primary: #149954;
    --primary-hover: #128347;
    --danger: #e4312b;
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-top: env(safe-area-inset-top, 0px);
}

/*
 * Digit-only face (unicode-range 0-9). Letters never use this file —
 * they fall through to Source Sans Pro / Rubik / Roboto / Hind Siliguri.
 */
@font-face {
    font-family: 'ZenDigits';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(https://fonts.gstatic.com/l/font?kit=mFT0WbgRxKvF_Z5eQMO9sx0R2npLoe9j6Qge1L4&skey=183ef205570cc250&v=v11) format('woff2');
    unicode-range: U+0030-0039;
}
@font-face {
    font-family: 'ZenDigits';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url(https://fonts.gstatic.com/l/font?kit=mFT0WbgRxKvF_Z5eQMO9sx0R2npLoe9j6Qge1L4&skey=183ef205570cc250&v=v11) format('woff2');
    unicode-range: U+0030-0039;
}
@font-face {
    font-family: 'ZenDigits';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url(https://fonts.gstatic.com/l/font?kit=mFT0WbgRxKvF_Z5eQMO9sx0R2npLoe9j6Qge1L4&skey=183ef205570cc250&v=v11) format('woff2');
    unicode-range: U+0030-0039;
}
@font-face {
    font-family: 'ZenDigits';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(https://fonts.gstatic.com/l/font?kit=mFT0WbgRxKvF_Z5eQMO9sx0R2npLoe9j6Qge1L4&skey=183ef205570cc250&v=v11) format('woff2');
    unicode-range: U+0030-0039;
}
@font-face {
    font-family: 'ZenDigits';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url(https://fonts.gstatic.com/l/font?kit=mFT0WbgRxKvF_Z5eQMO9sx0R2npLoe9j6Qge1L4&skey=183ef205570cc250&v=v11) format('woff2');
    unicode-range: U+0030-0039;
}

/* Fluid Typography — letters stay on the existing UI stack */
html {
    font-family: 'ZenDigits', 'Source Sans Pro', 'Rubik', 'Roboto', 'Hind Siliguri', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background-color: var(--bg-main);
    color: #f3f4f6;
    scroll-behavior: smooth;
    -webkit-tap-highlight-color: transparent;
    text-size-adjust: 100%;
}

.font-mono {
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
}

.font-digits,
.pay-value,
.dep-num-text,
.cb-code,
.cb-n {
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

/* Fluid Headings */
h1, .text-fluid-h1 {
    font-size: clamp(1.75rem, 6vw, 3.5rem);
    line-height: 1.15;
}

h2, .text-fluid-h2 {
    font-size: clamp(1.35rem, 4.5vw, 2.25rem);
    line-height: 1.25;
}

h3, .text-fluid-h3 {
    font-size: clamp(1.15rem, 3.5vw, 1.75rem);
}

/* Hide native scrollbars on chip rows */
.no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}
.no-scrollbar::-webkit-scrollbar {
    display: none;
}

[x-cloak] {
    display: none !important;
}

/* Compact header / admin chrome — do not inflate nav chips */
header a,
header button,
.admin-subnav a,
.admin-ui header a,
.admin-ui header button {
    min-height: 0 !important;
}

/* User-facing forms stay tappable; admin tables stay compact.
   The toast's dismiss button is deliberately excluded: it is a 16px glyph
   inside a ~27px pill, and stretching it to a 44px tap target made the whole
   toast exactly twice as tall on the public site (53.76px) as in the admin
   console (26.88px), where body.admin-ui already exempts it. */
body:not(.admin-ui) button:not(.zv-toast__close),
body:not(.admin-ui) input,
body:not(.admin-ui) select,
body:not(.admin-ui) textarea,
.touch-target {
    min-height: 44px;
}

.admin-ui main input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="number"]),
.admin-ui main select,
.admin-ui main textarea {
    min-height: 42px;
}

.admin-ui main button,
.admin-ui main a.inline-flex {
    min-height: 34px;
}

.admin-ui main table button,
.admin-ui main table a,
.admin-ui main td button,
.admin-ui main td a,
.admin-ui main td input[type="number"] {
    min-height: 32px !important;
    height: auto;
    width: auto;
    max-width: 100%;
}

.admin-ui main td input[type="number"] {
    width: 5.5rem;
    padding: 0.25rem 0.5rem;
}

.admin-ui main td[data-label="Actions"],
.admin-ui main td[data-label="Update"] {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: flex-end;
    align-items: center;
}

/* iOS zoom prevention: minimum 16px font-size on form controls */
input, select, textarea {
    font-size: 16px !important;
}

/* No Horizontal Overflow at any viewport */
body {
    overflow-x: hidden;
    position: relative;
    min-height: 100vh;
    padding-bottom: calc(64px + var(--safe-bottom)); /* Clearance for mobile bottom nav */
}

@media (min-width: 1024px) {
    body {
        padding-bottom: 0;
    }
}

/* Glassmorphism & Borders */
.glass-panel {
    background: rgba(17, 17, 21, 0.85);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid rgba(255, 255, 255, 0.08);
}

.glass-panel-glow {
    background: rgba(17, 17, 21, 0.9);
    border: 1px solid rgba(20, 153, 84, 0.3);
    box-shadow: 0 0 25px rgba(20, 153, 84, 0.15);
}

/* Button Tap Ripples & Active state */
.btn-primary {
    background-color: var(--primary);
    color: #ffffff;
    transition: transform 0.1s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}

.btn-primary:active {
    transform: scale(0.96);
}

/* Mobile Bottom Navigation Bar */
.bottom-nav {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: calc(64px + var(--safe-bottom));
    padding-bottom: var(--safe-bottom);
    background: rgba(11, 11, 15, 0.95);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    z-index: 50;
}

/* Floating Telegram Button (Above Bottom Nav on Mobile) */
.telegram-floating {
    position: fixed;
    right: 16px;
    bottom: calc(64px + var(--safe-bottom) + 12px);
    z-index: 49;
}

@media (min-width: 1024px) {
    .telegram-floating {
        bottom: 24px;
        right: 24px;
    }
}

/* Table Card-Stack on Mobile */
@media (max-width: 767px) {
    .mobile-card-table thead {
        display: none;
    }
    .mobile-card-table, 
    .mobile-card-table tbody, 
    .mobile-card-table tr, 
    .mobile-card-table td {
        display: block;
        width: 100%;
    }
    .mobile-card-table tr {
        background: #111115;
        border: 1px solid rgba(255, 255, 255, 0.08);
        border-radius: 1rem;
        padding: 1rem;
        margin-bottom: 0.75rem;
    }
    .mobile-card-table td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0.5rem 0;
        border-bottom: 1px solid rgba(255, 255, 255, 0.04);
        text-align: right;
    }
    .mobile-card-table td:last-child {
        border-bottom: none;
    }
    .mobile-card-table td[data-label="Actions"],
    .mobile-card-table td[data-label="Update"] {
        flex-wrap: wrap;
        gap: 0.5rem;
        justify-content: flex-end;
        align-items: center;
    }
    .mobile-card-table td[data-label="Actions"]::before,
    .mobile-card-table td[data-label="Update"]::before {
        width: 100%;
        margin-bottom: 0.35rem;
    }
    .mobile-card-table td[data-label="Actions"] button,
    .mobile-card-table td[data-label="Actions"] a,
    .mobile-card-table td[data-label="Update"] button {
        min-height: 34px !important;
        flex: 1 1 auto;
    }
    .mobile-card-table td::before {
        content: attr(data-label);
        font-weight: 600;
        font-size: 0.75rem;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: #9ca3af;
        text-align: left;
        padding-right: 0.5rem;
    }
}

/* Mobile Bottom Sheet Modal */
.bottom-sheet {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    max-height: 90vh;
    border-top-left-radius: 1.5rem;
    border-top-right-radius: 1.5rem;
    background: #111115;
    border: 1px solid rgba(255, 255, 255, 0.1);
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    z-index: 60;
    overflow-y: auto;
    padding-bottom: calc(1rem + var(--safe-bottom));
}

.bottom-sheet.open {
    transform: translateY(0);
}

@media (min-width: 768px) {
    .bottom-sheet {
        position: relative;
        max-width: 32rem;
        margin: auto;
        border-radius: 1.5rem;
        transform: scale(0.95);
        opacity: 0;
        transition: transform 0.2s ease, opacity 0.2s ease;
    }
    .bottom-sheet.open {
        transform: scale(1);
        opacity: 1;
    }
}

/* Shimmer Loading Skeleton */
@keyframes shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.skeleton {
    background: linear-gradient(90deg, rgba(255,255,255,0.03) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.03) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
    border-radius: 0.5rem;
}

/* Pulsing Badge Indicator */
@keyframes pulseGlow {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.5; transform: scale(1.15); }
}

.pulse-dot {
    animation: pulseGlow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

/* Homepage floating mail bubbles */
.mail-orbits {
    overflow: hidden;
}

.mail-bubble {
    position: absolute;
    left: var(--x);
    top: var(--y);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 9999px;
    color: rgba(16, 185, 129, 0.9);
    background: rgba(20, 153, 84, 0.12);
    border: 1px solid rgba(20, 153, 84, 0.28);
    box-shadow: 0 0 16px rgba(20, 153, 84, 0.18);
    animation: mailFloat var(--dur, 10s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
}

.mail-bubble svg {
    width: 11px;
    height: 11px;
}

.mail-bubble-2,
.mail-bubble-5,
.mail-bubble-8 {
    width: 18px;
    height: 18px;
    color: rgba(255, 255, 255, 0.72);
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.12);
}

.mail-bubble-2 svg,
.mail-bubble-5 svg,
.mail-bubble-8 svg {
    width: 9px;
    height: 9px;
}

.mail-bubble-3,
.mail-bubble-6 {
    width: 26px;
    height: 26px;
}

.mail-bubble-3 svg,
.mail-bubble-6 svg {
    width: 12px;
    height: 12px;
}

@keyframes mailFloat {
    0% { transform: translate3d(0, 0, 0) rotate(0deg); }
    25% { transform: translate3d(10px, -14px, 0) rotate(8deg); }
    50% { transform: translate3d(-8px, 12px, 0) rotate(-6deg); }
    75% { transform: translate3d(12px, 8px, 0) rotate(4deg); }
    100% { transform: translate3d(0, 0, 0) rotate(0deg); }
}

@media (min-width: 768px) {
    .mail-bubble {
        width: 26px;
        height: 26px;
    }
    .mail-bubble svg {
        width: 12px;
        height: 12px;
    }
}

/* Respect Reduced Motion */
@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;
    }
}

/* Auth buttons: spinner only after click */
.auth-btn .auth-spin { display: none; width: 18px; height: 18px; }
.auth-btn.is-loading .auth-spin { display: block; }
.auth-btn.is-loading .auth-label { opacity: 0.85; }
.auth-btn.is-loading { pointer-events: none; opacity: 0.9; }

/* Full-screen PalestineX deposit redirect loader */
.zv-loader {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 99999;
    background: rgba(7, 7, 9, 0.94);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 18px;
}
.zv-loader.show { display: flex; }
.zv-loader-ring {
    width: 128px;
    height: 128px;
    border-radius: 999px;
    border: 3px solid rgba(20, 153, 84, 0.18);
    border-top-color: #149954;
    animation: zvSpin 0.9s linear infinite;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 40px rgba(20, 153, 84, 0.25);
}
.zv-loader-ring span {
    font-weight: 800;
    letter-spacing: 0.08em;
    color: #fff;
    font-size: 13px;
}
@keyframes zvSpin { to { transform: rotate(360deg); } }

/* Purchase success / overlay */
.purchase-overlay {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(8px);
    display: none;
    align-items: center;
    justify-content: center;
    padding: 16px;
}
.purchase-overlay.open { display: flex; }

.copy-burst { animation: copyBurst 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes copyBurst {
    0% { transform: scale(1); }
    40% { transform: scale(1.08); filter: brightness(1.3); }
    100% { transform: scale(1); }
}

.site-banner {
    background: linear-gradient(90deg, rgba(20,153,84,0.18), rgba(20,153,84,0.05));
    border-bottom: 1px solid rgba(20,153,84,0.25);
    color: #d1fae5;
    font-size: 12px;
    text-align: center;
    padding: 8px 16px;
}

/* ==============================================================================
   MOTION SYSTEM
   Top progress bar, particles, reveals, micro-interactions.
   Everything below animates only transform/opacity to stay on the compositor.
   ============================================================================== */

/* ------------------------------------------------------------------------------
   1. Thin top page-loading progress bar (NProgress-style)
   Fixed, 2px, never affects layout flow.
   ----------------------------------------------------------------------------- */
.zv-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 100%;
    z-index: 9999;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s ease, height 0.25s ease;
    background: transparent;
}

.zv-progress.is-active {
    opacity: 1;
}

.zv-progress.is-done {
    opacity: 0;
}

.zv-progress__bar {
    height: 100%;
    width: 0%;
    border-radius: 0 2px 2px 0;
    background: linear-gradient(90deg, #0d6b3a 0%, #149954 45%, #34d399 75%, #a7f3d0 100%);
    box-shadow: 0 0 10px rgba(20, 153, 84, 0.7), 0 0 22px rgba(20, 153, 84, 0.35);
    transition: width 0.2s ease-out, opacity 0.2s ease;
    will-change: width;
}

.zv-progress__bar::after {
    content: "";
    display: block;
    height: 100%;
    width: 40px;
    border-radius: 2px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.85), transparent);
    animation: zvProgressShimmer 1.1s linear infinite;
}

@keyframes zvProgressShimmer {
    from { transform: translateX(-60px); }
    to   { transform: translateX(320px); }
}

/* ------------------------------------------------------------------------------
   2. Ambient upward green particles
   Purely decorative: no pointer events, no layout impact, no overflow.
   ----------------------------------------------------------------------------- */
.zv-particles {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    contain: strict;
    z-index: 0;
}

.zv-particle {
    position: absolute;
    bottom: -6px;
    width: 2px;
    height: 2px;
    border-radius: 9999px;
    background: #34d399;
    box-shadow: 0 0 4px rgba(52, 211, 153, 0.9);
    opacity: 0;
    will-change: transform, opacity;
    animation-name: zvParticleRise;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

@keyframes zvParticleRise {
    0%   { opacity: 0;   transform: translate3d(0, 0, 0) scale(0.6); }
    12%  { opacity: var(--peak, 0.5); }
    70%  { opacity: var(--peak, 0.5); }
    100% { opacity: 0;   transform: translate3d(var(--drift, 14px), -108vh, 0) scale(1); }
}

/* ------------------------------------------------------------------------------
   3. Compact premium stat cards (replaces oversized floating icons)
   ----------------------------------------------------------------------------- */
.zv-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.625rem;
    max-width: 44rem;
    margin-inline: auto;
    padding-inline: 1rem;
}

@media (min-width: 640px) {
    .zv-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.875rem; padding-inline: 0; }
}

.zv-stat {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.75rem 0.875rem;
    border-radius: 0.875rem;
    background: linear-gradient(160deg, rgba(20, 153, 84, 0.07), rgba(17, 17, 21, 0.85));
    border: 1px solid rgba(255, 255, 255, 0.07);
    overflow: hidden;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1),
                border-color 0.28s ease,
                box-shadow 0.28s ease,
                background 0.28s ease;
    will-change: transform;
}

/* Soft green sheen that sweeps across on hover */
.zv-stat::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(120% 80% at 0% 0%, rgba(20, 153, 84, 0.22), transparent 60%);
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

/* Top hairline accent */
.zv-stat::after {
    content: "";
    position: absolute;
    top: 0;
    left: 12%;
    right: 12%;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(52, 211, 153, 0.65), transparent);
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

.zv-stat:hover,
.zv-stat:focus-within {
    transform: translateY(-3px);
    border-color: rgba(20, 153, 84, 0.42);
    box-shadow: 0 10px 28px -12px rgba(20, 153, 84, 0.5);
}

.zv-stat:hover::before,
.zv-stat:hover::after { opacity: 1; }

.zv-stat__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.625rem;
    background: rgba(20, 153, 84, 0.12);
    border: 1px solid rgba(20, 153, 84, 0.22);
    color: #34d399;
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), background 0.3s ease;
}

.zv-stat__icon svg { width: 1rem; height: 1rem; }

.zv-stat:hover .zv-stat__icon {
    transform: scale(1.08) rotate(-4deg);
    background: rgba(20, 153, 84, 0.2);
}

.zv-stat__body { min-width: 0; }

.zv-stat__value {
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-weight: 700;
    font-size: 0.9375rem;
    line-height: 1.15;
    color: #fff;
    letter-spacing: -0.01em;
    white-space: nowrap;
}

.zv-stat__value.is-accent { color: #34d399; }

.zv-stat__label {
    margin-top: 0.125rem;
    font-size: 0.625rem;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    font-weight: 600;
    color: #6b7280;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ------------------------------------------------------------------------------
   4. Live Dispatch Visualizer — the signature animated homepage element
   ----------------------------------------------------------------------------- */
.zv-flow {
    position: relative;
    border-radius: 1.25rem;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background:
        radial-gradient(90% 120% at 100% 0%, rgba(20, 153, 84, 0.14), transparent 60%),
        linear-gradient(160deg, #101014, #0b0b0e);
    padding: 1.25rem;
    overflow: hidden;
    box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.9);
}

@media (min-width: 640px) { .zv-flow { padding: 1.5rem; } }

.zv-flow__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1.125rem;
}

.zv-flow__title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #9ca3af;
}

.zv-flow__live {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.625rem;
    letter-spacing: 0.08em;
    color: #34d399;
    padding: 0.25rem 0.5rem;
    border-radius: 9999px;
    background: rgba(20, 153, 84, 0.1);
    border: 1px solid rgba(20, 153, 84, 0.25);
}

/* Track: the wire the packets travel along */
.zv-flow__track {
    position: relative;
    height: 3.5rem;
    border-radius: 0.75rem;
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.05);
    overflow: hidden;
}

.zv-flow__wire {
    position: absolute;
    left: 10%;
    right: 10%;
    top: 50%;
    height: 1px;
    transform: translateY(-50%);
    background: linear-gradient(90deg, transparent, rgba(20, 153, 84, 0.5) 20%, rgba(52, 211, 153, 0.75) 50%, rgba(20, 153, 84, 0.5) 80%, transparent);
}

/* Travelling glow along the wire */
.zv-flow__wire::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(52, 211, 153, 0.9), transparent);
    transform: translateX(-100%);
    animation: zvWireSweep 2.6s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

@keyframes zvWireSweep {
    0%   { transform: translateX(-100%); opacity: 0; }
    25%  { opacity: 1; }
    75%  { opacity: 1; }
    100% { transform: translateX(100%); opacity: 0; }
}

/* Individual packets */
.zv-flow__packet {
    position: absolute;
    top: 50%;
    width: 0.4375rem;
    height: 0.4375rem;
    margin-top: -0.21875rem;
    border-radius: 9999px;
    background: #34d399;
    box-shadow: 0 0 8px rgba(52, 211, 153, 0.9);
    opacity: 0;
    animation: zvPacketTravel var(--dur, 3s) linear infinite;
    animation-delay: var(--delay, 0s);
    will-change: transform, opacity;
}

@keyframes zvPacketTravel {
    0%   { left: 8%;  opacity: 0; transform: scale(0.7); }
    10%  { opacity: 1; transform: scale(1); }
    85%  { opacity: 1; transform: scale(1); }
    100% { left: 90%; opacity: 0; transform: scale(0.7); }
}

/* Endpoints */
.zv-flow__node {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.5rem;
    background: #0b0b0e;
    border: 1px solid rgba(20, 153, 84, 0.3);
    color: #34d399;
    z-index: 2;
}

.zv-flow__node svg { width: 0.875rem; height: 0.875rem; }

.zv-flow__node--src { left: 0.5rem; }
.zv-flow__node--dst { right: 0.5rem; }

.zv-flow__node::after {
    content: "";
    position: absolute;
    inset: -3px;
    border-radius: 0.625rem;
    border: 1px solid rgba(52, 211, 153, 0.4);
    opacity: 0;
    animation: zvNodePing 2.4s ease-out infinite;
}

@keyframes zvNodePing {
    0%   { opacity: 0.8; transform: scale(0.9); }
    70%  { opacity: 0;   transform: scale(1.5); }
    100% { opacity: 0;   transform: scale(1.5); }
}

/* Log rows that stream in */
.zv-flow__rows {
    margin-top: 0.875rem;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.625rem;
}

.zv-flow__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid rgba(255, 255, 255, 0.04);
    color: #6b7280;
    opacity: 0;
    transform: translateY(6px);
    animation: zvRowIn 4.2s ease-in-out infinite;
    animation-delay: var(--delay, 0s);
}

@keyframes zvRowIn {
    0%   { opacity: 0; transform: translateY(6px); }
    12%  { opacity: 1; transform: translateY(0); }
    80%  { opacity: 1; transform: translateY(0); }
    100% { opacity: 0; transform: translateY(-4px); }
}

.zv-flow__row .dot {
    width: 0.3125rem;
    height: 0.3125rem;
    border-radius: 9999px;
    background: #34d399;
    flex: 0 0 auto;
}

.zv-flow__row .grow { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.zv-flow__row .ok   { color: #34d399; flex: 0 0 auto; }

/* ------------------------------------------------------------------------------
   5. Button micro-interactions
   ----------------------------------------------------------------------------- */
.btn-motion {
    position: relative;
    transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1),
                box-shadow 0.24s ease,
                filter 0.24s ease,
                background-color 0.24s ease,
                border-color 0.24s ease;
    will-change: transform;
}

.btn-motion:hover { transform: translateY(-1px); }
.btn-motion:active { transform: translateY(0) scale(0.97); transition-duration: 0.08s; }

.btn-motion:disabled,
.btn-motion[aria-disabled="true"] {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
    filter: saturate(0.6);
}

.btn-primary-motion {
    box-shadow: 0 8px 22px -10px rgba(20, 153, 84, 0.85);
}

.btn-primary-motion:hover:not(:disabled) {
    box-shadow: 0 14px 30px -10px rgba(20, 153, 84, 1);
    filter: brightness(1.08);
}

/* In-button spinner, shown only while a request is in flight */
.btn-spin {
    display: none;
    width: 1rem;
    height: 1rem;
    border-radius: 9999px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    opacity: 0.85;
    animation: zvSpin 0.7s linear infinite;
    flex: 0 0 auto;
}

.btn-motion.is-loading .btn-spin { display: block; }
.btn-motion.is-loading { pointer-events: none; }

/* ------------------------------------------------------------------------------
   6. Card micro-interactions
   ----------------------------------------------------------------------------- */
.zv-card {
    position: relative;
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
                border-color 0.3s ease,
                box-shadow 0.3s ease;
    will-change: transform;
}

.zv-card:hover {
    transform: translateY(-4px);
    border-color: rgba(20, 153, 84, 0.4);
    box-shadow: 0 18px 40px -22px rgba(20, 153, 84, 0.55);
}

/* Border highlight that follows the pointer (set via --mx/--my) */
.zv-card-glow::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: radial-gradient(180px circle at var(--mx, 50%) var(--my, 0%),
                rgba(52, 211, 153, 0.55), transparent 70%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

.zv-card-glow:hover::before { opacity: 1; }

/* ------------------------------------------------------------------------------
   7. Scroll reveal (IntersectionObserver driven, no GSAP required)
   ----------------------------------------------------------------------------- */
.zv-reveal {
    opacity: 0;
    transform: translate3d(0, 18px, 0);
    transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: transform, opacity;
}

.zv-reveal.is-in {
    opacity: 1;
    transform: none;
}

.zv-reveal[data-zv-delay="1"] { transition-delay: 0.07s; }
.zv-reveal[data-zv-delay="2"] { transition-delay: 0.14s; }
.zv-reveal[data-zv-delay="3"] { transition-delay: 0.21s; }
.zv-reveal[data-zv-delay="4"] { transition-delay: 0.28s; }
.zv-reveal[data-zv-delay="5"] { transition-delay: 0.35s; }

/* ------------------------------------------------------------------------------
   8. Homepage floating mail bubbles — smoother, subtler glow
   ----------------------------------------------------------------------------- */
.mail-orbits { overflow: hidden; }

.mail-bubble {
    position: absolute;
    left: var(--x);
    top: var(--y);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 9999px;
    color: rgba(16, 185, 129, 0.9);
    background: rgba(20, 153, 84, 0.12);
    border: 1px solid rgba(20, 153, 84, 0.28);
    box-shadow: 0 0 16px rgba(20, 153, 84, 0.18);
    animation: mailFloat var(--dur, 10s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
    opacity: 0.75;
    will-change: transform, opacity;
    transition: opacity 0.6s ease;
}

.mail-bubble svg { width: 11px; height: 11px; }

.mail-bubble-2, .mail-bubble-5, .mail-bubble-8 {
    width: 18px; height: 18px;
    color: rgba(255, 255, 255, 0.72);
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.12);
}

.mail-bubble-2 svg, .mail-bubble-5 svg, .mail-bubble-8 svg { width: 9px; height: 9px; }

.mail-bubble-3, .mail-bubble-6 { width: 26px; height: 26px; }
.mail-bubble-3 svg, .mail-bubble-6 svg { width: 12px; height: 12px; }

@keyframes mailFloat {
    0%   { transform: translate3d(0, 0, 0) rotate(0deg);       opacity: 0.45; }
    25%  { transform: translate3d(10px, -14px, 0) rotate(8deg);  opacity: 0.95; }
    50%  { transform: translate3d(-8px, 12px, 0) rotate(-6deg);  opacity: 0.55; }
    75%  { transform: translate3d(12px, 8px, 0) rotate(4deg);   opacity: 0.9; }
    100% { transform: translate3d(0, 0, 0) rotate(0deg);        opacity: 0.45; }
}

@media (min-width: 768px) {
    .mail-bubble { width: 26px; height: 26px; }
    .mail-bubble svg { width: 12px; height: 12px; }
}

/* ------------------------------------------------------------------------------
   9. Bottom navigation polish
   ----------------------------------------------------------------------------- */
.bottom-nav {
    box-shadow: 0 -8px 30px -18px rgba(0, 0, 0, 0.9);
}

.bottom-nav a {
    position: relative;
    transition: color 0.2s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.bottom-nav a:active { transform: scale(0.92); }

.bottom-nav a.is-active::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 1.75rem;
    height: 2px;
    border-radius: 0 0 2px 2px;
    background: #149954;
    box-shadow: 0 0 10px rgba(20, 153, 84, 0.9);
}

/* ------------------------------------------------------------------------------
   10. Loading / empty / error state primitives
   ----------------------------------------------------------------------------- */
.zv-empty {
    display: grid;
    place-items: center;
    gap: 0.625rem;
    padding: 2.25rem 1rem;
    text-align: center;
    border: 1px dashed rgba(255, 255, 255, 0.1);
    border-radius: 1rem;
    background: rgba(255, 255, 255, 0.015);
}

.zv-empty__icon {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 0.875rem;
    background: rgba(20, 153, 84, 0.1);
    border: 1px solid rgba(20, 153, 84, 0.2);
    color: #34d399;
}

.zv-empty__icon svg { width: 1.25rem; height: 1.25rem; }

.zv-skeleton {
    border-radius: 0.75rem;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0.03) 25%,
                rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.03) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.5s infinite;
}

/* Shake used for inline validation errors */
@keyframes zvShake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-5px); }
    40% { transform: translateX(5px); }
    60% { transform: translateX(-3px); }
    80% { transform: translateX(3px); }
}

.zv-shake { animation: zvShake 0.4s ease; }

/* Success pulse for credited deposits */
@keyframes zvSuccessPop {
    0%   { transform: scale(0.94); opacity: 0; }
    60%  { transform: scale(1.02); }
    100% { transform: scale(1); opacity: 1; }
}

.zv-success-pop { animation: zvSuccessPop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* ------------------------------------------------------------------------------
   11. Supporting keyframes referenced from app.js
   ----------------------------------------------------------------------------- */
.zv-ripple {
    position: absolute;
    border-radius: 9999px;
    background: currentColor;
    opacity: 0.18;
    pointer-events: none;
    transform: scale(0);
    animation: zvRippleOut 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    will-change: transform, opacity;
}

@keyframes zvRippleOut {
    from { transform: scale(0);   opacity: 0.22; }
    to   { transform: scale(1);   opacity: 0; }
}

@keyframes zvGlowBreathe {
    0%, 100% { transform: scale(1);    opacity: 0.85; }
    50%      { transform: scale(1.09); opacity: 1; }
}

/* ------------------------------------------------------------------------------
   13. Responsive refinements
   Balanced headings, scrollable data tables, container discipline.
   ----------------------------------------------------------------------------- */

/* Avoid orphaned words in headings / short marketing copy */
h1, h2, h3, .hero-title, .hero-sub {
    text-wrap: balance;
}

/* Long body copy should not stretch into unreadable line lengths on wide screens */
p { text-wrap: pretty; }

/* Data tables scroll inside their own container instead of pushing the page wide.
   The wrapper is added by .table-scroll on the page views. */
.table-scroll {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.table-scroll > table {
    min-width: 640px;
}

@media (max-width: 639px) {
    .table-scroll > table { min-width: 0; }
}

/* Never let a long unbroken token (order codes, tokens, keys) break the layout */
.break-anywhere {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Preformatted blocks (API examples, credentials) scroll rather than overflow */
pre, .font-mono {
    overflow-wrap: break-word;
}

pre {
    max-width: 100%;
    overflow-x: auto;
}

/* Keep the page content clear of the fixed mobile bottom nav even on short pages */
@media (max-width: 1023px) {
    main { padding-bottom: 1.5rem; }
}

/* Desktop: a touch more breathing room on very large displays without
   letting content stretch across the full width. */
@media (min-width: 1536px) {
    .zv-stats { max-width: 46rem; }
}

/* Images and media never exceed their column */
img, svg, video, canvas, iframe {
    max-width: 100%;
}

img, video, canvas {
    height: auto;
}

/* Modals and sheets never exceed the viewport on short/landscape phones */
@media (max-height: 520px) {
    .bottom-sheet { max-height: 96vh; }
    .purchase-overlay > div { max-height: 92vh; }
}

/* Tighten the stat cards a little on the narrowest phones (320px) */
@media (max-width: 359px) {
    .zv-stat {
        padding: 0.625rem 0.625rem;
        gap: 0.5rem;
    }
    .zv-stat__icon { width: 1.75rem; height: 1.75rem; border-radius: 0.5rem; }
    .zv-stat__icon svg { width: 0.875rem; height: 0.875rem; }
    .zv-stat__value { font-size: 0.8125rem; }
    .zv-stat__label { font-size: 0.5625rem; letter-spacing: 0.04em; }
}

/* Deposit / pay wrappers already cap themselves; make sure they never exceed
   the viewport minus the safe area on very small devices. */
.dep-wrap, .pay-wrap { max-width: min(480px, 100%); }
.pay-wrap { max-width: min(440px, 100%); }

/* ------------------------------------------------------------------------------
   14. Toast / notification system - compact pill that hugs its text
   The container is sized entirely by its content: a 13px line, a 16px icon and
   ~6px of vertical padding, so it reads as a slim pill rather than a card.
   There is no min-height and no fixed height, and the padding is only as large
   as it needs to be to clear the glyphs. Long messages wrap and the pill grows
   with them, staying snug.
   ----------------------------------------------------------------------------- */

.zv-toast-stack {
    position: fixed;
    /* Sits BELOW the sticky site header (64px mobile / 80px desktop) so it can
       never overlap the logo, nav, wallet controls or the admin bar. */
    top: 4.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 70;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.375rem;
    width: max-content;
    max-width: min(92vw, 30rem);
    pointer-events: none;
    padding-top: var(--safe-top);
}

@media (min-width: 640px) {
    .zv-toast-stack { top: 5.75rem; }
}

/* The public layout puts a dismissible announcement banner ABOVE the sticky
   header. At scroll 0 that pushes the header down to 99px (mobile) / 115px
   (desktop), so the shared offsets above - which are tuned for the admin
   console, whose header is 72px with no banner - dropped the toast straight on
   top of the centre navigation. The toast itself is byte-identical to the
   admin one; only its clearance differs, because the two headers genuinely
   differ. Scrolled down, the banner is gone and this simply leaves a slightly
   roomier gap. */
body:not(.admin-ui) .zv-toast-stack {
    top: 6.5rem;   /* 104px - clears banner 35px + header 64px */
}

@media (min-width: 640px) {
    body:not(.admin-ui) .zv-toast-stack {
        top: 7.5rem;   /* 120px - clears banner 35px + header 80px */
    }
}

.zv-toast {
    --zv-accent:      #34d399;
    --zv-accent-soft: rgba(52, 211, 153, 0.14);
    --zv-border:      rgba(20, 153, 84, 0.30);
    --zv-glow:        rgba(20, 153, 84, 0.5);

    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;          /* 7px */
    max-width: 100%;

    /* The toast font is pinned to the UI sans stack rather than inheriting the
       body stack. The public layout's body stack includes Hind Siliguri, which
       has taller line metrics — inheriting it made the same toast render
       visibly taller on the main site than in the admin panel. Pinning it here
       guarantees an identical height on every page. */
    font-family: 'ZenDigits', 'Source Sans Pro', 'Rubik', 'Roboto', -apple-system,
                 BlinkMacSystemFont, 'Segoe UI', sans-serif;

    /* 6px above and below a ~16px line => a ~30px pill that hugs the text. */
    padding: 0.3125rem 0.4375rem 0.3125rem 0.375rem;
    border-radius: 0.5rem;

    background: rgba(10, 10, 14, 0.92);
    -webkit-backdrop-filter: blur(12px) saturate(150%);
    backdrop-filter: blur(12px) saturate(150%);
    border: 1px solid var(--zv-border);
    box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.85);

    color: var(--zv-accent);
    pointer-events: auto;
    cursor: default;

    opacity: 0;
    transform: translate3d(0, -0.5rem, 0) scale(0.96);
    transition:
        opacity 0.24s ease,
        transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: transform, opacity;
}

/* Visible state — data-state keeps enter/exit interruptible. */
.zv-toast[data-state="in"] {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
}

.zv-toast[data-state="out"] {
    opacity: 0;
    transform: translate3d(0, -0.375rem, 0) scale(0.97);
}

/* Brief acknowledgement when an identical message is submitted again. */
.zv-toast--pulse {
    animation: zvToastPulse 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes zvToastPulse {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    40%  { transform: translate3d(0, 0, 0) scale(1.04); }
    100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* ---- Icon: a small tinted chip, sized to the text ---- */
.zv-toast__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 1rem;    /* 16px */
    height: 1rem;
    border-radius: 0.25rem;
    background: var(--zv-accent-soft);
    color: var(--zv-accent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--zv-accent) 26%, transparent);
}

.zv-toast__icon svg {
    width: 0.5625rem;  /* 9px */
    height: 0.5625rem;
    display: block;
}

/* ---- Message: the element that sets the pill's height ---- */
.zv-toast__text {
    font-size: 0.8125rem;   /* 13px */
    line-height: 1.2;       /* compact and identical on every page */
    font-weight: 500;
    /* White, high-contrast body text. The status colour lives on the icon and
       the border, so the message itself stays maximally readable. */
    color: #f3f4f6;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
    overflow-wrap: anywhere;
    min-width: 0;
    flex: 0 1 auto;
}

/* The text reveals itself on arrival rather than simply appearing. */
.zv-toast[data-state="in"] .zv-toast__text {
    animation: zvToastTextIn 0.34s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes zvToastTextIn {
    from { opacity: 0; transform: translateY(0.2em); filter: blur(2px); }
    to   { opacity: 1; transform: none;             filter: blur(0);   }
}

.zv-toast__text strong { font-weight: 700; color: #fff; }

/* ---- Close button: icon only, no panel ---- */
.zv-toast__close {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 1rem;
    height: 1rem;
    margin-left: 0.0625rem;
    padding: 0;
    border: 0;
    border-radius: 0.25rem;
    background: transparent;
    color: currentColor;
    opacity: 0.5;
    cursor: pointer;
    transition: opacity 0.18s ease, background-color 0.18s ease, transform 0.18s ease;
}

.zv-toast__close svg {
    width: 0.5rem;  /* 8px */
    height: 0.5rem;
    display: block;
}

.zv-toast:hover .zv-toast__close { opacity: 0.95; }

.zv-toast__close:hover {
    background: color-mix(in srgb, var(--zv-accent) 16%, transparent);
    transform: scale(1.12);
}

.zv-toast__close:active { transform: scale(0.86); }
.zv-toast__close:focus-visible { outline: 2px solid var(--zv-accent); outline-offset: 1px; }

@media (pointer: coarse) {
    .zv-toast__close { position: relative; }
    .zv-toast__close::after {
        content: "";
        position: absolute;
        inset: -9px;   /* keeps a ~34px touch target */
    }
}

/* ---- Type modifiers: only accent, border, icon and motion change ---- */
.zv-toast[data-type="success"],
.zv-toast[data-type="welcome"] {
    --zv-accent:      #34d399;
    --zv-accent-soft: rgba(52, 211, 153, 0.15);
    --zv-border:      rgba(20, 153, 84, 0.34);
    --zv-glow:        rgba(20, 153, 84, 0.55);
}

.zv-toast[data-type="error"] {
    --zv-accent:      #ff6b6b;
    --zv-accent-soft: rgba(228, 49, 43, 0.16);
    --zv-border:      rgba(228, 49, 43, 0.36);
    --zv-glow:        rgba(228, 49, 43, 0.5);
}

/* "Failed" is the same visual state as "error" - same box, same icon motion. */
.zv-toast[data-type="failed"] {
    --zv-accent:      #ff6b6b;
    --zv-accent-soft: rgba(228, 49, 43, 0.16);
    --zv-border:      rgba(228, 49, 43, 0.36);
    --zv-glow:        rgba(228, 49, 43, 0.5);
}

.zv-toast[data-type="warning"] {
    --zv-accent:      #fbbf24;
    --zv-accent-soft: rgba(245, 158, 11, 0.16);
    --zv-border:      rgba(245, 158, 11, 0.34);
    --zv-glow:        rgba(245, 158, 11, 0.45);
}

.zv-toast[data-type="info"] {
    --zv-accent:      #60a5fa;
    --zv-accent-soft: rgba(59, 130, 246, 0.16);
    --zv-border:      rgba(59, 130, 246, 0.34);
    --zv-glow:        rgba(59, 130, 246, 0.45);
}

/* Pending / processing use the brand green, matching a live operation. */
.zv-toast[data-type="pending"],
.zv-toast[data-type="processing"] {
    --zv-accent:      #34d399;
    --zv-accent-soft: rgba(52, 211, 153, 0.14);
    --zv-border:      rgba(20, 153, 84, 0.30);
    --zv-glow:        rgba(20, 153, 84, 0.4);
}

/* ---- Success: a quick scale-in bloom as the check settles ---- */
.zv-toast[data-type="success"] .zv-toast__icon,
.zv-toast[data-type="welcome"] .zv-toast__icon {
    animation: zvToastIconIn 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes zvToastIconIn {
    0%   { transform: scale(0.7); box-shadow: inset 0 0 0 1px var(--zv-accent), 0 0 0 0 var(--zv-glow); }
    60%  { transform: scale(1.08); }
    100% { transform: scale(1); }
}

/* Success tick draws itself in. */
.zv-toast[data-type="success"] .zv-toast__icon svg path,
.zv-toast[data-type="welcome"] .zv-toast__icon svg path {
    stroke-dasharray: 22;
    stroke-dashoffset: 22;
    animation: zvToastTick 0.34s 0.06s cubic-bezier(0.65, 0, 0.35, 1) forwards;
}

@keyframes zvToastTick {
    to { stroke-dashoffset: 0; }
}

/* ---- Error / failed: a restrained settle. The whole toast never shakes. ---- */
.zv-toast[data-type="error"] .zv-toast__icon,
.zv-toast[data-type="failed"] .zv-toast__icon {
    animation: zvToastIconSettle 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes zvToastIconSettle {
    0%   { transform: translateX(0); }
    35%  { transform: translateX(-2px); }
    70%  { transform: translateX(1.5px); }
    100% { transform: translateX(0); }
}

/* ---- Pending / processing: a compact spinner that never grows the line ---- */
.zv-toast[data-type="pending"] .zv-toast__icon svg,
.zv-toast[data-type="processing"] .zv-toast__icon svg {
    animation: zvToastSpin 0.7s linear infinite;
    transform-origin: 50% 50%;
    will-change: transform;
}

@keyframes zvToastSpin {
    to { transform: rotate(360deg); }
}

/* Legacy hook kept so any page targeting .toast-item still behaves */
.toast-item { pointer-events: auto; }

/* Very small phones: slightly smaller type, but the stack still clears the
   sticky header (64px) so it never sits on top of the nav. */
@media (max-width: 480px) {
    /* Must still clear the 64px sticky header. An earlier value of 1.25rem put
       the toast straight back on top of the nav, which is what we fixed. */
    .zv-toast-stack { top: 4.25rem; }
    .zv-toast__text { font-size: 0.78125rem; }  /* 12.5px */
    .zv-toast { padding: 0.25rem 0.375rem 0.25rem 0.3125rem; }
}

/* Reduced motion: keep the feedback, drop the movement. Pending/processing
   must NOT go static - a motionless ring reads as "stuck", so the rotation is
   replaced by an opacity pulse. */
@media (prefers-reduced-motion: reduce) {
    .zv-toast {
        transform: none !important;
        transition: opacity 0.15s linear !important;
    }
    .zv-toast--pulse { animation: none !important; }
    .zv-toast[data-state="in"] .zv-toast__text { animation: none !important; }
    .zv-toast[data-type="success"] .zv-toast__icon,
    .zv-toast[data-type="welcome"] .zv-toast__icon,
    .zv-toast[data-type="error"] .zv-toast__icon,
    .zv-toast[data-type="failed"] .zv-toast__icon {
        animation: none !important;
    }
    .zv-toast[data-type="success"] .zv-toast__icon svg path,
    .zv-toast[data-type="welcome"] .zv-toast__icon svg path {
        stroke-dashoffset: 0;
        animation: none !important;
    }
    .zv-toast[data-type="pending"] .zv-toast__icon svg,
    .zv-toast[data-type="processing"] .zv-toast__icon svg {
        animation: zvToastOpacityPulse 1.2s ease-in-out infinite !important;
    }
    @keyframes zvToastOpacityPulse {
        0%, 100% { opacity: 0.35; }
        50%      { opacity: 1; }
    }
}

/* ------------------------------------------------------------------------------
   15. Developer API documentation portal
   ------------------------------------------------------------------------------ */

/* ---- Layout: sticky rail + reading column, never full-bleed ---- */
.api-shell {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
    max-width: 78rem;          /* balanced measure on wide monitors */
    margin-inline: auto;
}

@media (min-width: 1024px) {
    .api-shell { grid-template-columns: 15rem minmax(0, 1fr); gap: 2.5rem; }
}

.api-rail {
    position: sticky;
    top: 5.5rem;               /* clears the sticky site header */
    align-self: start;
    max-height: calc(100vh - 7rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}

@media (max-width: 1023px) {
    /* Mobile: the rail becomes a horizontally scrollable chip bar. */
    .api-rail {
        position: sticky;
        top: 3.5rem;
        z-index: 20;
        max-height: none;
        overflow: visible;
        margin-inline: -1rem;
        padding-inline: 1rem;
        background: linear-gradient(#070709 70%, transparent);
        padding-block: 0.5rem;
    }
    .api-rail__list {
        display: flex;
        gap: 0.375rem;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        padding-bottom: 2px;
    }
    .api-rail__list::-webkit-scrollbar { display: none; }
    .api-rail__item { white-space: nowrap; }
    .api-rail__link {
        display: inline-flex;
        align-items: center;
        gap: 0.375rem;
        padding: 0.375rem 0.625rem;
        border-radius: 0.5rem;
        font-size: 0.75rem;
        font-weight: 600;
        color: #9ca3af;
        background: rgba(255, 255, 255, 0.03);
        border: 1px solid transparent;
    }
    .api-rail__item.is-active .api-rail__link {
        color: #34d399;
        background: rgba(20, 153, 84, 0.12);
        border-color: rgba(20, 153, 84, 0.3);
    }
}

.api-rail__item + .api-rail__item { margin-top: 0.125rem; }

.api-rail__link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4375rem 0.625rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 500;
    color: #9ca3af;
    border-left: 2px solid transparent;
    transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}

.api-rail__link:hover { color: #fff; background: rgba(255, 255, 255, 0.04); }

.api-rail__item.is-active .api-rail__link {
    color: #34d399;
    background: rgba(20, 153, 84, 0.1);
    border-left-color: #149954;
}

/* ---- Method badges ---- */
.api-method {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 3.25rem;
    padding: 0.125rem 0.4375rem;
    border-radius: 0.3125rem;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.5;
}

.api-method[data-m="GET"]  { color: #60a5fa; background: rgba(59, 130, 246, 0.14);  border: 1px solid rgba(59, 130, 246, 0.3); }
.api-method[data-m="POST"] { color: #34d399; background: rgba(20, 153, 84, 0.14);   border: 1px solid rgba(20, 153, 84, 0.32); }
.api-method[data-m="PUT"]  { color: #fbbf24; background: rgba(245, 158, 11, 0.14);  border: 1px solid rgba(245, 158, 11, 0.3); }
.api-method[data-m="DELETE"] { color: #ff6b6b; background: rgba(228, 49, 43, 0.14); border: 1px solid rgba(228, 49, 43, 0.3); }

/* ---- Endpoint cards ---- */
.api-card {
    background: rgba(17, 17, 21, 0.7);
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: 1rem;
    overflow: hidden;
    transition: border-color 0.28s ease, box-shadow 0.28s ease, transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

.api-card:hover {
    border-color: rgba(20, 153, 84, 0.28);
    box-shadow: 0 16px 40px -26px rgba(20, 153, 84, 0.6);
}

.api-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.625rem;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    background: rgba(255, 255, 255, 0.012);
}

.api-card__path {
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.8125rem;
    font-weight: 600;
    color: #fff;
    overflow-wrap: anywhere;
    min-width: 0;
    flex: 1 1 12rem;
}

.api-card__body { padding: 1rem; }
.api-card__summary { font-size: 0.875rem; font-weight: 600; color: #f3f4f6; }
.api-card__desc { font-size: 0.8125rem; color: #9ca3af; line-height: 1.6; margin-top: 0.375rem; }

/* ---- Auth pill ---- */
.api-auth {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.1875rem 0.5rem;
    border-radius: 9999px;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.api-auth--req  { color: #fbbf24; background: rgba(245, 158, 11, 0.12); border: 1px solid rgba(245, 158, 11, 0.28); }
.api-auth--pub  { color: #34d399; background: rgba(20, 153, 84, 0.12);  border: 1px solid rgba(20, 153, 84, 0.3); }

/* ---- Collapsible sections ---- */
.api-fold { border-top: 1px solid rgba(255, 255, 255, 0.05); }
.api-fold:first-of-type { border-top: 0; }

.api-fold__btn {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.6875rem 1rem;
    background: transparent;
    border: 0;
    cursor: pointer;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #9ca3af;
    transition: color 0.2s ease, background-color 0.2s ease;
}

.api-fold__btn:hover { color: #fff; background: rgba(255, 255, 255, 0.02); }
.api-fold__btn:focus-visible { outline: 2px solid #149954; outline-offset: -2px; }

.api-fold__chev {
    width: 0.875rem;
    height: 0.875rem;
    flex: 0 0 auto;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
    color: #6b7280;
}

.api-fold__btn[aria-expanded="true"] .api-fold__chev { transform: rotate(180deg); }
.api-fold__btn[aria-expanded="true"] { color: #34d399; }

.api-fold__panel {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.api-fold__panel.is-open { grid-template-rows: 1fr; }
.api-fold__inner { overflow: hidden; min-height: 0; }
.api-fold__pad { padding: 0 1rem 1rem; }

/* ---- Parameter table ---- */
.api-params { width: 100%; border-collapse: collapse; font-size: 0.78125rem; }

.api-params th {
    text-align: left;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #6b7280;
    padding: 0 0.5rem 0.5rem 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.api-params td {
    padding: 0.5rem 0.5rem 0.5rem 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
    vertical-align: top;
    color: #9ca3af;
}

.api-params tr:last-child td { border-bottom: 0; }
.api-params code { font-size: 0.75rem; }

.api-req {
    display: inline-block;
    margin-left: 0.25rem;
    font-size: 0.5625rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    color: #ff6b6b;
    text-transform: uppercase;
    vertical-align: middle;
}

/* On narrow screens the table becomes stacked cards so nothing overflows. */
@media (max-width: 639px) {
    .api-params thead { display: none; }
    .api-params, .api-params tbody, .api-params tr, .api-params td { display: block; width: 100%; }
    .api-params tr {
        border: 1px solid rgba(255, 255, 255, 0.06);
        border-radius: 0.5rem;
        padding: 0.5rem 0.625rem;
        margin-bottom: 0.5rem;
        background: rgba(255, 255, 255, 0.015);
    }
    .api-params td { border: 0; padding: 0.125rem 0; }
    .api-params td::before {
        content: attr(data-label);
        display: block;
        font-size: 0.5625rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: #6b7280;
    }
    .api-params td:first-child::before { display: none; }
}

/* ---- Code blocks ---- */
.api-code {
    position: relative;
    border-radius: 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.07);
    background: #060608;
    overflow: hidden;
}

.api-code__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem 0.375rem 0.75rem;
    background: rgba(255, 255, 255, 0.025);
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.api-code__lang {
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #6b7280;
}

.api-code pre {
    margin: 0;
    padding: 0.75rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.75rem;
    line-height: 1.7;
    color: #d1d5db;
    tab-size: 2;
}

@media (max-width: 639px) {
    .api-code pre { font-size: 0.6875rem; padding: 0.625rem; }
}

/* Token colours */
.tok-key  { color: #7dd3fc; }
.tok-str  { color: #a5f3c4; }
.tok-num  { color: #fbbf24; }
.tok-bool { color: #c4b5fd; }
.tok-null { color: #f472b6; }
.tok-cmt  { color: #6b7280; font-style: italic; }
.tok-fn   { color: #34d399; }
.tok-var  { color: #e5e7eb; }
.tok-punc { color: #9ca3af; }

/* ---- Icon-only copy button ---- */
.api-copy {
    display: grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    flex: 0 0 auto;
    padding: 0;
    border: 0;
    border-radius: 0.375rem;
    background: transparent;
    color: #6b7280;
    cursor: pointer;
    transition: color 0.18s ease, background-color 0.18s ease, transform 0.18s ease;
}

.api-copy svg { width: 0.8125rem; height: 0.8125rem; }
.api-copy:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.api-copy:active { transform: scale(0.9); }
.api-copy:focus-visible { outline: 2px solid #149954; outline-offset: 1px; }

.api-copy.is-done {
    color: #34d399;
    background: rgba(20, 153, 84, 0.16);
    animation: apiCopyPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes apiCopyPop {
    0%   { transform: scale(0.85); }
    55%  { transform: scale(1.15); }
    100% { transform: scale(1); }
}

/* ---- Status code chips ---- */
.api-status {
    display: inline-flex;
    align-items: center;
    min-width: 2.25rem;
    justify-content: center;
    padding: 0.125rem 0.375rem;
    border-radius: 0.3125rem;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.6875rem;
    font-weight: 700;
}

.api-status[data-s="200"], .api-status[data-s="201"] { color: #34d399; background: rgba(20, 153, 84, 0.14); }
.api-status[data-s="400"], .api-status[data-s="422"] { color: #fbbf24; background: rgba(245, 158, 11, 0.14); }
.api-status[data-s="401"], .api-status[data-s="403"] { color: #ff6b6b; background: rgba(228, 49, 43, 0.14); }
.api-status[data-s="404"] { color: #f472b6; background: rgba(244, 114, 182, 0.14); }
.api-status[data-s="409"], .api-status[data-s="402"] { color: #fb923c; background: rgba(251, 146, 60, 0.14); }
.api-status[data-s="429"], .api-status[data-s="502"], .api-status[data-s="503"] { color: #60a5fa; background: rgba(59, 130, 246, 0.14); }

/* ---- Search ---- */
.api-search { position: relative; }
.api-search input {
    width: 100%;
    padding: 0.5rem 0.75rem 0.5rem 2rem;
    border-radius: 0.625rem;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: #fff;
    font-size: 0.8125rem;
    outline: none;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}
.api-search input::placeholder { color: #6b7280; }
.api-search input:focus { border-color: rgba(20, 153, 84, 0.5); background: rgba(255, 255, 255, 0.06); }
.api-search svg { position: absolute; left: 0.625rem; top: 50%; transform: translateY(-50%); width: 0.875rem; height: 0.875rem; color: #6b7280; pointer-events: none; }

.api-empty {
    display: none;
    padding: 2.5rem 1rem;
    text-align: center;
    color: #6b7280;
    font-size: 0.8125rem;
}

.api-empty.is-shown { display: block; }

/* ---- Endpoint hidden by search ---- */
.api-ep[hidden] { display: none !important; }
.api-ep { scroll-margin-top: 7rem; }

@media (prefers-reduced-motion: reduce) {
    .api-copy.is-done { animation: none; }
    .api-fold__panel { transition: none; }
    .api-card:hover { transform: none; }
}


/* ------------------------------------------------------------------------------
   19. Logout button (shared: admin panel + user area)
   Compact, premium, with a restrained red accent. The markup is a normal POST
   form, so behaviour is identical to the previous inline-styled button.
   ----------------------------------------------------------------------------- */

.zv-logout {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4375rem;
    /* Compact, not oversized: 40px tall on desktop, 44px touch target. */
    min-height: 40px;
    padding: 0.5rem 0.875rem;
    border-radius: 0.625rem;

    background: rgba(228, 49, 43, 0.08);
    border: 1px solid rgba(228, 49, 43, 0.22);
    color: #ff8a80;

    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    white-space: nowrap;

    transition:
        background-color 0.2s ease,
        border-color 0.2s ease,
        color 0.2s ease,
        transform 0.15s cubic-bezier(0.16, 1, 0.3, 1),
        box-shadow 0.2s ease;
}

.zv-logout__ico {
    width: 0.9375rem;
    height: 0.9375rem;
    flex: 0 0 auto;
    transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}

.zv-logout:hover {
    background: rgba(228, 49, 43, 0.16);
    border-color: rgba(228, 49, 43, 0.45);
    color: #ffb4ae;
    box-shadow: 0 6px 18px -10px rgba(228, 49, 43, 0.8);
}

.zv-logout:hover .zv-logout__ico { transform: translateX(2px); }

.zv-logout:active { transform: scale(0.96); }

.zv-logout:focus-visible {
    outline: 2px solid #ff6b6b;
    outline-offset: 2px;
}

@media (pointer: coarse) {
    .zv-logout { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
    .zv-logout:hover .zv-logout__ico { transform: none; }
    .zv-logout:active { transform: none; }
}

/* ------------------------------------------------------------------------------
   18b. Cursor-following green ambient glow (landing page, desktop only)
   A single fixed radial blob lerped toward the pointer. No layout impact,
   pointer-events:none, and never rendered on touch devices.
   ----------------------------------------------------------------------------- */

.zv-cursor-glow {
    position: fixed;
    top: 0;
    left: 0;
    width: 460px;
    height: 460px;
    margin: -230px 0 0 -230px;   /* centre the blob on the point */
    border-radius: 50%;
    background: radial-gradient(circle closest-side,
                rgba(20, 153, 84, 0.16),
                rgba(20, 153, 84, 0.07) 45%,
                rgba(20, 153, 84, 0) 72%);
    pointer-events: none;
    z-index: 2;
    opacity: 0;
    /* Only transform/opacity animate, so this stays on the compositor. */
    will-change: transform, opacity;
    transition: opacity 0.5s ease;
    filter: blur(18px);
}

/* Shown only once the pointer has actually moved (a mouse, not a tap). */
.zv-cursor-glow.is-active { opacity: 1; }

/* Touch / coarse pointers never get this effect. */
@media (hover: none), (pointer: coarse) {
    .zv-cursor-glow { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
    .zv-cursor-glow { display: none !important; }
}

/* ------------------------------------------------------------------------------
   18c. Floating mail icons: randomised drift + cursor repulsion
   The base positions still come from the --x/--y custom properties, so the
   existing visual style is untouched. JS adds an extra per-icon offset on top.
   ----------------------------------------------------------------------------- */

.mail-orbits { pointer-events: none; }

/* JS sets --dx/--dy for the drift, and --rx/--ry for the cursor push. */
.mail-bubble {
    translate: calc(var(--x, 50%) + var(--dx, 0px)) calc(var(--y, 50%) + var(--dy, 0px));
}

@media (prefers-reduced-motion: reduce) {
    .mail-bubble { translate: calc(var(--x, 50%) + var(--rx, 0px)) calc(var(--y, 50%) + var(--ry, 0px)); }
}

/* ------------------------------------------------------------------------------
   17. Admin mobile navigation
   A compact single-column list instead of a grid of large cards, so opening the
   menu on a phone cannot bury the dashboard content underneath it.
   ----------------------------------------------------------------------------- */

/* The nav starts hidden via the `hidden` attribute, independent of Tailwind's
   preflight, so the buttons are never permanently on screen. */
.zv-admin-menu[hidden] { display: none !important; }

/* Gentle reveal when the menu opens. Purely cosmetic. */
.zv-admin-menu:not([hidden]) {
    animation: zvAdminMenuIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
    transform-origin: top center;
}

@keyframes zvAdminMenuIn {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .zv-admin-menu:not([hidden]) { animation: none; }
}

.zv-admin-link {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    /* 40px: comfortably tappable, but a third of a 56px card row. */
    min-height: 40px;
    padding: 0.4375rem 0.625rem;
    border-radius: 0.625rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: #9ca3af;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid transparent;
    transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.zv-admin-link:hover { color: #fff; background: rgba(255, 255, 255, 0.07); }
.zv-admin-link:active { transform: scale(0.985); }
.zv-admin-link:focus-visible { outline: 2px solid #149954; outline-offset: -2px; }

.zv-admin-link__ico {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.05);
    color: #6b7280;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.zv-admin-link__ico svg { width: 0.9375rem; height: 0.9375rem; display: block; }
.zv-admin-link__label { flex: 1 1 auto; min-width: 0; }

.zv-admin-link__tick { width: 0.875rem; height: 0.875rem; flex: 0 0 auto; color: #34d399; }

.zv-admin-link.is-active {
    color: #fff;
    background: rgba(20, 153, 84, 0.16);
    border-color: rgba(20, 153, 84, 0.42);
}

.zv-admin-link.is-active .zv-admin-link__ico {
    background: rgba(20, 153, 84, 0.26);
    color: #34d399;
}

@media (prefers-reduced-motion: reduce) {
    .zv-admin-link:active { transform: none; }
}

/* ---- Per-service quick-start cards ---- */
.api-svc__badge {
    display: inline-flex;
    align-items: center;
    padding: 0.1875rem 0.5rem;
    border-radius: 0.375rem;
    background: rgba(20, 153, 84, 0.14);
    border: 1px solid rgba(20, 153, 84, 0.34);
    color: #34d399;
    font-family: 'ZenDigits', ui-monospace, monospace;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    white-space: nowrap;
    flex: 0 0 auto;
}

.api-lbl {
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #6b7280;
    margin-bottom: 0.375rem;
}

.api-hint { font-size: 0.75rem; line-height: 1.6; color: #9ca3af; }

.api-keychip {
    display: inline-block;
    padding: 0.1875rem 0.4375rem;
    border-radius: 0.3125rem;
    background: rgba(125, 211, 252, 0.09);
    border: 1px solid rgba(125, 211, 252, 0.24);
    color: #7dd3fc;
    font-size: 0.6875rem;
    overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------------------
   15b. API docs — mobile-first hardening
   Nothing on this page may cause the document to scroll sideways. Only the
   code blocks scroll internally.
   ----------------------------------------------------------------------------- */

.zv-api-doc {
    /* `clip` (not `hidden`) so it never creates a scroll container that would
       break position:sticky inside it. */
    overflow-x: clip;
    max-width: 100%;
}

/* Every text-ish node is allowed to shrink and wrap. */
.zv-api-doc h1, .zv-api-doc h2, .zv-api-doc h3,
.zv-api-doc p, .zv-api-doc li, .zv-api-doc td, .zv-api-doc th,
.zv-api-doc code, .zv-api-doc span {
    min-width: 0;
    overflow-wrap: break-word;
}

/* Long endpoint paths and URLs must never push the layout wider. */
.zv-api-doc .api-card__path,
.zv-api-doc .api-code pre code,
.zv-api-doc .api-code pre {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Pre allows internal horizontal scroll, which is the one place it is wanted. */
.zv-api-doc .api-code pre {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

@media (max-width: 639px) {
    /* Tighter gutters so 320px screens keep usable measure. */
    .zv-api-doc .api-card__head { padding: 0.75rem 0.75rem; }
    .zv-api-doc .api-card__body { padding: 0.75rem; }
    .zv-api-doc .api-fold__btn { padding: 0.625rem 0.75rem; }
    .zv-api-doc .api-fold__pad { padding: 0 0.75rem 0.75rem; }
    .zv-api-doc .api-shell { gap: 1rem; }
    .zv-api-doc .api-card__path { font-size: 0.75rem; }
    /* The method badge keeps its own line above a long path. */
    .zv-api-doc .api-card__head { flex-wrap: wrap; }
}

/* Very narrow phones: drop the code font a touch further. */
@media (max-width: 359px) {
    .zv-api-doc .api-code pre { font-size: 0.625rem; padding: 0.5rem; }
    .zv-api-doc .api-code__lang { font-size: 0.5625rem; }
}

/* ------------------------------------------------------------------------------
   16. Display-currency switcher (BDT / USDT)
   Built on <details>, so it works with no JavaScript. Small, dense pill that
   sits next to the wallet balance without crowding the header.
   ----------------------------------------------------------------------------- */

.zv-cur { position: relative; flex: 0 0 auto; }

.zv-cur__btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.375rem 0.5rem;
    min-height: 32px;
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: #d1d5db;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.zv-cur__btn::-webkit-details-marker { display: none; }
.zv-cur__btn::-moz-details-marker { display: none; }

.zv-cur__btn > svg:first-child { width: 0.8125rem; height: 0.8125rem; color: #6b7280; }
.zv-cur__chev { width: 0.625rem; height: 0.625rem; color: #6b7280; transition: transform 0.24s cubic-bezier(0.16, 1, 0.3, 1); }

.zv-cur__btn:hover {
    background: rgba(20, 153, 84, 0.1);
    border-color: rgba(20, 153, 84, 0.4);
    color: #fff;
}
.zv-cur__btn:active { transform: scale(0.96); }
.zv-cur[open] .zv-cur__chev { transform: rotate(180deg); }
.zv-cur[open] .zv-cur__btn {
    background: rgba(20, 153, 84, 0.14);
    border-color: rgba(20, 153, 84, 0.5);
    color: #34d399;
}
.zv-cur__btn:focus-visible { outline: 2px solid #149954; outline-offset: 2px; }

.zv-cur__code { line-height: 1; }

.zv-cur__menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 80;
    width: 15.5rem;
    max-width: calc(100vw - 2rem);
    padding: 0.5rem;
    border-radius: 0.875rem;
    background: rgba(14, 14, 18, 0.97);
    -webkit-backdrop-filter: blur(16px) saturate(150%);
    backdrop-filter: blur(16px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 24px 48px -24px rgba(0, 0, 0, 0.9);
    animation: zvCurIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
    transform-origin: top right;
}

/* Desktop fix: when JS promotes the menu to position:fixed it escapes any
   clipping/stacking ancestor (the wallet card, or any overflow:hidden section),
   so it can never be cut off or painted behind neighbouring content. The
   absolute rules above remain the no-JS fallback. */
.zv-cur.is-floating > .zv-cur__menu {
    position: fixed;
    right: auto;
    top: auto;
    z-index: 2000;   /* above page content and the toast stack's neighbours */
}

/* Never allow the menu to exceed the viewport, whichever mode is active. */
.zv-cur.is-floating > .zv-cur__menu,
.zv-cur__menu {
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}

@keyframes zvCurIn {
    from { opacity: 0; transform: translateY(-4px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}

/* On narrow headers the menu must not run off-screen to the right. */
@media (max-width: 420px) {
    .zv-cur__menu { right: -0.25rem; width: min(15.5rem, calc(100vw - 1.5rem)); }
}

.zv-cur__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem 0.5rem;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #6b7280;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    margin-bottom: 0.375rem;
}

.zv-cur__rate { color: #4b5563; font-family: 'ZenDigits', ui-monospace, monospace; text-transform: none; letter-spacing: 0; }

.zv-cur__form { margin: 0; }

.zv-cur__opt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.5rem;
    border: 0;
    border-radius: 0.5rem;
    background: transparent;
    color: #e5e7eb;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.18s ease, color 0.18s ease;
}

button.zv-cur__opt:hover { background: rgba(20, 153, 84, 0.14); color: #fff; }
.zv-cur__opt:focus-visible { outline: 2px solid #149954; outline-offset: -2px; }

.zv-cur__opt.is-active {
    background: rgba(20, 153, 84, 0.12);
    color: #34d399;
    cursor: default;
}

.zv-cur__opt-name { font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.02em; }
.zv-cur__opt-note { font-size: 0.6875rem; color: #6b7280; margin-left: auto; }
.zv-cur__opt.is-active .zv-cur__opt-note { color: #4b5563; }

.zv-cur__tick { width: 0.8125rem; height: 0.8125rem; color: #34d399; }

.zv-cur__auto {
    margin: 0.5rem 0.25rem 0;
    padding-top: 0.5rem;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    font-size: 0.6875rem;
    line-height: 1.5;
    color: #6b7280;
}

.zv-cur__auto--manual { color: #4b5563; }

.zv-cur__warn {
    margin: 0.5rem 0 0;
    padding: 0.5rem 0.5rem;
    border-radius: 0.5rem;
    background: rgba(245, 158, 11, 0.1);
    border: 1px solid rgba(245, 158, 11, 0.28);
    font-size: 0.6875rem;
    line-height: 1.5;
    color: #fbbf24;
}

@media (prefers-reduced-motion: reduce) {
    .zv-cur__menu { animation: none; }
    .zv-cur__btn:active { transform: none; }
}

/* ------------------------------------------------------------------------------
   11b-bis-3. Site-wide notice

   Deliberately not a modal. No backdrop, no scroll lock, no focus trap: a notice
   that traps somebody who is trying to reach their accounts has become an
   outage, which is the opposite of what a notice is for.

   The card is a fixed overlay so it appears on every page without each view
   having to include anything.
   ----------------------------------------------------------------------------- */
.zv-notice[data-state] { opacity: 0; }
.zv-notice[data-state="in"] {
    opacity: 1;
    transition: opacity .22s ease;
}
.zv-notice[data-state="out"] {
    opacity: 0;
    transition: opacity .24s ease;
}

.zv-notice__card {
    background: linear-gradient(180deg, #14141a 0%, #101014 100%);
    border-color: rgba(255, 255, 255, .12);
    color: #e5e7eb;
    transform: translateY(-14px) scale(.97);
    transform-origin: top center;
    transition: transform .28s cubic-bezier(.16, 1, .3, 1);
}
.zv-notice[data-state="in"] .zv-notice__card { transform: none; }
.zv-notice[data-state="out"] .zv-notice__card { transform: translateY(-10px) scale(.98); }

.zv-notice__accent {
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    border-radius: 3px 0 0 3px;
    background: #149954;
}
.zv-notice__icon { color: #149954; }
.zv-notice__link { color: #149954; transition: color .15s ease, background-color .15s ease; }
.zv-notice__link:hover { color: #2ecb7a; background: rgba(20, 153, 84, .1); }
.zv-notice__link:focus-visible { outline: 2px solid #149954; outline-offset: 2px; }
.zv-notice__close {
    color: #9ca3af;
    transition: background-color .15s ease, color .15s ease, transform .15s ease;
}
.zv-notice__close:hover { background: rgba(255, 255, 255, .07); color: #fff; }
.zv-notice__close:active { transform: scale(.92); }
.zv-notice__close:focus-visible { outline: 2px solid #149954; outline-offset: 2px; }

/* Type variants. Each also carries its own icon in the markup, so the meaning
   never depends on colour alone. */
.zv-notice__card[data-notice-type="warning"] { border-color: rgba(234, 179, 8, .35); }
.zv-notice__card[data-notice-type="warning"] .zv-notice__accent { background: #eab308; }
.zv-notice__card[data-notice-type="warning"] .zv-notice__icon { color: #eab308; }

.zv-notice__card[data-notice-type="success"] { border-color: rgba(20, 153, 84, .4); }
.zv-notice__card[data-notice-type="success"] .zv-notice__accent { background: #149954; }
.zv-notice__card[data-notice-type="success"] .zv-notice__icon { color: #149954; }

.zv-notice__body { color: #b6bcc7; }
.zv-notice__body a { color: #2ecb7a; text-decoration: underline; }
.zv-notice__body a:hover { color: #56d997; }

@media (prefers-reduced-motion: reduce) {
    .zv-notice,
    .zv-notice__card { transition: none !important; }
    .zv-notice__card { transform: none; }
}

/* ------------------------------------------------------------------------------
   11b-bis-2. Per-order download menu in the order history

   Each order needs its own download, so the row action is a small menu rather
   than a single link. The marker is hidden so the browser does not draw its own
   disclosure triangle over the icon.
   ----------------------------------------------------------------------------- */
.zv-dlmenu > summary { -webkit-appearance: none; appearance: none; }
.zv-dlmenu > summary::-webkit-details-marker { display: none; }
.zv-dlmenu > summary::-moz-details-marker { display: none; }
.zv-dlmenu > summary:focus-visible { outline: 2px solid #149954; outline-offset: 2px; }

.zv-dlmenu__panel {
    /* The row is inside a scrolling table, so the panel is positioned in the
       viewport rather than clipped by an ancestor overflow. */
    min-width: 11rem;
    animation: zvDlIn .14s ease-out;
}
@keyframes zvDlIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}

.zv-dlmenu__item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    padding: .5rem .75rem;
    font-size: .8125rem;
    color: #e5e7eb;
    min-height: 36px;
    transition: background-color .15s ease, color .15s ease;
}
.zv-dlmenu__item:hover { background: rgba(255, 255, 255, .05); color: #fff; }
.zv-dlmenu__item:focus-visible { outline: 2px solid #149954; outline-offset: -2px; }
.zv-dlmenu__item span {
    font-size: .6875rem;
    color: #6b7280;
    font-family: 'ZenDigits', 'JetBrains Mono', ui-monospace, monospace;
}

@media (prefers-reduced-motion: reduce) {
    .zv-dlmenu__panel { animation: none; }
}

/* ------------------------------------------------------------------------------
   11b. Desktop nav links + header balance pill
   ----------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------------
   11b-bis. Give the four header controls one common height (sm and up only)

   Measured in the browser at 640/768/1024/1280/1600px, they were 38px (balance
   pill), 32px (currency switcher, from .zv-cur__btn's own min-height), 40px
   (profile, w-10 h-10) and 33-36px (logout). Their vertical centres already
   agreed exactly, so only the heights differed.

   A single min-height is the minimal fix: it changes no padding, no colour, no
   border, no icon geometry and no animation. 40px is used because the profile
   button is already exactly 40px, so three of the four stay untouched.

   Everything is inside min-width: 640px, so the mobile header is unaffected.
   The mobile bottom navigation bar is a different component entirely and is
   not referenced here.
   ----------------------------------------------------------------------------- */
@media (min-width: 640px) {
    /* The <details> wrapper is a block by default, so the button inside it was
       not participating as a flex item of the header row. */
    .zv-cur {
        display: inline-flex;
        align-items: center;
    }

    /* !important is required, not stylistic.
       app.css has an earlier rule `header a, header button { min-height: 0
       !important; }` whose job is to stop the desktop nav chips from being
       inflated to 44px tap targets. The balance pill is an <a> inside <header>,
       so it was caught by that rule too and could never take a min-height.
       Exempting the three action controls keeps the nav behaviour exactly as
       it is and only gives the action row a shared height. */
    .balance-pill,
    .zv-cur__btn,
    .zv-logout {
        min-height: 40px !important;
    }
}

.nav-link {
    position: relative;
    transition: color 0.2s ease, background-color 0.2s ease;
}

.nav-link::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 2px;
    width: 0;
    height: 1.5px;
    border-radius: 2px;
    background: #149954;
    box-shadow: 0 0 8px rgba(20, 153, 84, 0.8);
    transform: translateX(-50%);
    transition: width 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-link:hover::after { width: 45%; }
.nav-link.is-active::after { width: 45%; }

.balance-pill {
    transition: border-color 0.25s ease, background-color 0.25s ease, transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
    display: inline-flex;
    align-items: center;
}

.balance-pill:hover { transform: translateY(-1px); }

/* Pulse the balance when it changes after a deposit */
@keyframes zvBalancePop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.12); color: #34d399; }
    100% { transform: scale(1); }
}

.balance-pill.is-updated { animation: zvBalancePop 0.7s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* ------------------------------------------------------------------------------
   12. Reduced motion — keep functionality, drop the 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;
    }

    /* Reveal content that would otherwise stay invisible */
    .zv-reveal { opacity: 1 !important; transform: none !important; }

    /* Hide decorative motion entirely */
    .zv-particle,
    .mail-orbits,
    .zv-flow__packet,
    .zv-flow__wire::after,
    .zv-flow__row,
    .zv-flow__node::after {
        display: none !important;
    }

    .zv-flow__row { opacity: 1 !important; transform: none !important; }

    /* Progress bar still shows progress, just without the shimmer sweep */
    .zv-progress__bar::after { display: none; }

    .btn-motion:hover,
    .zv-card:hover,
    .zv-stat:hover,
    .bottom-nav a:active {
        transform: none;
    }
}

/* Also respect the OS "reduce motion" setting for the Tailwind hover lifts
   that are expressed as utility classes. */
@media (prefers-reduced-motion: reduce) {
    .hover\:-translate-y-1:hover { transform: none !important; }
}
