// Optimal Trade App UI Kit — shared primitives, icons, and mock data const { useState, useEffect, useRef, useMemo } = React; /* ============================== ICONS (Lucide path data, React-native) ============ */ const ICONS = { "arrow-right-left": '', "arrow-up-down": '', "arrow-down-left": '', "arrow-up-right": '', "plus": '', "ship": '', "truck": '', "plane": '', "package": '', "warehouse": '', "lock": '', "shield-check": '', "chevron-right": '', "chevron-down": '', "chevron-left": '', "wallet": '', "home": '', "file-text": '', "search": '', "calculator": '', "check": '', "check-check": '', "x": '', "bell": '', "smartphone": '', "landmark": '', "bitcoin": '', "coins": '', "clock": '', "map-pin": '', "user": '', "users": '', "log-out": '', "settings": '', "more-horizontal": '', "activity": '', "trending-up": '', "circle-check": '', "dollar-sign": '', "store": '', "copy": '', "info": '', "filter": '', "signal": '', "wifi": '', "battery": '', "refresh-cw": '', "banknote": '', "star": '', "credit-card": '', "monitor": '', "external-link": '', "scan-line": '', "sparkles": '', "upload": '', "send": '', "paperclip": '', "scale": '', "badge-check": '', "circle-dot": '', "list": '', "headphones": '', "zap": '', "chevron-up": '', "globe": '', "shield": '', "wifi-off": '', "languages": '', "file-check": '', "git-compare": '', "message-square": '', "radar": '', "file-search": '', "handshake": '', }; function Icon({ name, size = 20, className = "", style = {}, strokeWidth = 2 }) { return ( ); } /* ============================== FLAGS (circle-flags CDN, real SVGs) =============== */ function Flag({ code, size = 22, style = {} }) { return {code}; } /* ============================== LOGO (inline SVG, bundler-proof) ================ */ const LOGO_OPTIMAL = 'M 16.739 28.865 C 13.24 28.865 10.244 28.262 7.75 27.056 C 5.256 25.837 3.336 24.144 1.992 21.976 C 0.664 19.808 0 17.293 0 14.432 C 0 11.572 0.664 9.057 1.992 6.889 C 3.336 4.721 5.256 3.034 7.75 1.828 C 10.244 0.609 13.24 0 16.739 0 C 20.237 0 23.233 0.609 25.727 1.828 C 28.238 3.034 30.157 4.721 31.485 6.889 C 32.829 9.057 33.501 11.572 33.501 14.432 C 33.501 17.293 32.829 19.808 31.485 21.976 C 30.157 24.144 28.238 25.837 25.727 27.056 C 23.233 28.262 20.237 28.865 16.739 28.865 Z M 16.739 24.497 C 19.087 24.509 21.039 24.099 22.593 23.265 C 24.164 22.431 25.339 21.257 26.116 19.744 C 26.91 18.23 27.306 16.459 27.306 14.432 C 27.306 12.405 26.91 10.648 26.116 9.16 C 25.339 7.659 24.164 6.491 22.593 5.658 C 21.039 4.824 19.087 4.394 16.739 4.368 C 14.39 4.355 12.439 4.766 10.884 5.6 C 9.329 6.434 8.155 7.608 7.361 9.121 C 6.584 10.635 6.195 12.405 6.195 14.432 C 6.195 16.459 6.584 18.223 7.361 19.724 C 8.138 21.212 9.305 22.373 10.859 23.207 C 12.43 24.041 14.39 24.471 16.739 24.497 Z M 37.365 28.288 L 37.365 0.577 L 51.82 0.577 C 52.16 0.577 52.598 0.59 53.132 0.616 C 53.666 0.629 54.16 0.667 54.614 0.731 C 56.638 0.975 58.307 1.507 59.618 2.328 C 60.947 3.149 61.926 4.189 62.558 5.446 C 63.206 6.69 63.53 8.076 63.53 9.602 C 63.53 11.116 63.206 12.502 62.558 13.759 C 61.91 15.003 60.922 16.036 59.594 16.857 C 58.282 17.678 56.622 18.211 54.614 18.454 C 54.16 18.506 53.658 18.544 53.108 18.57 C 52.573 18.595 52.144 18.608 51.82 18.608 L 43.22 18.608 L 43.22 28.288 L 37.365 28.288 Z M 43.22 14.278 L 51.577 14.278 C 51.901 14.278 52.266 14.266 52.67 14.24 C 53.075 14.214 53.448 14.163 53.788 14.086 C 54.76 13.894 55.521 13.554 56.072 13.066 C 56.638 12.579 57.035 12.027 57.262 11.411 C 57.505 10.795 57.626 10.193 57.626 9.602 C 57.626 9.012 57.505 8.409 57.262 7.794 C 57.035 7.165 56.638 6.607 56.072 6.119 C 55.521 5.632 54.76 5.292 53.788 5.099 C 53.448 5.022 53.075 4.978 52.67 4.965 C 52.266 4.939 51.901 4.926 51.577 4.926 L 43.22 4.926 L 43.22 14.278 Z M 75.831 28.288 L 75.831 4.926 L 64.51 4.926 L 64.51 0.577 L 93.007 0.577 L 93.007 4.926 L 81.686 4.926 L 81.686 28.288 L 75.831 28.288 Z M 96.405 28.288 L 96.405 0.577 L 102.26 0.577 L 102.26 28.288 L 96.405 28.288 Z M 106.622 28.288 L 106.622 0.577 L 111.894 0.577 L 123.992 20.205 L 136.091 0.577 L 141.362 0.577 L 141.362 28.288 L 135.896 28.288 L 135.896 10.526 L 125.158 28.288 L 122.826 28.288 L 112.112 10.526 L 112.112 28.288 L 106.622 28.288 Z M 143.814 28.288 L 154.844 0.577 L 163.42 0.577 L 174.449 28.288 L 168.424 28.288 L 158.415 3.425 L 159.727 3.425 L 149.839 28.288 L 143.814 28.288 Z M 149.961 22.284 L 149.961 17.954 L 168.327 17.954 L 168.327 22.284 L 149.961 22.284 Z M 177.384 28.288 L 177.384 0.577 L 183.239 0.577 L 183.239 23.939 L 198.69 23.939 L 198.69 28.288 L 177.384 28.288 Z'; const LOGO_TRADE = 'M 4.377 2.109 L 0 2.109 L 0 0 L 11.159 0 L 11.159 2.109 L 6.782 2.109 L 6.782 11.857 L 4.377 11.857 L 4.377 2.109 Z M 13.42 0 L 19.451 0 C 20.361 0 21.15 0.096 21.818 0.288 C 22.485 0.475 23.039 0.748 23.479 1.108 C 23.919 1.467 24.245 1.904 24.457 2.42 C 24.675 2.936 24.784 3.52 24.784 4.172 C 24.784 4.612 24.731 5.032 24.624 5.432 C 24.518 5.826 24.354 6.19 24.131 6.524 C 23.914 6.858 23.638 7.156 23.304 7.419 C 22.971 7.677 22.579 7.892 22.129 8.064 L 24.708 11.857 L 21.764 11.857 L 19.534 8.489 L 19.466 8.489 L 15.825 8.481 L 15.825 11.857 L 13.42 11.857 L 13.42 0 Z M 19.511 6.403 C 19.967 6.403 20.364 6.349 20.702 6.243 C 21.046 6.137 21.332 5.988 21.56 5.796 C 21.792 5.603 21.964 5.371 22.075 5.098 C 22.192 4.82 22.25 4.511 22.25 4.172 C 22.25 3.51 22.022 2.999 21.567 2.64 C 21.112 2.276 20.427 2.094 19.511 2.094 L 15.825 2.094 L 15.825 6.403 L 19.511 6.403 Z M 31.369 0 L 33.917 0 L 39.652 11.857 L 37.126 11.857 L 36.178 9.862 L 29.252 9.862 L 28.334 11.857 L 25.8 11.857 L 31.369 0 Z M 35.23 7.882 L 32.666 2.496 L 30.17 7.882 L 35.23 7.882 Z M 41.185 0 L 45.532 0 C 46.801 0 47.904 0.137 48.839 0.41 C 49.775 0.683 50.549 1.075 51.161 1.585 C 51.778 2.091 52.235 2.708 52.534 3.436 C 52.837 4.16 52.989 4.974 52.989 5.879 C 52.989 6.739 52.84 7.535 52.541 8.269 C 52.243 8.997 51.788 9.629 51.176 10.165 C 50.564 10.696 49.79 11.113 48.855 11.417 C 47.919 11.715 46.816 11.864 45.547 11.864 L 41.185 11.857 L 41.185 0 Z M 46.124 9.778 C 46.842 9.778 47.471 9.687 48.012 9.505 C 48.554 9.318 49.004 9.055 49.363 8.716 C 49.727 8.377 50 7.968 50.182 7.487 C 50.364 7.007 50.455 6.471 50.455 5.879 C 50.455 5.298 50.364 4.774 50.182 4.309 C 50 3.838 49.727 3.441 49.363 3.118 C 49.004 2.789 48.554 2.536 48.012 2.359 C 47.471 2.182 46.842 2.094 46.124 2.094 L 43.59 2.094 L 43.59 9.778 L 46.124 9.778 Z M 55.09 0 L 65.187 0 L 65.187 2.109 L 57.495 2.109 L 57.495 4.476 L 64.307 4.476 L 64.307 6.456 L 57.495 6.456 L 57.495 9.756 L 65.347 9.756 L 65.347 11.857 L 55.09 11.857 L 55.09 0 Z'; const LOGO_T = 'M 36.354 0 C 51.296 0 63.409 12.113 63.409 27.055 C 63.409 41.996 51.296 54.109 36.354 54.109 L 34.562 54.109 L 38.355 22.673 L 48.161 20.244 C 49.371 19.944 50.329 19.023 50.675 17.825 L 52.109 12.857 L 18.546 12.857 C 16.991 12.857 15.624 13.886 15.192 15.38 L 13.928 19.761 L 26.578 22.673 C 26.572 22.736 25.653 32.293 24.833 39.467 C 24.133 45.593 14.509 48.557 11.662 49.304 C 4.616 44.42 0 36.277 0 27.055 C 0 12.113 12.113 0 27.055 0 L 36.354 0 Z'; function Logo({ height = 26, variant = "color" }) { const mark = variant === "white" ? "#fff" : "#FF5C00"; const text = variant === "white" ? "#fff" : variant === "navy" ? "#1A2238" : "#1A2238"; return ( ); } function TMark({ size = 30, bg = "var(--ot-orange)", fg = "#fff", radius = 9 }) { return ( ); } /* ============================== SMALL PRIMITIVES ================================== */ function Button({ children, variant = "primary", icon, onClick, disabled, style }) { return ( ); } function Badge({ kind, children, icon, dot }) { return ( {icon && }{dot && }{children} ); } function StatusBar({ dark }) { return (
9:41
); } function ScreenHead({ title, onBack, right }) { return (
{onBack && }

{title}

{right}
); } function Row({ icon, iconEl, title, sub, right, onClick, chevron }) { return (
{iconEl || (icon && )} {title}{sub && {sub}} {right} {chevron && }
); } /* ============================== FORMATTERS ======================================= */ const fmt = (n, dp = 0) => n.toLocaleString("en-US", { minimumFractionDigits: dp, maximumFractionDigits: dp }); const money = (sym, n, dp = 0) => `${sym}\u00A0${fmt(n, dp)}`; /* ============================== MOTION PRIMITIVES ================================ */ // Odometer: tweens a number from old→new (money recount). Manrope tabular via .ot-num. function Odometer({ value, from, sym, dp = 0, className = "", style = {}, dur = 700 }) { const seed = from != null ? from : value; const [disp, setDisp] = useState(seed); const prev = useRef(seed); useEffect(() => { const a = prev.current, b = value; prev.current = value; if (a === b) { setDisp(b); return; } const t0 = performance.now(); let raf; const tick = (t) => { const k = Math.min(1, (t - t0) / dur); const e = 1 - Math.pow(1 - k, 3); setDisp(a + (b - a) * e); if (k < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [value]); return {sym ? sym + "\u00A0" : ""}{fmt(disp, dp)}; } // RateRing: 60s lock countdown ring; amber pulse in the last 10s. function RateRing({ seconds = 60, size = 54, onExpire }) { const [s, setS] = useState(seconds); useEffect(() => { const t = setInterval(() => setS(v => { if (v <= 1) { onExpire && onExpire(); return seconds; } return v - 1; }), 1000); return () => clearInterval(t); }, []); const r = (size - 8) / 2, c = 2 * Math.PI * r, pct = s / seconds, amber = s <= 10; return ( 0:{String(s).padStart(2, "0")} ); } // Skeleton shimmer block function Skeleton({ w = "100%", h = 14, r = 8, style = {} }) { return ; } // SwipeToPay: drag the knob to the end to confirm an irreversible payment. function SwipeToPay({ label = "Slide to pay", onConfirm, disabled }) { const trackRef = useRef(null), knobRef = useRef(null); const [x, setX] = useState(0); const [armed, setArmed] = useState(false); const state = useRef({ dragging: false, max: 0 }); const KN = 52; const begin = (e) => { if (disabled || armed) return; const track = trackRef.current; state.current.max = track.clientWidth - KN - 8; state.current.dragging = true; knobRef.current.setPointerCapture(e.pointerId); }; const move = (e) => { if (!state.current.dragging) return; const track = trackRef.current.getBoundingClientRect(); let nx = e.clientX - track.left - KN / 2; nx = Math.max(0, Math.min(state.current.max, nx)); setX(nx); }; const end = () => { if (!state.current.dragging) return; state.current.dragging = false; if (x >= state.current.max * 0.82) { setX(state.current.max); setArmed(true); setTimeout(() => onConfirm && onConfirm(), 220); } else setX(0); }; return (
{label}
); } // AI agent floating action button function AIFab({ onClick, label = "Ask agent" }) { return ( ); } // Confetti burst for success ceremony (transform/opacity only) function Confetti() { const bits = useMemo(() => Array.from({ length: 26 }).map((_, i) => ({ id: i, x: (Math.random() * 2 - 1) * 130, y: -(60 + Math.random() * 200), rot: Math.random() * 360, c: ["#FF5C00", "#FF7A00", "#1F8A5B", "#4842C8", "#FFBC8C"][i % 5], d: Math.random() * .12, s: 6 + Math.random() * 6 })), []); return ( {bits.map(b => )} ); } Object.assign(window, { React, useState, useEffect, useRef, useMemo, Icon, Flag, Logo, TMark, Button, Badge, StatusBar, ScreenHead, Row, fmt, money, Odometer, RateRing, Skeleton, SwipeToPay, AIFab, Confetti });