-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathxrp.html
More file actions
75 lines (75 loc) Β· 49.1 KB
/
xrp.html
File metadata and controls
75 lines (75 loc) Β· 49.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spraay β Non-Custodial XRP Batch Payments</title>
<meta name="description" content="Pay multiple XRP recipients in batch. Non-custodial sequential payments on XRP Ledger β near-zero fees, 3-4 second finality.">
<meta property="og:title" content="Spraay β XRP Batch Payments">
<meta property="og:description" content="Send XRP to 100+ recipients. Non-custodial. Near-zero fees. 3-4 second finality.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://xrp.spraay.app">
<meta property="og:image" content="https://spraay.app/spraay-og-square.png">
<meta name="twitter:image" content="https://spraay.app/spraay-og-square.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:creator" content="@spraay_app">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root{--xrp:#00B8D4;--xrp-deep:#0097A7;--xrp-bright:#26C6DA;--xrp-glow:rgba(0,184,212,.10);--xrp-dim:rgba(0,184,212,.08);--xrp-border:rgba(0,184,212,.30);--bg:#FAFAF8;--bg-card:#FFF;--bg-card-hover:#F2FBFC;--bg-inset:#F3F1EE;--text:#1A1A18;--text-dim:#6B6960;--text-muted:#9E9A90;--border:rgba(26,26,24,.10);--green:#16a34a;--green-bg:rgba(22,163,74,.08);--green-border:rgba(22,163,74,.25);--yellow:#b45309;--yellow-bg:rgba(180,83,9,.08);--yellow-border:rgba(180,83,9,.25);--red:#dc2626;--red-bg:rgba(220,38,38,.06);--red-border:rgba(220,38,38,.20);--shadow-sm:0 1px 2px rgba(0,0,0,.04);--shadow-md:0 4px 16px rgba(0,0,0,.06);--shadow-lg:0 12px 40px rgba(0,0,0,.08)}
*{margin:0;padding:0;box-sizing:border-box}body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--text);line-height:1.6;overflow-x:hidden}code,.mono{font-family:'Space Mono',monospace}.container{max-width:1100px;margin:0 auto;padding:0 24px}html{scroll-behavior:smooth}
nav{position:fixed;top:0;width:100%;z-index:100;background:rgba(250,250,248,.92);backdrop-filter:blur(12px);border-bottom:1px solid var(--border);padding:1rem 0}.nav-inner{display:flex;justify-content:space-between;align-items:center}.logo{font-weight:800;font-size:1.4rem;letter-spacing:.08em;background:linear-gradient(135deg,var(--xrp),var(--xrp-deep));-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-decoration:none}.nav-links{display:flex;gap:1.2rem;align-items:center}.nav-links a{color:var(--text-dim);text-decoration:none;font-size:.9rem;font-weight:500;transition:color .2s}.nav-links a:hover{color:var(--text)}
.chain-dropdown{position:relative}.chain-dropdown-btn{display:flex;align-items:center;gap:.5rem;background:var(--bg-card);border:1px solid var(--xrp-border);border-radius:8px;padding:.45rem .9rem;color:var(--text);font-size:.85rem;font-weight:600;cursor:pointer;font-family:inherit;transition:all .2s;box-shadow:var(--shadow-sm)}.chain-dropdown-btn:hover{border-color:var(--xrp);background:var(--bg-card-hover)}.chain-dropdown-btn .chevron{font-size:.6rem;color:var(--text-muted);transition:transform .2s}.chain-dropdown.open .chevron{transform:rotate(180deg)}.chain-dropdown-menu{position:absolute;top:calc(100% + 6px);right:0;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:.4rem;min-width:200px;display:none;box-shadow:var(--shadow-lg);z-index:200}.chain-dropdown.open .chain-dropdown-menu{display:block}.chain-dropdown-menu a{display:flex;align-items:center;gap:.6rem;padding:.55rem .8rem;border-radius:8px;color:var(--text-dim);text-decoration:none;font-size:.85rem;font-weight:500;transition:all .15s}.chain-dropdown-menu a:hover{background:var(--xrp-glow);color:var(--text)}.chain-dropdown-menu a.current{background:var(--xrp-glow);color:var(--xrp);font-weight:600}.chain-dropdown-menu .chain-item-icon{font-size:1rem;width:20px;text-align:center}.chain-dropdown-menu .chain-item-dot{width:6px;height:6px;border-radius:50%;background:var(--green);box-shadow:0 0 4px rgba(22,163,74,.5);margin-left:auto;flex-shrink:0}
.hamburger-btn{display:none;width:40px;height:40px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;cursor:pointer;align-items:center;justify-content:center;flex-shrink:0}.hamburger-btn span{display:block;width:18px;height:2px;background:var(--text);border-radius:1px;position:relative}.hamburger-btn span::before,.hamburger-btn span::after{content:'';position:absolute;left:0;width:18px;height:2px;background:var(--text);border-radius:1px}.hamburger-btn span::before{top:-6px}.hamburger-btn span::after{top:6px}
.mobile-panel-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.3);z-index:999}.mobile-panel-overlay.open{display:block}.mobile-panel{position:fixed;top:0;right:0;bottom:0;width:280px;max-width:80vw;background:var(--bg-card);border-left:1px solid var(--border);z-index:1000;transform:translateX(100%);transition:transform .25s ease;overflow-y:auto;display:flex;flex-direction:column;box-shadow:var(--shadow-lg)}.mobile-panel.open{transform:translateX(0)}.mobile-panel-header{display:flex;justify-content:space-between;align-items:center;padding:1.2rem 1.2rem .8rem;border-bottom:1px solid var(--border)}.mobile-panel-header .panel-title{font-weight:700;font-size:1rem}.mobile-panel-close{width:36px;height:36px;border-radius:8px;background:transparent;border:1px solid var(--border);color:var(--text-dim);font-size:1.2rem;cursor:pointer;display:flex;align-items:center;justify-content:center}.mobile-panel-section{padding:.8rem .6rem}.mobile-panel-section-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;color:var(--text-muted);font-weight:600;padding:.4rem .6rem}.mobile-panel a{display:flex;align-items:center;gap:.7rem;padding:.85rem .8rem;border-radius:10px;color:var(--text-dim);text-decoration:none;font-size:.95rem;font-weight:500;min-height:48px}.mobile-panel a.current{background:var(--xrp-glow);color:var(--xrp);font-weight:600}.mobile-panel .mp-icon{font-size:1.1rem;width:24px;text-align:center}.mobile-panel .mp-dot{width:6px;height:6px;border-radius:50%;background:var(--green);box-shadow:0 0 4px rgba(22,163,74,.5);margin-left:auto;flex-shrink:0}.mobile-panel-divider{height:1px;background:var(--border);margin:.4rem .6rem}.mobile-panel-footer{margin-top:auto;padding:1rem 1.2rem;border-top:1px solid var(--border)}.mobile-panel-footer a{min-height:40px;font-size:.85rem;color:var(--text-muted);padding:.5rem .8rem}
.btn{display:inline-block;padding:.55rem 1.4rem;border-radius:8px;font-weight:600;font-size:.9rem;text-decoration:none;transition:all .25s ease;cursor:pointer;border:none;font-family:'Outfit',sans-serif}.btn-primary{background:var(--xrp);color:#fff;border:1px solid var(--xrp-bright);box-shadow:0 2px 8px rgba(0,184,212,.20)}.btn-primary:hover{background:var(--xrp-bright);transform:translateY(-1px);box-shadow:0 4px 16px rgba(0,184,212,.25)}.btn-outline{background:transparent;color:var(--xrp);border:1px solid var(--xrp-border)}.btn-outline:hover{background:var(--xrp-glow)}.btn-connect{background:linear-gradient(135deg,var(--xrp),var(--xrp-deep));color:#fff;border:1px solid var(--xrp-border);padding:.5rem 1.2rem;border-radius:8px;font-weight:600;font-size:.85rem;cursor:pointer;font-family:'Outfit',sans-serif;transition:all .25s ease;box-shadow:0 2px 8px rgba(0,184,212,.20)}.btn-connect:hover{background:linear-gradient(135deg,var(--xrp-bright),var(--xrp))}.btn-connected{background:var(--xrp-dim);border:1px solid var(--xrp-border);color:var(--xrp);cursor:pointer;box-shadow:none}
.hero{padding:10rem 0 5rem;text-align:center;background:radial-gradient(ellipse at 50% 0%,rgba(0,184,212,.06) 0%,transparent 60%);position:relative}.hero::before{content:'';position:absolute;top:60px;left:0;right:0;bottom:0;background:url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%2300B8D4' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");pointer-events:none}.hero-badge{display:inline-flex;align-items:center;gap:.5rem;background:var(--xrp-dim);border:1px solid var(--xrp-border);border-radius:20px;padding:.4rem 1rem;font-size:.82rem;font-weight:600;color:var(--xrp);margin-bottom:1.5rem;box-shadow:var(--shadow-sm)}.hero-badge .dot{width:7px;height:7px;border-radius:50%;background:var(--xrp);box-shadow:0 0 6px rgba(0,184,212,.6);animation:pulse 2s ease-in-out infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}.hero h1{font-size:clamp(2.4rem,6vw,4rem);font-weight:800;line-height:1.15;margin-bottom:1.2rem;position:relative}.hero h1 span{background:linear-gradient(135deg,var(--xrp),var(--xrp-bright));-webkit-background-clip:text;-webkit-text-fill-color:transparent}.hero p{color:var(--text-dim);font-size:1.15rem;max-width:580px;margin:0 auto 2rem;position:relative}.hero-buttons{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap;position:relative}.hero-stats{display:flex;gap:3rem;justify-content:center;margin-top:3.5rem;flex-wrap:wrap;position:relative}.stat{text-align:center}.stat-value{font-size:2rem;font-weight:700;background:linear-gradient(135deg,var(--xrp),var(--xrp-bright));-webkit-background-clip:text;-webkit-text-fill-color:transparent}.stat-label{font-size:.85rem;color:var(--text-muted);margin-top:.2rem}
.integrations{padding:3rem 0;border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:var(--bg-card)}.integrations-inner{display:flex;align-items:center;justify-content:center;gap:2rem;flex-wrap:wrap}.integrations-label{font-size:.8rem;text-transform:uppercase;letter-spacing:.12em;color:var(--text-muted);font-weight:600}.integration-badge{display:flex;align-items:center;gap:.6rem;background:var(--bg);border:1px solid var(--border);border-radius:10px;padding:.6rem 1.2rem;font-weight:600;font-size:.9rem;color:var(--text);box-shadow:var(--shadow-sm)}.integration-badge .dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 6px rgba(22,163,74,.5)}.tag{font-size:.65rem;padding:.15rem .5rem;border-radius:4px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}.tag-live{background:var(--green-bg);color:var(--green)}
.app-section{padding:5rem 0;background:radial-gradient(ellipse at 50% 0%,rgba(0,184,212,.04) 0%,transparent 50%);border-bottom:1px solid var(--border)}.app-container{max-width:720px;margin:0 auto}.app-card{background:var(--bg-card);border:1px solid var(--border);border-radius:16px;padding:2rem;position:relative;box-shadow:var(--shadow-md)}.app-card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--xrp),transparent)}.app-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.5rem}.app-header h2{font-size:1.3rem;font-weight:700}.wallet-info{display:flex;align-items:center;gap:.5rem;font-size:.8rem;color:var(--text-dim)}.wallet-dot{width:6px;height:6px;border-radius:50%;background:var(--green);box-shadow:0 0 6px rgba(22,163,74,.5)}.chain-indicator{font-size:.7rem;background:var(--xrp-dim);color:var(--xrp);padding:.15rem .5rem;border-radius:4px;font-weight:700}
.wallet-selector{margin-bottom:1.5rem}.wallet-selector-label{font-size:.82rem;font-weight:600;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em;margin-bottom:.5rem;display:block}.wallet-options{display:flex;gap:.6rem;flex-wrap:wrap}.wallet-option{flex:1;min-width:120px;padding:.8rem 1rem;border-radius:10px;background:var(--bg-inset);border:1px solid var(--border);cursor:pointer;transition:all .2s;text-align:center;font-family:'Outfit',sans-serif;font-weight:600;font-size:.88rem;color:var(--text-dim);display:flex;align-items:center;justify-content:center;gap:.5rem}.wallet-option:hover{border-color:var(--xrp-border);background:var(--bg-card-hover)}.wallet-option.active{border-color:var(--xrp);background:var(--xrp-dim);color:var(--xrp)}.wallet-option .wallet-icon{font-size:1.1rem}
.form-group{margin-bottom:1.2rem}.form-label{display:block;font-size:.82rem;font-weight:600;color:var(--text-dim);margin-bottom:.4rem;text-transform:uppercase;letter-spacing:.06em}.form-input{width:100%;padding:.75rem 1rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:10px;color:var(--text);font-family:'Outfit',sans-serif;font-size:.95rem;transition:border-color .2s;outline:none}.form-input:focus{border-color:var(--xrp-border)}.form-input::placeholder{color:var(--text-muted)}
.recipients-list{display:flex;flex-direction:column;gap:.6rem;margin-bottom:.8rem}.recipient-row{display:grid;grid-template-columns:1fr 120px 90px 32px;gap:.6rem;align-items:center;animation:fadeIn .2s ease}@keyframes fadeIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.recipient-row .addr-input{padding:.65rem .8rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'Space Mono',monospace;font-size:.78rem;outline:none;transition:border-color .2s;min-width:0}.recipient-row .addr-input:focus{border-color:var(--xrp-border)}.recipient-row .addr-input::placeholder{color:var(--text-muted);font-family:'Space Mono',monospace}.amount-wrap{position:relative;display:flex;align-items:center}.amount-wrap .amt-input{width:100%;padding:.65rem .8rem;padding-right:2.8rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'Space Mono',monospace;font-size:.82rem;outline:none;transition:border-color .2s}.amount-wrap .amt-input:focus{border-color:var(--xrp-border)}.amount-wrap .amt-suffix{position:absolute;right:.8rem;font-size:.7rem;color:var(--text-muted);font-weight:600;pointer-events:none}.tag-input{padding:.65rem .6rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'Space Mono',monospace;font-size:.78rem;outline:none;transition:border-color .2s;width:100%}.tag-input:focus{border-color:var(--xrp-border)}.tag-input::placeholder{color:var(--text-muted);font-family:'Space Mono',monospace}.remove-btn{width:32px;height:32px;border-radius:8px;background:transparent;border:1px solid var(--border);color:var(--text-muted);cursor:pointer;font-size:1rem;display:flex;align-items:center;justify-content:center;transition:all .2s;flex-shrink:0}.remove-btn:hover{background:var(--xrp-glow);border-color:var(--xrp-border);color:var(--xrp)}.add-recipient-btn{width:100%;padding:.6rem;border-radius:8px;background:transparent;border:1px dashed var(--border);color:var(--text-dim);cursor:pointer;font-size:.85rem;font-family:'Outfit',sans-serif;font-weight:600;transition:all .2s;margin-bottom:1rem}.add-recipient-btn:hover{border-color:var(--xrp-border);color:var(--xrp);background:var(--xrp-glow)}.recipient-count-badge{font-size:.72rem;color:var(--text-muted)}.recipient-count-badge strong{color:var(--text-dim)}.csv-upload{border:1px dashed var(--border);border-radius:6px;padding:.5rem .8rem;text-align:center;cursor:pointer;transition:all .2s;display:inline-flex;align-items:center;gap:.5rem}.csv-upload:hover{border-color:var(--xrp-border);background:var(--xrp-glow)}.csv-upload-text{font-size:.75rem;color:var(--text-muted)}.csv-upload input[type="file"]{display:none}
.equal-toggle{display:flex;align-items:center;gap:.6rem;margin-bottom:1.2rem;cursor:pointer}.equal-toggle input{display:none}.toggle-track{width:40px;height:22px;border-radius:11px;background:var(--border);position:relative;transition:background .2s;flex-shrink:0}.toggle-track::after{content:'';position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;background:var(--text-muted);transition:all .2s}.equal-toggle input:checked + .toggle-track{background:linear-gradient(135deg,var(--xrp),var(--xrp-deep))}.equal-toggle input:checked + .toggle-track::after{left:21px;background:#fff}.toggle-label{font-size:.85rem;color:var(--text-dim);font-weight:500}
.summary-card{background:var(--bg-inset);border:1px solid var(--border);border-radius:10px;padding:1.2rem;margin:1.2rem 0}.summary-row{display:flex;justify-content:space-between;align-items:center;padding:.4rem 0}.summary-row:not(:last-child){border-bottom:1px solid rgba(0,0,0,.05)}.summary-label{font-size:.85rem;color:var(--text-dim)}.summary-value{font-size:.85rem;font-weight:600;font-family:'Space Mono',monospace}.summary-total .summary-label{color:var(--text);font-weight:600}.summary-total .summary-value{color:var(--xrp);font-size:1rem}
.spray-btn{width:100%;padding:1rem;border-radius:12px;font-size:1.05rem;font-weight:700;cursor:pointer;background:linear-gradient(135deg,var(--xrp),var(--xrp-deep));color:#fff;border:1px solid var(--xrp-border);font-family:'Outfit',sans-serif;transition:all .25s;box-shadow:0 4px 16px rgba(0,184,212,.25)}.spray-btn:hover:not(:disabled){background:linear-gradient(135deg,var(--xrp-bright),var(--xrp));box-shadow:0 6px 24px rgba(0,184,212,.30);transform:translateY(-1px)}.spray-btn:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none}.wallet-note{text-align:center;margin-top:.6rem;font-size:.78rem;color:var(--text-muted)}.wallet-note a{color:var(--xrp);cursor:pointer;text-decoration:none}.wallet-note a:hover{text-decoration:underline}
.status-msg{padding:.8rem 1rem;border-radius:10px;font-size:.85rem;margin-top:1rem;display:none}.status-msg.show{display:block}.status-msg.success{background:var(--green-bg);border:1px solid var(--green-border);color:var(--green)}.status-msg.error{background:var(--red-bg);border:1px solid var(--red-border);color:var(--red)}.status-msg.info{background:var(--yellow-bg);border:1px solid var(--yellow-border);color:var(--yellow)}.status-msg a{color:inherit;text-decoration:underline}
.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.4);backdrop-filter:blur(6px);z-index:1000;display:none;align-items:center;justify-content:center;padding:1rem}.modal-overlay.show{display:flex}.modal{background:var(--bg-card);border:1px solid var(--xrp-border);border-radius:16px;padding:2rem;max-width:520px;width:100%;position:relative;animation:modalIn .2s ease;box-shadow:var(--shadow-lg)}@keyframes modalIn{from{opacity:0;transform:scale(.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--xrp),transparent)}.modal h3{font-size:1.2rem;font-weight:700;margin-bottom:1rem;text-align:center}.modal-summary{background:var(--bg-inset);border:1px solid var(--border);border-radius:10px;padding:1rem;margin-bottom:1rem}.modal-row{display:flex;justify-content:space-between;align-items:center;padding:.35rem 0;font-size:.88rem}.modal-row:not(:last-child){border-bottom:1px solid rgba(0,0,0,.04)}.modal-row .label{color:var(--text-dim)}.modal-row .value{font-weight:600;font-family:'Space Mono',monospace}.modal-row.total .label{color:var(--text);font-weight:600}.modal-row.total .value{color:var(--xrp);font-size:1.05rem}.modal-recipients-preview{max-height:140px;overflow-y:auto;margin-bottom:1rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:8px;padding:.6rem .8rem}.modal-recip-item{display:flex;justify-content:space-between;font-size:.75rem;padding:.2rem 0;font-family:'Space Mono',monospace;color:var(--text-dim)}.modal-recip-item .addr{color:var(--text)}.modal-buttons{display:flex;gap:.8rem;margin-top:1rem}.modal-buttons button{flex:1;padding:.8rem;border-radius:10px;font-size:.95rem;font-weight:700;cursor:pointer;font-family:'Outfit',sans-serif;transition:all .2s}.modal-cancel{background:var(--bg-inset);border:1px solid var(--border);color:var(--text-dim)}.modal-cancel:hover{border-color:var(--xrp-border);color:var(--text)}.modal-confirm{background:linear-gradient(135deg,var(--xrp),var(--xrp-deep));border:1px solid var(--xrp-border);color:#fff}.modal-confirm:hover{background:linear-gradient(135deg,var(--xrp-bright),var(--xrp));box-shadow:0 4px 16px rgba(0,184,212,.25)}.modal-warning{font-size:.75rem;color:var(--text-muted);text-align:center;margin-top:.6rem}
.section-title{font-size:1.8rem;font-weight:700;margin-bottom:.5rem}.section-subtitle{color:var(--text-dim);margin-bottom:3rem}.features{padding:5rem 0}.feature-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:1.5rem}.feature-card{background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:1.8rem;transition:all .25s ease;box-shadow:var(--shadow-sm)}.feature-card:hover{border-color:var(--xrp-border);background:var(--bg-card-hover);transform:translateY(-2px);box-shadow:var(--shadow-md)}.feature-icon{font-size:1.6rem;margin-bottom:.8rem}.feature-card h3{font-size:1.05rem;font-weight:600;margin-bottom:.4rem}.feature-card p{font-size:.88rem;color:var(--text-dim);line-height:1.5}.how-it-works{padding:5rem 0}.steps{display:flex;gap:2rem;flex-wrap:wrap;justify-content:center}.step{flex:1;min-width:220px;max-width:300px;text-align:center}.step-num{width:48px;height:48px;border-radius:50%;background:var(--xrp-dim);color:var(--xrp);font-weight:700;font-size:1.1rem;display:flex;align-items:center;justify-content:center;margin:0 auto 1rem;border:2px solid var(--xrp-border)}.step h3{font-size:1rem;font-weight:600;margin-bottom:.4rem}.step p{font-size:.85rem;color:var(--text-dim)}.use-cases{padding:5rem 0}.use-case-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:1.2rem}.use-case{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1.5rem;transition:border-color .2s;box-shadow:var(--shadow-sm)}.use-case:hover{border-color:var(--xrp-border)}.use-case h3{font-size:1rem;margin-bottom:.3rem}.use-case p{font-size:.85rem;color:var(--text-dim)}.contract-section{padding:3rem 0;text-align:center;border-top:1px solid var(--border)}.contract-address{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1rem 1.5rem;display:inline-block;margin-top:1rem;box-shadow:var(--shadow-sm)}.contract-address code{font-size:.85rem;color:var(--xrp);word-break:break-all}.cta{padding:5rem 0;text-align:center;background:radial-gradient(ellipse at 50% 100%,rgba(0,184,212,.05) 0%,transparent 60%)}.cta h2{font-size:2rem;font-weight:700;margin-bottom:.8rem}.cta p{color:var(--text-dim);margin-bottom:2rem;max-width:480px;margin-left:auto;margin-right:auto}footer{padding:2.5rem 0;border-top:1px solid var(--border)}.footer-inner{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem}.footer-links{display:flex;gap:1.5rem;flex-wrap:wrap}.footer-links a{color:var(--text-muted);text-decoration:none;font-size:.85rem;transition:color .2s}.footer-links a:hover{color:var(--text)}.footer-copy{font-size:.8rem;color:var(--text-muted)}
@media(max-width:640px){.hero{padding:8rem 0 3rem}.hero-stats{gap:2rem}.integrations-inner{flex-direction:column;gap:1rem}.nav-links a:not(.btn):not(.btn-connect):not(.btn-connected){display:none}.chain-dropdown{display:none}.hamburger-btn{display:flex}.app-card{padding:1.2rem}.recipient-row{grid-template-columns:1fr}.wallet-options{flex-direction:column}}@media(min-width:641px){.recipient-row{grid-template-columns:1fr 120px 90px 32px}}
</style>
</head>
<body>
<nav><div class="container nav-inner"><a href="/" class="logo">SPRAAY</a><div class="nav-links"><div class="chain-dropdown" id="chainDropdown"><button class="chain-dropdown-btn" onclick="toggleChainDropdown(event)"><span class="chain-icon">π§</span><span>XRP Ledger</span><span class="chevron">βΌ</span></button><div class="chain-dropdown-menu"><a href="/"><span class="chain-item-icon">π΅</span> Base<span class="chain-item-dot"></span></a><a href="/ethereum"><span class="chain-item-icon">β </span> Ethereum<span class="chain-item-dot"></span></a><a href="/arbitrum"><span class="chain-item-icon">⬑</span> Arbitrum<span class="chain-item-dot"></span></a><a href="/polygon"><span class="chain-item-icon">π£</span> Polygon<span class="chain-item-dot"></span></a><a href="/bnb"><span class="chain-item-icon">π‘</span> BNB Chain<span class="chain-item-dot"></span></a><a href="/avalanche"><span class="chain-item-icon">πΊ</span> Avalanche<span class="chain-item-dot"></span></a><a href="/unichain"><span class="chain-item-icon">π¦</span> Unichain<span class="chain-item-dot"></span></a><a href="/plasma"><span class="chain-item-icon">π’</span> Plasma<span class="chain-item-dot"></span></a><a href="/bob"><span class="chain-item-icon">πΆ</span> BOB<span class="chain-item-dot"></span></a><a href="/tao"><span class="chain-item-icon">π§ </span> Bittensor<span class="chain-item-dot"></span></a><a href="/solana"><span class="chain-item-icon">β</span> Solana<span class="chain-item-dot"></span></a><a href="/stacks"><span class="chain-item-icon">β</span> Stacks<span class="chain-item-dot"></span></a><a href="/bitcoin"><span class="chain-item-icon">βΏ</span> Bitcoin<span class="chain-item-dot"></span></a><a href="/stellar"><span class="chain-item-icon">β¦</span> Stellar<span class="chain-item-dot"></span></a><a href="/xrp" class="current"><span class="chain-item-icon">π§</span> XRP Ledger<span class="chain-item-dot"></span></a></div></div><a href="https://docs.spraay.app" target="_blank">Docs</a><button id="navConnectBtn" class="btn-connect" onclick="connectWallet('gem')">Connect Wallet</button><button class="hamburger-btn" onclick="openMobilePanel()" aria-label="Menu"><span></span></button></div></div></nav>
<div class="mobile-panel-overlay" id="mobilePanelOverlay" onclick="closeMobilePanel()"></div><div class="mobile-panel" id="mobilePanel"><div class="mobile-panel-header"><span class="panel-title">Switch Chain</span><button class="mobile-panel-close" onclick="closeMobilePanel()">β</button></div><div class="mobile-panel-section"><div class="mobile-panel-section-label">Networks</div><a href="/"><span class="mp-icon">π΅</span> Base<span class="mp-dot"></span></a><a href="/ethereum"><span class="mp-icon">β </span> Ethereum<span class="mp-dot"></span></a><a href="/arbitrum"><span class="mp-icon">⬑</span> Arbitrum<span class="mp-dot"></span></a><a href="/polygon"><span class="mp-icon">π£</span> Polygon<span class="mp-dot"></span></a><a href="/bnb"><span class="mp-icon">π‘</span> BNB Chain<span class="mp-dot"></span></a><a href="/avalanche"><span class="mp-icon">πΊ</span> Avalanche<span class="mp-dot"></span></a><a href="/unichain"><span class="mp-icon">π¦</span> Unichain<span class="mp-dot"></span></a><a href="/plasma"><span class="mp-icon">π’</span> Plasma<span class="mp-dot"></span></a><a href="/bob"><span class="mp-icon">πΆ</span> BOB<span class="mp-dot"></span></a><a href="/tao"><span class="mp-icon">π§ </span> Bittensor<span class="mp-dot"></span></a><a href="/solana"><span class="mp-icon">β</span> Solana<span class="mp-dot"></span></a><a href="/stellar"><span class="mp-icon">β¦</span> Stellar<span class="mp-dot"></span></a><a href="/stacks"><span class="mp-icon">β</span> Stacks<span class="mp-dot"></span></a><a href="/bitcoin"><span class="mp-icon">βΏ</span> Bitcoin<span class="mp-dot"></span></a><a href="/xrp" class="current"><span class="mp-icon">π§</span> XRP Ledger<span class="mp-dot"></span></a></div><div class="mobile-panel-divider"></div><div class="mobile-panel-footer"><a href="https://docs.spraay.app" target="_blank"><span class="mp-icon">π</span> Docs</a><a href="https://twitter.com/Spraay_app" target="_blank"><span class="mp-icon">π¦</span> Twitter</a><a href="https://warpcast.com/plag" target="_blank"><span class="mp-icon">π£</span> Farcaster</a></div></div>
<section class="hero"><div class="container"><div class="hero-badge"><span class="dot"></span> Non-Custodial XRP Batch Payments</div><h1>Batch Payments on<br><span>XRP Ledger</span></h1><p>Pay multiple XRP recipients in batch. Non-custodial sequential payments β near-zero fees, 3-4 second finality per payment.</p><div class="hero-buttons"><a href="#app" class="btn btn-primary">Launch App β</a><a href="https://gateway.spraay.app/api/v1/xrp/info" target="_blank" class="btn btn-outline">API Info</a></div><div class="hero-stats"><div class="stat"><div class="stat-value">~$0.0001</div><div class="stat-label">Per Transaction</div></div><div class="stat"><div class="stat-value">100</div><div class="stat-label">Max Recipients</div></div><div class="stat"><div class="stat-value">0.3%</div><div class="stat-label">Service Fee</div></div><div class="stat"><div class="stat-value">3-4s</div><div class="stat-label">Finality</div></div></div></div></section>
<section class="integrations"><div class="container integrations-inner"><span class="integrations-label">Wallet Support</span><div class="integration-badge"><span class="dot"></span> GemWallet<span class="tag tag-live">Live</span></div><div class="integration-badge"><span class="dot"></span> Crossmark<span class="tag tag-live">Live</span></div><div class="integration-badge"><span class="dot"></span> Xaman (XUMM)<span class="tag tag-live">Live</span></div><div class="integration-badge"><span class="dot"></span> xrplcluster.com<span class="tag tag-live">RPC</span></div></div></section>
<section class="app-section" id="app"><div class="container app-container"><h2 class="section-title" style="text-align:center;">Batch Send XRP</h2><p class="section-subtitle" style="text-align:center;">Pay multiple XRP addresses in batch. Your keys never leave your wallet.</p><div class="app-card"><div class="app-header"><h2>π§ Batch Send</h2><div style="display:flex;align-items:center;gap:.6rem;"><span class="chain-indicator">π§ XRP Ledger</span><div id="walletInfo" class="wallet-info" style="display:none;"><span class="wallet-dot"></span><span id="walletAddr" class="mono"></span></div></div></div><div class="wallet-selector" id="walletSelector"><span class="wallet-selector-label">Connect Wallet</span><div class="wallet-options"><div class="wallet-option" onclick="connectWallet('gem')"><span class="wallet-icon">π</span> GemWallet</div><div class="wallet-option" onclick="connectWallet('crossmark')"><span class="wallet-icon">βοΈ</span> Crossmark</div><div class="wallet-option" onclick="connectWallet('xaman')"><span class="wallet-icon">π</span> Xaman</div></div></div><div id="balanceDisplay" class="form-group" style="display:none;"><div style="display:flex;justify-content:space-between;align-items:center;padding:.6rem 1rem;background:var(--bg-inset);border:1px solid var(--border);border-radius:8px;"><span style="font-size:.82rem;font-weight:600;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em;">Balance</span><span id="balanceValue" class="mono" style="font-size:.9rem;font-weight:600;color:var(--xrp);">β XRP</span></div></div><label class="equal-toggle"><input type="checkbox" id="equalToggle" onchange="toggleEqualMode()"><span class="toggle-track"></span><span class="toggle-label">Send equal amount to all</span></label><div id="equalAmountSection" class="form-group" style="display:none;"><label class="form-label">Amount Per Recipient (XRP)</label><div class="amount-wrap" style="max-width:200px;"><input type="text" class="amt-input" id="equalAmount" placeholder="10" oninput="updateSummary()"><span class="amt-suffix">XRP</span></div></div><div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem;"><label class="form-label" style="margin-bottom:0;">Recipients</label><span class="recipient-count-badge"><strong id="recipientCount">0</strong> / 100</span></div><div class="recipients-list" id="recipientsList"></div><div style="display:flex;align-items:center;gap:.8rem;margin-bottom:1.2rem;"><button class="add-recipient-btn" onclick="addRecipientRow()" style="margin-bottom:0;">+ Add Recipient</button><div class="csv-upload" onclick="document.getElementById('csvFile').click()" style="margin-bottom:0;flex-shrink:0;"><span class="csv-upload-text">π Upload CSV</span><input type="file" id="csvFile" accept=".csv,.txt" onchange="handleCSV(event)"></div></div><div class="form-group"><label class="form-label">Memo (optional)</label><input type="text" class="form-input" id="globalMemo" placeholder="Applied to all payments" style="font-size:.88rem;"></div><div class="summary-card"><div class="summary-row"><span class="summary-label">Recipients</span><span class="summary-value" id="summaryRecipients">0</span></div><div class="summary-row"><span class="summary-label">Total Amount</span><span class="summary-value"><span id="summaryAmount">0 XRP</span></span></div><div class="summary-row"><span class="summary-label">Service Fee (0.3%)</span><span class="summary-value"><span id="summaryServiceFee">0 XRP</span></span></div><div class="summary-row"><span class="summary-label">Network Fees (est.)</span><span class="summary-value"><span id="summaryNetworkFee">0 XRP</span></span></div><div class="summary-row summary-total"><span class="summary-label">Total Cost</span><span class="summary-value"><span id="summaryTotal">0 XRP</span></span></div></div><button class="spray-btn" id="sprayBtn" onclick="handleSprayClick()" disabled>π§ Sign & Send Batch</button><div class="wallet-note" id="walletNote"><a onclick="connectWallet('gem')">Connect a wallet</a> to send β or explore the interface first</div><div class="status-msg" id="statusMsg"></div></div></div></section>
<section class="features"><div class="container"><h2 class="section-title">Why Spraay on XRP?</h2><p class="section-subtitle">Non-custodial batch payments with near-zero fees and instant finality.</p><div class="feature-grid"><div class="feature-card"><div class="feature-icon">π</div><h3>Non-Custodial</h3><p>Your wallet signs each transaction locally. Private keys never leave your device. GemWallet, Crossmark, or Xaman.</p></div><div class="feature-card"><div class="feature-icon">β‘</div><h3>3-4 Second Finality</h3><p>XRP Ledger validates transactions in 3-4 seconds. No waiting for block confirmations β payments settle almost instantly.</p></div><div class="feature-card"><div class="feature-icon">π°</div><h3>Near-Zero Fees</h3><p>XRP network fees are ~0.000012 XRP per transaction (~$0.00003). Even 100 recipients costs fractions of a cent.</p></div><div class="feature-card"><div class="feature-icon">π·οΈ</div><h3>Destination Tags</h3><p>Per-recipient destination tags supported β critical for sending to exchanges and hosted wallets that require them.</p></div><div class="feature-card"><div class="feature-icon">π</div><h3>CSV Import</h3><p>Upload a CSV with addresses, amounts, and optional destination tags. Perfect for payroll, airdrops, and bulk distributions.</p></div><div class="feature-card"><div class="feature-icon">π</div><h3>Multi-Chain</h3><p>Spraay is live on 15+ chains including Base, Ethereum, Solana, Bitcoin, Stellar, and now XRP Ledger.</p></div></div></div></section>
<section class="how-it-works"><div class="container"><h2 class="section-title" style="text-align:center">How It Works</h2><p class="section-subtitle" style="text-align:center">Four steps. Sequential signing. Non-custodial.</p><div class="steps"><div class="step"><div class="step-num">1</div><h3>Connect Wallet</h3><p>Connect GemWallet, Crossmark, or Xaman. Your keys stay local on your device.</p></div><div class="step"><div class="step-num">2</div><h3>Add Recipients</h3><p>Enter XRP addresses, amounts, and optional destination tags. Or upload a CSV file.</p></div><div class="step"><div class="step-num">3</div><h3>Sign Transactions</h3><p>Spraay builds sequential Payment transactions. Your wallet signs each one locally.</p></div><div class="step"><div class="step-num">4</div><h3>Watch It Settle</h3><p>Each payment validates in 3-4 seconds. Track on the XRP Ledger explorer.</p></div></div></div></section>
<section class="use-cases"><div class="container"><h2 class="section-title">Perfect For</h2><p class="section-subtitle">XRP-native batch payments for every use case.</p><div class="use-case-grid"><div class="use-case"><h3>π΅ XRP Payroll</h3><p>Pay your entire team in XRP with a single batch operation.</p></div><div class="use-case"><h3>π Token Airdrops</h3><p>Distribute XRP to your community efficiently with near-zero cost.</p></div><div class="use-case"><h3>π Reward Distributions</h3><p>Gaming rewards, contest prizes, affiliate payouts β all in one batch.</p></div><div class="use-case"><h3>ποΈ Treasury Operations</h3><p>DAO payouts, fund distributions, and grant disbursements on XRP Ledger.</p></div></div></div></section>
<section class="contract-section"><div class="container"><h2 class="section-title">Fee Structure</h2><p class="section-subtitle" style="margin-bottom:.5rem;">Transparent service fee</p><div class="contract-address"><code>0.3% of batch total Β· Network fees ~0.000012 XRP/tx Β· No cap</code></div><div style="margin-top:1.5rem;"><span style="color:var(--text-dim);font-size:.85rem;">Fee address: <code style="color:var(--xrp);">rpyynY82uCCgjjyPbxE6iYu6EzNZu6Hg1w</code></span></div></div></section>
<section class="cta"><div class="container"><h2>Ready to Batch Send on XRP?</h2><p>Non-custodial XRP batch payments. Near-zero fees with 3-4 second finality per payment.</p><div class="hero-buttons"><a href="#app" class="btn btn-primary">Launch App β</a><a href="https://docs.spraay.app" target="_blank" class="btn btn-outline">Documentation</a></div></div></section>
<footer><div class="container footer-inner"><div><a href="/" class="logo" style="font-size:1.1rem">SPRAAY</a><span class="footer-copy" style="margin-left:1rem">Batch crypto payments, multi-chain</span></div><div class="footer-links"><a href="/">Base</a><a href="/ethereum">Ethereum</a><a href="/solana">Solana</a><a href="/stacks">Stacks</a><a href="/stellar">Stellar</a><a href="/bitcoin">Bitcoin</a><a href="/xrp">XRP</a><a href="https://twitter.com/Spraay_app" target="_blank">Twitter</a><a href="https://warpcast.com/plag" target="_blank">Farcaster</a><a href="https://github.com/plagtech" target="_blank">GitHub</a></div></div></footer>
<div class="modal-overlay" id="confirmModal"><div class="modal"><h3>π§ Confirm Batch Send</h3><p style="text-align:center;font-size:.85rem;color:var(--text-dim);margin-bottom:1rem;">Review the details below before signing.</p><div id="modalRecipientsList" class="modal-recipients-preview"></div><div class="modal-summary"><div class="modal-row"><span class="label">Recipients</span><span class="value" id="modalRecipients">0</span></div><div class="modal-row"><span class="label">Total Amount</span><span class="value" id="modalAmount">0 XRP</span></div><div class="modal-row"><span class="label">Service Fee (0.3%)</span><span class="value" id="modalServiceFee">0 XRP</span></div><div class="modal-row"><span class="label">Network Fees</span><span class="value" id="modalNetworkFee">0 XRP</span></div><div class="modal-row total"><span class="label">Total</span><span class="value" id="modalTotal">0 XRP</span></div></div><div class="modal-buttons"><button class="modal-cancel" onclick="closeConfirmModal()">Cancel</button><button class="modal-confirm" id="modalConfirmBtn" onclick="confirmAndSend()">Sign & Send</button></div><p class="modal-warning">Your wallet will prompt you to sign each transaction sequentially. No funds leave until you approve.</p></div></div>
<script>
const GATEWAY_URL='https://gateway.spraay.app';const FEE_ADDRESS='rpyynY82uCCgjjyPbxE6iYu6EzNZu6Hg1w';const FEE_PERCENT=0.003;const EXPLORER='https://livenet.xrpl.org';const BASE_FEE_XRP=0.000012;let connectedWallet=null;let userAddress=null;let isEqualMode=false;let rowCounter=0;
let dropdownJustToggled=false;function toggleChainDropdown(e){e.preventDefault();e.stopPropagation();document.getElementById('chainDropdown').classList.toggle('open');dropdownJustToggled=true;setTimeout(()=>{dropdownJustToggled=false},100)}document.addEventListener('click',function(e){if(dropdownJustToggled)return;const dd=document.getElementById('chainDropdown');if(dd&&!dd.contains(e.target))dd.classList.remove('open')});function openMobilePanel(){document.getElementById('mobilePanelOverlay').classList.add('open');requestAnimationFrame(()=>{document.getElementById('mobilePanel').classList.add('open')});document.body.style.overflow='hidden'}function closeMobilePanel(){document.getElementById('mobilePanel').classList.remove('open');document.getElementById('mobilePanelOverlay').classList.remove('open');document.body.style.overflow=''}
window.addEventListener('DOMContentLoaded',()=>{addRecipientRow();addRecipientRow();addRecipientRow()});
async function connectWallet(type){try{if(type==='gem'){if(typeof window.GemWalletApi!=='undefined'){const resp=await window.GemWalletApi.getAddress();if(resp?.result?.address){setConnected(type,resp.result.address);return}}const addr=prompt('GemWallet not detected. Enter your XRP address:');if(addr&&addr.startsWith('r'))setConnected(type,addr)}else if(type==='crossmark'){if(typeof window.crossmark!=='undefined'){const resp=await window.crossmark.signIn();if(resp?.address){setConnected(type,resp.address);return}}const addr=prompt('Crossmark not detected. Enter your XRP address:');if(addr&&addr.startsWith('r'))setConnected(type,addr)}else if(type==='xaman'){const addr=prompt('Enter your XRP address (Xaman mobile signing coming soon):');if(addr&&addr.startsWith('r'))setConnected(type,addr)}}catch(err){showStatus('Wallet connection failed: '+err.message,'error')}}
function setConnected(type,address){connectedWallet=type;userAddress=address;document.querySelectorAll('.wallet-option').forEach(o=>o.classList.remove('active'));document.getElementById('walletInfo').style.display='flex';document.getElementById('walletAddr').textContent=address.slice(0,6)+'...'+address.slice(-4);const btn=document.getElementById('navConnectBtn');btn.textContent=address.slice(0,6)+'...'+address.slice(-4);btn.className='btn-connect btn-connected';document.getElementById('walletNote').innerHTML='Connected via <strong>'+type+'</strong> Β· <a onclick="disconnectWallet()">Disconnect</a>';document.getElementById('balanceDisplay').style.display='block';fetchBalance();updateSprayBtn()}
function disconnectWallet(){connectedWallet=null;userAddress=null;document.querySelectorAll('.wallet-option').forEach(o=>o.classList.remove('active'));document.getElementById('walletInfo').style.display='none';document.getElementById('balanceDisplay').style.display='none';const btn=document.getElementById('navConnectBtn');btn.textContent='Connect Wallet';btn.className='btn-connect';document.getElementById('walletNote').innerHTML='<a onclick="connectWallet(\'gem\')">Connect a wallet</a> to send β or explore the interface first';updateSprayBtn()}
async function fetchBalance(){if(!userAddress)return;try{const resp=await fetch('https://xrplcluster.com',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({method:'account_info',params:[{account:userAddress,ledger_index:'validated'}]})});const data=await resp.json();if(data.result?.account_data?.Balance){const xrp=(parseInt(data.result.account_data.Balance)/1000000).toFixed(6);document.getElementById('balanceValue').textContent=xrp+' XRP'}}catch(e){document.getElementById('balanceValue').textContent='β XRP'}}
function addRecipientRow(address,amount,tag){const id=++rowCounter;const list=document.getElementById('recipientsList');const row=document.createElement('div');row.className='recipient-row';row.id='row-'+id;row.innerHTML='<input type="text" class="addr-input" placeholder="rXXX..." value="'+(address||'')+'" oninput="updateSummary()"><div class="amount-wrap"><input type="text" class="amt-input" placeholder="0" value="'+(amount||'')+'" oninput="updateSummary()" '+(isEqualMode?'disabled':'')+'><span class="amt-suffix">XRP</span></div><input type="text" class="tag-input" placeholder="Tag" value="'+(tag||'')+'" title="Destination Tag (optional)"><button class="remove-btn" onclick="removeRow('+id+')">Γ</button>';list.appendChild(row);updateRecipientCount();updateSummary()}
function removeRow(id){const row=document.getElementById('row-'+id);if(row){row.remove();updateRecipientCount();updateSummary()}}function updateRecipientCount(){document.getElementById('recipientCount').textContent=document.querySelectorAll('.recipient-row').length}
function getRecipients(){const rows=document.querySelectorAll('.recipient-row');const recipients=[];rows.forEach(row=>{const addr=row.querySelector('.addr-input').value.trim();let amt=row.querySelector('.amt-input').value.trim();const tag=row.querySelector('.tag-input').value.trim();if(isEqualMode)amt=document.getElementById('equalAmount').value.trim();if(addr&&amt&&parseFloat(amt)>0){const r={address:addr,amount:amt};if(tag&&!isNaN(parseInt(tag)))r.tag=parseInt(tag);recipients.push(r)}});return recipients}
function toggleEqualMode(){isEqualMode=document.getElementById('equalToggle').checked;document.getElementById('equalAmountSection').style.display=isEqualMode?'block':'none';document.querySelectorAll('.recipient-row .amt-input').forEach(i=>{i.disabled=isEqualMode});updateSummary()}
function handleCSV(event){const file=event.target.files[0];if(!file)return;const reader=new FileReader();reader.onload=(e)=>{const lines=e.target.result.split('\n').filter(l=>l.trim());let added=0;for(const line of lines){if(line.toLowerCase().includes('address')||line.startsWith('#'))continue;const parts=line.split(',').map(p=>p.trim());if(parts.length>=2&&parts[0].startsWith('r')){addRecipientRow(parts[0],parts[1],parts[2]||'');added++}}if(added>0)showStatus('Imported '+added+' recipients from CSV.','success');else showStatus('No valid recipients found. Format: address, amount, tag','error')};reader.readAsText(file);event.target.value=''}
function updateSummary(){const recipients=getRecipients();const count=recipients.length;const totalXRP=recipients.reduce((s,r)=>s+parseFloat(r.amount||0),0);const serviceFee=totalXRP*FEE_PERCENT;const networkFee=(count+1)*BASE_FEE_XRP;const total=totalXRP+serviceFee+networkFee;document.getElementById('summaryRecipients').textContent=count;document.getElementById('summaryAmount').textContent=totalXRP.toFixed(6)+' XRP';document.getElementById('summaryServiceFee').textContent=serviceFee.toFixed(6)+' XRP';document.getElementById('summaryNetworkFee').textContent=networkFee.toFixed(6)+' XRP';document.getElementById('summaryTotal').textContent=total.toFixed(6)+' XRP';updateSprayBtn()}
function updateSprayBtn(){const recipients=getRecipients();document.getElementById('sprayBtn').disabled=!userAddress||recipients.length===0}
function handleSprayClick(){const recipients=getRecipients();if(!userAddress||recipients.length===0)return;const totalXRP=recipients.reduce((s,r)=>s+parseFloat(r.amount),0);const serviceFee=totalXRP*FEE_PERCENT;const networkFee=(recipients.length+1)*BASE_FEE_XRP;let html='';recipients.forEach(r=>{html+='<div class="modal-recip-item"><span class="addr">'+r.address.slice(0,8)+'...'+r.address.slice(-4)+'</span><span>'+parseFloat(r.amount).toFixed(6)+' XRP</span></div>'});document.getElementById('modalRecipientsList').innerHTML=html;document.getElementById('modalRecipients').textContent=recipients.length;document.getElementById('modalAmount').textContent=totalXRP.toFixed(6)+' XRP';document.getElementById('modalServiceFee').textContent=serviceFee.toFixed(6)+' XRP';document.getElementById('modalNetworkFee').textContent=networkFee.toFixed(6)+' XRP';document.getElementById('modalTotal').textContent=(totalXRP+serviceFee+networkFee).toFixed(6)+' XRP';document.getElementById('confirmModal').classList.add('show')}
function closeConfirmModal(){document.getElementById('confirmModal').classList.remove('show')}
async function confirmAndSend(){closeConfirmModal();const recipients=getRecipients();const memo=document.getElementById('globalMemo').value.trim();const btn=document.getElementById('sprayBtn');btn.disabled=true;btn.textContent='Building transactions...';try{const resp=await fetch(GATEWAY_URL+'/api/v1/xrp/batch',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sender:userAddress,recipients,memo:memo||undefined})});if(resp.status===402){showStatus('Payment required (x402). Ensure gateway access.','info');btn.disabled=false;btn.textContent='π§ Sign & Send Batch';return}const data=await resp.json();if(!data.success){showStatus('Error: '+data.error,'error');btn.disabled=false;btn.textContent='π§ Sign & Send Batch';return}const txs=data.transactions;let signed=0;btn.textContent='Signing 0/'+txs.length+'...';for(let i=0;i<txs.length;i++){btn.textContent='Signing '+(i+1)+'/'+txs.length+'...';try{if(connectedWallet==='gem'&&window.GemWalletApi){await window.GemWalletApi.submitTransaction({transaction:txs[i]});signed++}else if(connectedWallet==='crossmark'&&window.crossmark){await window.crossmark.signAndSubmit(txs[i]);signed++}else{console.log('Transaction to sign:',JSON.stringify(txs[i],null,2));signed++}}catch(err){console.error('Tx '+(i+1)+' failed:',err)}}if(signed===txs.length){showStatus('β
All '+txs.length+' transactions signed and submitted! <a href="'+EXPLORER+'/accounts/'+userAddress+'" target="_blank">View on XRPL Explorer β</a>','success')}else if(signed>0){showStatus(signed+'/'+txs.length+' transactions completed. Some may have been rejected.','info')}else{showStatus('No transactions were signed.','error')}fetchBalance()}catch(err){showStatus('Error: '+err.message,'error')}btn.disabled=false;btn.textContent='π§ Sign & Send Batch'}
function showStatus(msg,type){const el=document.getElementById('statusMsg');el.innerHTML=msg;el.className='status-msg show '+type}
</script>
</body>
</html>