<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gift Trust</title>
    <description>The latest articles on DEV Community by Gift Trust (@gift_trust_44882a016531d7).</description>
    <link>https://dev.to/gift_trust_44882a016531d7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3603662%2F256e9dbd-34fd-4680-b501-79323563d823.jpg</url>
      <title>DEV Community: Gift Trust</title>
      <link>https://dev.to/gift_trust_44882a016531d7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gift_trust_44882a016531d7"/>
    <language>en</language>
    <item>
      <title>CONTINENTAL EPICTURE</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Sat, 15 Aug 2026 09:51:39 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/continental-epicture-18e3</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/continental-epicture-18e3</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{% raw %}' was not properly terminated with regexp: /\}\}/&lt;/p&gt;
</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
    </item>
    <item>
      <title>CONTINETAL EPICTURE</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Thu, 13 Aug 2026 06:54:57 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/continetal-epicture-1cpk</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/continetal-epicture-1cpk</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{% raw %}' was not properly terminated with regexp: /\}\}/&lt;/p&gt;
</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>CONTINENTAL EPICTURE</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Thu, 13 Aug 2026 06:49:47 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/continental-epicture-29il</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/continental-epicture-29il</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{% raw %}' was not properly terminated with regexp: /\}\}/&lt;/p&gt;
</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SPACESHOP</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Wed, 12 Aug 2026 20:37:12 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/spaceshop-2248</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/spaceshop-2248</guid>
      <description>&lt;p&gt;*&amp;lt;!DOCTYPE html&amp;gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
SPACESHOP — Dashboard
&lt;br&gt;
&lt;br&gt;


&lt;p&gt;:root{&lt;br&gt;
    --ink:#0D0E14; --panel:#12141C; --card:rgba(255,255,255,0.03); --card-b:rgba(255,255,255,0.07);&lt;br&gt;
    --line:rgba(255,255,255,0.08); --white:#F7F5F0;&lt;br&gt;
    --orange:#FF6A3D; --gold:#F0B429; --mint:#3DDC8A; --blue:#5B8CFF;&lt;br&gt;
    --muted:rgba(255,255,255,0.45); --muted2:rgba(255,255,255,0.28);&lt;br&gt;
  }&lt;br&gt;
  *{box-sizing:border-box;}&lt;br&gt;
  body{margin:0;background:var(--ink);color:var(--white);font-family:'Inter',-apple-system,'Segoe UI',Roboto,system-ui,sans-serif;-webkit-font-smoothing:antialiased;}&lt;br&gt;
  h1,h2,.brand{font-family:'Space Grotesk','Inter',-apple-system,sans-serif;}&lt;br&gt;
  .mono{font-family:'JetBrains Mono','SFMono-Regular',Consolas,monospace;}&lt;br&gt;
  button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;}&lt;br&gt;
  input,textarea{font-family:inherit;color:inherit;}&lt;br&gt;
  ::selection{background:var(--orange);color:var(--ink);}&lt;/p&gt;

&lt;p&gt;/* ---- layout shells ---- */&lt;br&gt;
  .screen{min-height:100vh;}&lt;br&gt;
  .hidden{display:none !important;}&lt;/p&gt;

&lt;p&gt;/* ---- onboarding ---- */&lt;br&gt;
  .onboard{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:24px;}&lt;br&gt;
  .onboard-box{width:100%;max-width:380px;}&lt;br&gt;
  .brand-row{display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:32px;}&lt;br&gt;
  .brand-mark{width:36px;height:36px;border-radius:10px;background:var(--orange);display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--ink);font-size:16px;}&lt;br&gt;
  .brand-name{font-weight:700;font-size:20px;letter-spacing:-0.02em;}&lt;br&gt;
  .onboard h1{font-size:26px;font-weight:700;text-align:center;line-height:1.2;margin:0;}&lt;br&gt;
  .onboard p.sub{color:var(--muted);font-size:14px;text-align:center;margin:10px 0 0;line-height:1.5;}&lt;br&gt;
  .field{margin-top:14px;}&lt;br&gt;
  .field label{display:block;color:rgba(255,255,255,0.55);font-size:12px;font-weight:500;margin-bottom:6px;}&lt;br&gt;
  .field input, .field textarea{width:100%;background:rgba(255,255,255,0.05);border:1px solid var(--line);border-radius:12px;padding:12px 14px;font-size:14px;outline:none;transition:border-color .15s;}&lt;br&gt;
  .field input:focus, .field textarea:focus{border-color:rgba(255,106,61,0.6);}&lt;br&gt;
  .slug-preview{color:var(--muted2);font-size:12px;margin-top:6px;}&lt;br&gt;
  .btn-primary{width:100%;margin-top:20px;background:var(--orange);color:var(--ink);font-weight:700;padding:14px;border-radius:12px;font-size:15px;display:flex;align-items:center;justify-content:center;gap:6px;transition:filter .15s, transform .1s;}&lt;br&gt;
  .btn-primary:hover{filter:brightness(1.06);}&lt;br&gt;
  .btn-primary:active{transform:scale(0.99);}&lt;br&gt;
  .hint{color:var(--muted2);font-size:12px;text-align:center;margin-top:14px;}&lt;/p&gt;

&lt;p&gt;/* ---- dashboard top bar ---- */&lt;br&gt;
  .topbar{position:sticky;top:0;z-index:20;background:rgba(13,14,20,0.9);backdrop-filter:blur(8px);border-bottom:1px solid var(--line);padding:14px 20px;display:flex;align-items:center;justify-content:space-between;}&lt;br&gt;
  .topbar .brand-row{margin:0;}&lt;br&gt;
  .btn-ghost{display:flex;align-items:center;gap:6px;background:rgba(255,255,255,0.06);border:1px solid var(--line);color:var(--white);font-size:13px;font-weight:500;padding:9px 15px;border-radius:999px;transition:background .15s;}&lt;br&gt;
  .btn-ghost:hover{background:rgba(255,255,255,0.1);}&lt;/p&gt;

&lt;p&gt;.wrap{max-width:960px;margin:0 auto;padding:24px 20px 60px;}&lt;/p&gt;

&lt;p&gt;/* ---- shop identity ---- */&lt;br&gt;
  .shop-id{display:flex;align-items:center;gap:12px;margin-bottom:22px;}&lt;br&gt;
  .shop-avatar{width:48px;height:48px;border-radius:16px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;color:var(--ink);flex-shrink:0;}&lt;br&gt;
  .shop-name{font-weight:700;font-size:20px;line-height:1.2;}&lt;br&gt;
  .shop-link{display:flex;align-items:center;gap:5px;color:var(--muted);font-size:13px;background:none;border:none;padding:0;margin-top:2px;}&lt;br&gt;
  .shop-link:hover{color:rgba(255,255,255,0.7);}&lt;/p&gt;

&lt;p&gt;/* ---- stat cards ---- */&lt;br&gt;
  .stats{display:flex;gap:12px;overflow-x:auto;margin-bottom:22px;padding-bottom:2px;}&lt;br&gt;
  .stat-card{flex:1;min-width:130px;background:var(--card);border:1px solid var(--card-b);border-radius:16px;padding:14px 16px;}&lt;br&gt;
  .stat-top{display:flex;align-items:center;justify-content:space-between;}&lt;br&gt;
  .stat-label{color:var(--muted);font-size:12px;font-weight:500;}&lt;br&gt;
  .stat-value{font-size:22px;font-weight:700;margin-top:6px;}&lt;/p&gt;

&lt;p&gt;/* ---- toolbar ---- */&lt;br&gt;
  .toolbar{display:flex;gap:8px;margin-bottom:16px;}&lt;br&gt;
  .search-wrap{position:relative;flex:1;}&lt;br&gt;
  .search-wrap svg{position:absolute;left:14px;top:50%;transform:translateY(-50%);opacity:0.35;}&lt;br&gt;
  .search-wrap input{width:100%;background:rgba(255,255,255,0.04);border:1px solid var(--line);border-radius:12px;padding:11px 14px 11px 38px;font-size:14px;outline:none;}&lt;br&gt;
  .search-wrap input:focus{border-color:rgba(255,255,255,0.25);}&lt;br&gt;
  .btn-add{display:flex;align-items:center;gap:6px;background:var(--orange);color:var(--ink);font-weight:600;font-size:14px;padding:11px 18px;border-radius:12px;flex-shrink:0;transition:filter .15s, transform .1s;}&lt;br&gt;
  .btn-add:hover{filter:brightness(1.06);}&lt;br&gt;
  .btn-add:active{transform:scale(0.98);}&lt;/p&gt;

&lt;p&gt;/* ---- category pills ---- */&lt;br&gt;
  .pills{display:flex;gap:8px;overflow-x:auto;margin-bottom:16px;padding-bottom:2px;}&lt;br&gt;
  .pill{display:flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px;font-size:13px;font-weight:500;border:1px solid var(--line);color:rgba(255,255,255,0.6);white-space:nowrap;flex-shrink:0;transition:all .15s;}&lt;br&gt;
  .pill:hover{border-color:rgba(255,255,255,0.25);color:rgba(255,255,255,0.9);}&lt;br&gt;
  .pill.active-all{background:var(--white);color:var(--ink);border-color:var(--white);}&lt;/p&gt;

&lt;p&gt;/* ---- listings panel ---- */&lt;br&gt;
  .panel{background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);border-radius:18px;padding:0 16px;}&lt;br&gt;
  .empty{text-align:center;padding:56px 20px;color:var(--muted);font-size:14px;}&lt;br&gt;
  .row{display:flex;align-items:center;gap:12px;padding:14px 0;border-bottom:1px solid rgba(255,255,255,0.06);}&lt;br&gt;
  .row:last-child{border-bottom:none;}&lt;br&gt;
  .beacon{width:5px;align-self:stretch;border-radius:4px;flex-shrink:0;min-height:40px;}&lt;br&gt;
  .row-body{flex:1;min-width:0;}&lt;br&gt;
  .row-title-line{display:flex;align-items:center;gap:8px;}&lt;br&gt;
  .row-title{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}&lt;br&gt;
  .badge-sold{font-size:10px;font-weight:700;text-transform:uppercase;color:rgba(255,255,255,0.4);background:rgba(255,255,255,0.06);padding:2px 6px;border-radius:5px;flex-shrink:0;}&lt;br&gt;
  .row-meta{display:flex;align-items:center;gap:12px;font-size:12px;color:var(--muted);margin-top:3px;flex-wrap:wrap;}&lt;br&gt;
  .row-price{font-size:14px;font-weight:600;flex-shrink:0;}&lt;br&gt;
  .row-actions{display:flex;gap:2px;flex-shrink:0;}&lt;br&gt;
  .icon-btn{padding:8px;border-radius:8px;color:rgba(255,255,255,0.45);}&lt;br&gt;
  .icon-btn:hover{background:rgba(255,255,255,0.08);color:#fff;}&lt;br&gt;
  .icon-btn.danger:hover{background:rgba(239,68,68,0.12);color:#f87171;}&lt;/p&gt;

&lt;p&gt;/* ---- modals ---- */&lt;br&gt;
  .overlay{position:fixed;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);z-index:50;display:flex;align-items:flex-end;justify-content:center;}&lt;br&gt;
  &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt;(min-width:640px){.overlay{align-items:center;}}&lt;br&gt;
  .modal{width:100%;max-width:420px;background:#181A24;border:1px solid var(--line);border-radius:24px 24px 0 0;padding:20px 20px 32px;max-height:90vh;overflow-y:auto;animation:slideup .2s ease;}&lt;br&gt;
  &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt;(min-width:640px){.modal{border-radius:24px;padding-bottom:24px;}}&lt;br&gt;
  @keyframes slideup{from{transform:translateY(20px);opacity:0;}to{transform:translateY(0);opacity:1;}}&lt;br&gt;
  .modal-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}&lt;br&gt;
  .modal-head h2{font-size:18px;font-weight:700;margin:0;}&lt;br&gt;
  .close-btn{color:rgba(255,255,255,0.4);padding:4px;}&lt;br&gt;
  .close-btn:hover{color:#fff;}&lt;br&gt;
  .cat-grid{display:flex;gap:8px;flex-wrap:wrap;}&lt;br&gt;
  .cat-btn{display:flex;align-items:center;gap:6px;padding:9px 13px;border-radius:12px;font-size:13px;font-weight:500;border:1px solid var(--line);color:rgba(255,255,255,0.6);}&lt;br&gt;
  .cat-btn.active{color:var(--ink);}&lt;br&gt;
  .two-col{display:grid;grid-template-columns:1fr 1fr;gap:12px;}&lt;br&gt;
  .modal .field textarea{resize:none;}&lt;/p&gt;

&lt;p&gt;/* ---- share sheet ---- */&lt;br&gt;
  .share-link-box{display:flex;align-items:center;gap:8px;background:rgba(255,255,255,0.05);border:1px solid var(--line);border-radius:12px;padding:10px 12px;margin-top:14px;}&lt;br&gt;
  .share-link-box span{flex:1;font-size:13px;color:rgba(255,255,255,0.7);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}&lt;br&gt;
  .channels{display:flex;gap:8px;margin-top:14px;}&lt;br&gt;
  .channel{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;padding:14px 8px;border-radius:12px;background:rgba(255,255,255,0.05);border:1px solid var(--line);transition:background .15s;}&lt;br&gt;
  .channel:hover{background:rgba(255,255,255,0.09);}&lt;br&gt;
  .channel span{font-size:11px;color:rgba(255,255,255,0.6);}&lt;/p&gt;

&lt;p&gt;/* ---- toast ---- */&lt;br&gt;
  .toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:60;display:flex;align-items:center;gap:8px;padding:11px 18px;border-radius:999px;font-size:14px;font-weight:500;background:var(--panel);color:#fff;border:1px solid rgba(255,255,255,0.1);box-shadow:0 10px 30px rgba(0,0,0,0.4);animation:fadeup .2s ease;}&lt;br&gt;
  @keyframes fadeup{from{transform:translate(-50%,8px);opacity:0;}to{transform:translate(-50%,0);opacity:1;}}&lt;/p&gt;

&lt;p&gt;/* ---- storefront (public view) ---- */&lt;br&gt;
  .store-header{border-bottom:1px solid var(--line);padding:32px 20px 22px;}&lt;br&gt;
  .store-inner{max-width:720px;margin:0 auto;}&lt;br&gt;
  .store-search{position:relative;margin-top:18px;}&lt;br&gt;
  .store-search svg{position:absolute;left:14px;top:50%;transform:translateY(-50%);opacity:0.3;}&lt;br&gt;
  .store-search input{width:100%;background:rgba(255,255,255,0.05);border:1px solid var(--line);border-radius:16px;padding:13px 16px 13px 40px;font-size:14px;outline:none;}&lt;br&gt;
  .store-grid{display:grid;grid-template-columns:1fr;gap:12px;max-width:720px;margin:24px auto 0;padding:0 20px;}&lt;br&gt;
  &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt;(min-width:640px){.store-grid{grid-template-columns:1fr 1fr;}}&lt;br&gt;
  .listing-card{display:flex;gap:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.07);border-radius:16px;padding:12px;text-align:left;transition:background .15s;}&lt;br&gt;
  .listing-card:hover{background:rgba(255,255,255,0.06);}&lt;br&gt;
  .lc-title-line{display:flex;justify-content:space-between;gap:8px;}&lt;br&gt;
  .lc-title{font-size:15px;font-weight:600;line-height:1.3;}&lt;br&gt;
  .lc-desc{color:var(--muted);font-size:13px;margin-top:3px;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}&lt;br&gt;
  .lc-bottom{display:flex;justify-content:space-between;align-items:center;margin-top:10px;}&lt;br&gt;
  .lc-price{font-size:15px;font-weight:600;}&lt;br&gt;
  .lc-views{color:rgba(255,255,255,0.3);font-size:11px;display:flex;align-items:center;gap:4px;}&lt;br&gt;
  .footer-tag{text-align:center;padding:32px 0;color:rgba(255,255,255,0.25);font-size:12px;}&lt;br&gt;
  .empty-shop{display:flex;align-items:center;justify-content:center;min-height:100vh;text-align:center;padding:24px;}&lt;/p&gt;

&lt;p&gt;.wa-btn{margin-top:18px;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;background:#25D366;color:#0b1a10;font-weight:700;padding:14px;border-radius:16px;}&lt;br&gt;
  .wa-btn:hover{filter:brightness(0.97);}&lt;/p&gt;



&lt;p&gt;/* ===================================================================&lt;br&gt;
   SPACESHOP — single-file dashboard + storefront.&lt;br&gt;
   All data lives in-memory (window.STATE) for this demo file.&lt;br&gt;
   To make it permanent/shareable for real, this same logic drops&lt;br&gt;
   into a backend + real domain — see the note at the bottom of chat.&lt;br&gt;
=================================================================== */&lt;/p&gt;

&lt;p&gt;const CATEGORIES = {&lt;br&gt;
  clothes:    { label: "Clothes",    color: "#FF6A3D", icon: "shirt" },&lt;br&gt;
  food:       { label: "Food",       color: "#F0B429", icon: "food" },&lt;br&gt;
  houses:     { label: "Houses",     color: "#3DDC8A", icon: "house" },&lt;br&gt;
  appliances: { label: "Appliances", color: "#5B8CFF", icon: "bolt" },&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const ICONS = {&lt;br&gt;
  shirt: '&amp;lt;path d="M16 4l4 3-2 3-2-1.5V20H8V8.5L6 10 4 7l4-3 2 2h4z"/&amp;gt;',&lt;br&gt;
  food: '&amp;lt;path d="M6 2v6a3 3 0 003 3v11M6 2v6M10 2v6M6 8h4M18 2c-2 2-3 4-3 7 0 2 1 3 3 3v9"/&amp;gt;',&lt;br&gt;
  house: '&amp;lt;path d="M3 11l9-8 9 8M5 10v10h14V10"/&amp;gt;',&lt;br&gt;
  bolt: '&amp;lt;path d="M13 2L4 14h6l-1 8 9-12h-6l1-8z"/&amp;gt;',&lt;br&gt;
  search: '&amp;lt;circle cx="11" cy="11" r="7"/&amp;gt;&amp;lt;path d="M21 21l-4.3-4.3"/&amp;gt;',&lt;br&gt;
  eye: '&amp;lt;path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z"/&amp;gt;&amp;lt;circle cx="12" cy="12" r="3"/&amp;gt;',&lt;br&gt;
  plus: '&amp;lt;path d="M12 5v14M5 12h14"/&amp;gt;',&lt;br&gt;
  x: '&amp;lt;path d="M18 6L6 18M6 6l12 12"/&amp;gt;',&lt;br&gt;
  edit: '&amp;lt;path d="M12 20h9"/&amp;gt;&amp;lt;path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/&amp;gt;',&lt;br&gt;
  trash: '&amp;lt;path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0l-1 14a2 2 0 01-2 2H7a2 2 0 01-2-2L4 6"/&amp;gt;',&lt;br&gt;
  share: '&amp;lt;circle cx="18" cy="5" r="3"/&amp;gt;&amp;lt;circle cx="6" cy="12" r="3"/&amp;gt;&amp;lt;circle cx="18" cy="19" r="3"/&amp;gt;&amp;lt;path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/&amp;gt;',&lt;br&gt;
  copy: '&amp;lt;rect x="9" y="9" width="13" height="13" rx="2"/&amp;gt;&amp;lt;path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/&amp;gt;',&lt;br&gt;
  check: '&amp;lt;path d="M20 6L9 17l-5-5"/&amp;gt;',&lt;br&gt;
  pkg: '&amp;lt;path d="M21 8l-9-5-9 5v8l9 5 9-5V8z"/&amp;gt;&amp;lt;path d="M3 8l9 5 9-5M12 13v8"/&amp;gt;',&lt;br&gt;
  chart: '&amp;lt;path d="M3 3v18h18M18 9l-5 5-3-3-4 4"/&amp;gt;',&lt;br&gt;
  store: '&amp;lt;path d="M3 9l1-5h16l1 5M4 9v11h16V9M4 9h16M9 20v-6h6v6"/&amp;gt;',&lt;br&gt;
  chevron: '&amp;lt;path d="M9 18l6-6-6-6"/&amp;gt;',&lt;br&gt;
  sparkle: '&amp;lt;path d="M12 2l1.5 6.5L20 10l-6.5 1.5L12 18l-1.5-6.5L4 10l6.5-1.5L12 2z"/&amp;gt;',&lt;br&gt;
  wa: '&amp;lt;path d="M17 3a10 10 0 00-14.7 13L2 21l5.2-1.4A10 10 0 1017 3z"/&amp;gt;&amp;lt;path d="M8.5 8.5c0 4 3 7 7 7"/&amp;gt;',&lt;br&gt;
  twitter: '&amp;lt;path d="M22 4.5c-.8.4-1.6.6-2.5.8a4.3 4.3 0 001.9-2.4c-.9.5-1.8.9-2.8 1.1A4.3 4.3 0 0012 7c0 .3 0 .7.1 1A12.2 12.2 0 013 3.3a4.3 4.3 0 001.3 5.7c-.7 0-1.4-.2-2-.5v.1c0 2.1 1.5 3.8 3.4 4.2-.6.2-1.3.2-1.9.1a4.3 4.3 0 004 3A8.6 8.6 0 012 18a12.1 12.1 0 006.6 1.9c7.9 0 12.2-6.6 12.2-12.2v-.6c.8-.6 1.5-1.4 2.2-2.2z"/&amp;gt;',&lt;br&gt;
  fb: '&amp;lt;path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"/&amp;gt;',&lt;br&gt;
  alert: '&amp;lt;circle cx="12" cy="12" r="10"/&amp;gt;&amp;lt;path d="M12 8v4M12 16h.01"/&amp;gt;',&lt;br&gt;
  imgoff: '&amp;lt;path d="M3 3l18 18M10.5 10.5a2 2 0 002.9 2.9M21 15V5a2 2 0 00-2-2H8m-5 4v12a2 2 0 002 2h12"/&amp;gt;',&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;function icon(name, size=15, extra=""){&lt;br&gt;
  return &lt;code&gt;&amp;amp;lt;svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="${extra}"&amp;amp;gt;${ICONS[name]}&amp;amp;lt;/svg&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function uid(){ return Math.random().toString(36).slice(2,10); }&lt;br&gt;
function slugify(s){&lt;br&gt;
  return s.toLowerCase().trim().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-").slice(0,40) || "shop";&lt;br&gt;
}&lt;br&gt;
function formatNaira(n){ return "₦" + Number(n).toLocaleString("en-NG"); }&lt;br&gt;
function timeAgo(ts){&lt;br&gt;
  const s=(Date.now()-ts)/1000;&lt;br&gt;
  if(s&amp;lt;60) return "just now";&lt;br&gt;
  if(s&amp;lt;3600) return Math.floor(s/60)+"m ago";&lt;br&gt;
  if(s&amp;lt;86400) return Math.floor(s/3600)+"h ago";&lt;br&gt;
  return Math.floor(s/86400)+"d ago";&lt;br&gt;
}&lt;br&gt;
function escapeHtml(str){&lt;br&gt;
  return String(str).replace(/[&amp;amp;&amp;lt;&amp;gt;"']/g, c =&amp;gt; ({"&amp;amp;":"&amp;amp;amp;","&amp;lt;":"&amp;amp;lt;","&amp;gt;":"&amp;amp;gt;","\"":"&amp;amp;quot;","'":"&amp;amp;#39;"}[c]));&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ---- in-memory "database" (per-browser-tab demo store) ---- */&lt;br&gt;
const DB = { shops: {}, slugs: {} };&lt;/p&gt;

&lt;p&gt;async function reserveUniqueSlug(base){&lt;br&gt;
  let candidate = slugify(base), n = 1;&lt;br&gt;
  while (DB.slugs[candidate]) { n++; candidate = slugify(base) + "-" + n; }&lt;br&gt;
  return candidate;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const SEED = () =&amp;gt; ([&lt;br&gt;
  { id: uid(), name: "Ankara Wrap Dress", category: "clothes", price: 15000, desc: "Handmade wrap dress, true to size, machine washable.", stock: 8, views: 142, createdAt: Date.now() - 86400000*2 },&lt;br&gt;
  { id: uid(), name: "Jollof Rice (Party Pack)", category: "food", price: 8000, desc: "Serves 5. Smoky party jollof, made fresh on order.", stock: 20, views: 310, createdAt: Date.now() - 3600000*5 },&lt;br&gt;
  { id: uid(), name: "2 Bedroom Flat, Lekki", category: "houses", price: 1800000, desc: "Newly renovated, self-compound, 24/7 light.", stock: 1, views: 502, createdAt: Date.now() - 86400000*6 },&lt;br&gt;
  { id: uid(), name: "Standing Fan 18-inch", category: "appliances", price: 22000, desc: "Brand new, 1 year warranty, 3-speed.", stock: 12, views: 88, createdAt: Date.now() - 86400000 },&lt;br&gt;
]);&lt;/p&gt;

&lt;p&gt;function rankListings(listings, query, categoryFilter){&lt;br&gt;
  const q = query.trim().toLowerCase();&lt;br&gt;
  const now = Date.now();&lt;br&gt;
  return listings&lt;br&gt;
    .filter(l =&amp;gt; categoryFilter === "all" || l.category === categoryFilter)&lt;br&gt;
    .map(l =&amp;gt; {&lt;br&gt;
      let score = 0;&lt;br&gt;
      if (q) {&lt;br&gt;
        const name = l.name.toLowerCase();&lt;br&gt;
        const cat = CATEGORIES[l.category].label.toLowerCase();&lt;br&gt;
        const desc = (l.desc||"").toLowerCase();&lt;br&gt;
        if (name === q) score += 100;&lt;br&gt;
        else if (name.startsWith(q)) score += 60;&lt;br&gt;
        else if (name.includes(q)) score += 40;&lt;br&gt;
        if (cat.includes(q)) score += 20;&lt;br&gt;
        if (desc.includes(q)) score += 10;&lt;br&gt;
        if (score === 0) return null;&lt;br&gt;
      }&lt;br&gt;
      const ageDays = (now - l.createdAt) / 86400000;&lt;br&gt;
      score += Math.max(0, 6 - ageDays);&lt;br&gt;
      score += l.stock &amp;gt; 0 ? 3 : -50;&lt;br&gt;
      return Object.assign({}, l, { _score: score });&lt;br&gt;
    })&lt;br&gt;
    .filter(Boolean)&lt;br&gt;
    .sort((a,b) =&amp;gt; b._score - a._score);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ---------------- Toast ---------------- */&lt;br&gt;
function showToast(msg, kind="ok"){&lt;br&gt;
  const old = document.querySelector(".toast");&lt;br&gt;
  if (old) old.remove();&lt;br&gt;
  const t = document.createElement("div");&lt;br&gt;
  t.className = "toast";&lt;br&gt;
  t.innerHTML = (kind==="ok" ? icon("check",14) : icon("alert",14)) + &lt;code&gt;&amp;amp;lt;span&amp;amp;gt;${escapeHtml(msg)}&amp;amp;lt;/span&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
  document.body.appendChild(t);&lt;br&gt;
  setTimeout(()=&amp;gt;t.remove(), 2600);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ---------------- App state / router ---------------- */&lt;br&gt;
const App = {&lt;br&gt;
  view: "onboard",     // onboard | dashboard | storefront&lt;br&gt;
  shopId: null,&lt;br&gt;
  storeQuery: "",&lt;br&gt;
  storeCat: "all",&lt;br&gt;
  dashQuery: "",&lt;br&gt;
  dashCat: "all",&lt;br&gt;
  activeListing: null,&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const root = document.getElementById("root");&lt;/p&gt;

&lt;p&gt;function render(){&lt;br&gt;
  if (App.view === "onboard") root.innerHTML = renderOnboard();&lt;br&gt;
  else if (App.view === "dashboard") root.innerHTML = renderDashboard();&lt;br&gt;
  else if (App.view === "storefront") root.innerHTML = renderStorefront();&lt;br&gt;
  bindEvents();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= ONBOARDING ================= */&lt;br&gt;
function renderOnboard(){&lt;br&gt;
  return &lt;code&gt;&lt;br&gt;
  &amp;amp;lt;div class="screen onboard"&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="onboard-box"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="brand-row"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="brand-mark"&amp;amp;gt;${icon("sparkle",17)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="brand-name"&amp;amp;gt;SPACESHOP&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;h1&amp;amp;gt;Open your shop&amp;amp;lt;/h1&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;p class="sub"&amp;amp;gt;Sell clothes, food, houses, or appliances. Get one link that opens anywhere — WhatsApp, Instagram, Facebook, X.&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="field"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;label&amp;amp;gt;Shop name&amp;amp;lt;/label&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;input id="in-name" placeholder="e.g. Chidera's Closet" autofocus /&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="slug-preview" id="slug-preview"&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="field"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;label&amp;amp;gt;WhatsApp number (for orders)&amp;amp;lt;/label&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;input id="in-wa" placeholder="e.g. 2348012345678" /&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;button class="btn-primary" id="btn-create"&amp;amp;gt;Create my shop ${icon("chevron",16)}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;p class="hint"&amp;amp;gt;We'll drop in a few sample listings to start you off.&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
  &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= DASHBOARD ================= */&lt;br&gt;
function renderDashboard(){&lt;br&gt;
  const shop = DB.shops[App.shopId];&lt;br&gt;
  const listings = shop.listings;&lt;br&gt;
  const q = App.dashQuery.trim().toLowerCase();&lt;br&gt;
  const filtered = listings&lt;br&gt;
    .filter(l =&amp;gt; App.dashCat === "all" || l.category === App.dashCat)&lt;br&gt;
    .filter(l =&amp;gt; !q || l.name.toLowerCase().includes(q))&lt;br&gt;
    .sort((a,b) =&amp;gt; b.createdAt - a.createdAt);&lt;/p&gt;

&lt;p&gt;const totalViews = listings.reduce((s,l)=&amp;gt;s+(l.views||0),0);&lt;br&gt;
  const inStock = listings.filter(l=&amp;gt;l.stock&amp;gt;0).length;&lt;br&gt;
  const totalValue = listings.reduce((s,l)=&amp;gt;s+l.price*l.stock,0);&lt;br&gt;
  const counts = {};&lt;br&gt;
  listings.forEach(l =&amp;gt; counts[l.category] = (counts[l.category]||0)+1);&lt;/p&gt;

&lt;p&gt;return `&lt;br&gt;
  &amp;lt;div class="screen"&amp;gt;&lt;br&gt;
    &amp;lt;div class="topbar"&amp;gt;&lt;br&gt;
      &amp;lt;div class="brand-row"&amp;gt;&amp;lt;div class="brand-mark"&amp;gt;${icon("sparkle",15)}&amp;lt;/div&amp;gt;&amp;lt;span class="brand-name" style="font-size:16px;"&amp;gt;SPACESHOP&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br&gt;
      &amp;lt;button class="btn-ghost" id="btn-share"&amp;gt;${icon("share",14)} Share shop&amp;lt;/button&amp;gt;&lt;br&gt;
    &amp;lt;/div&amp;gt;&lt;br&gt;
    &amp;lt;div class="wrap"&amp;gt;&lt;br&gt;
      &amp;lt;div class="shop-id"&amp;gt;&lt;br&gt;
        &amp;lt;div class="shop-avatar" style="background:${shop.color}"&amp;gt;${escapeHtml(shop.name[0].toUpperCase())}&amp;lt;/div&amp;gt;&lt;br&gt;
        &amp;lt;div&amp;gt;&lt;br&gt;
          &amp;lt;div class="shop-name"&amp;gt;${escapeHtml(shop.name)}&amp;lt;/div&amp;gt;&lt;br&gt;
          &amp;lt;button class="shop-link" id="btn-copy-link"&amp;gt;/${escapeHtml(shop.slug)} ${icon("copy",11)}&amp;lt;/button&amp;gt;&lt;br&gt;
        &amp;lt;/div&amp;gt;&lt;br&gt;
      &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;amp;lt;div class="stats"&amp;amp;gt;
    &amp;amp;lt;div class="stat-card"&amp;amp;gt;&amp;amp;lt;div class="stat-top"&amp;amp;gt;&amp;amp;lt;span class="stat-label"&amp;amp;gt;Listings&amp;amp;lt;/span&amp;amp;gt;${icon("pkg",14,"color:#FF6A3D")}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;div class="stat-value mono"&amp;amp;gt;${listings.length}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;div class="stat-card"&amp;amp;gt;&amp;amp;lt;div class="stat-top"&amp;amp;gt;&amp;amp;lt;span class="stat-label"&amp;amp;gt;Total views&amp;amp;lt;/span&amp;amp;gt;${icon("eye",14,"color:#5B8CFF")}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;div class="stat-value mono"&amp;amp;gt;${totalViews}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;div class="stat-card"&amp;amp;gt;&amp;amp;lt;div class="stat-top"&amp;amp;gt;&amp;amp;lt;span class="stat-label"&amp;amp;gt;In stock&amp;amp;lt;/span&amp;amp;gt;${icon("chart",14,"color:#3DDC8A")}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;div class="stat-value mono"&amp;amp;gt;${inStock}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;div class="stat-card"&amp;amp;gt;&amp;amp;lt;div class="stat-top"&amp;amp;gt;&amp;amp;lt;span class="stat-label"&amp;amp;gt;Inventory value&amp;amp;lt;/span&amp;amp;gt;${icon("store",14,"color:#F0B429")}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;div class="stat-value mono"&amp;amp;gt;${formatNaira(totalValue)}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
  &amp;amp;lt;/div&amp;amp;gt;

  &amp;amp;lt;div class="toolbar"&amp;amp;gt;
    &amp;amp;lt;div class="search-wrap"&amp;amp;gt;${icon("search",15)}&amp;amp;lt;input id="dash-search" placeholder="Search your listings…" value="${escapeHtml(App.dashQuery)}" /&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
    &amp;amp;lt;button class="btn-add" id="btn-add-listing"&amp;amp;gt;${icon("plus",16)} Add listing&amp;amp;lt;/button&amp;amp;gt;
  &amp;amp;lt;/div&amp;amp;gt;

  &amp;amp;lt;div class="pills"&amp;amp;gt;
    &amp;amp;lt;button class="pill ${App.dashCat==='all'?'active-all':''}" data-cat="all"&amp;amp;gt;All · ${listings.length}&amp;amp;lt;/button&amp;amp;gt;
    ${Object.entries(CATEGORIES).map(([k,c])=&amp;amp;gt;`
      &amp;amp;lt;button class="pill" data-cat="${k}" style="${App.dashCat===k?`background:${c.color};border-color:${c.color};color:#12141C`:''}"&amp;amp;gt;
        ${icon(c.icon,13)} ${c.label} · ${counts[k]||0}
      &amp;amp;lt;/button&amp;amp;gt;`).join("")}
  &amp;amp;lt;/div&amp;amp;gt;

  &amp;amp;lt;div class="panel"&amp;amp;gt;
    ${filtered.length === 0 ? `
      &amp;amp;lt;div class="empty"&amp;amp;gt;${icon("pkg",28,"opacity:.25;margin-bottom:10px")}&amp;amp;lt;div&amp;amp;gt;${listings.length===0 ? "No listings yet — add your first one" : "Nothing matches that search"}&amp;amp;lt;/div&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;
    ` : filtered.map(l =&amp;amp;gt; renderRow(l)).join("")}
  &amp;amp;lt;/div&amp;amp;gt;
&amp;amp;lt;/div&amp;amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;`;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderRow(l){&lt;br&gt;
  const c = CATEGORIES[l.category];&lt;br&gt;
  return &lt;code&gt;&lt;br&gt;
  &amp;amp;lt;div class="row" data-id="${l.id}"&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="beacon" style="background:${c.color}"&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="row-body"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="row-title-line"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="row-title"&amp;amp;gt;${escapeHtml(l.name)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        ${l.stock&amp;amp;lt;=0 ? '&amp;amp;lt;span class="badge-sold"&amp;amp;gt;Sold out&amp;amp;lt;/span&amp;amp;gt;' : ''}&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="row-meta"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span style="color:${c.color};font-weight:500"&amp;amp;gt;${c.label}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span&amp;amp;gt;${l.stock} in stock&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span style="display:flex;align-items:center;gap:4px"&amp;amp;gt;${icon("eye",11)}${l.views}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span&amp;amp;gt;${timeAgo(l.createdAt)}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="row-price mono"&amp;amp;gt;${formatNaira(l.price)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="row-actions"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;button class="icon-btn btn-edit" data-id="${l.id}"&amp;amp;gt;${icon("edit",14)}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;button class="icon-btn danger btn-delete" data-id="${l.id}"&amp;amp;gt;${icon("trash",14)}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
  &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= LISTING FORM MODAL ================= */&lt;br&gt;
function openListingModal(listing){&lt;br&gt;
  const isEdit = !!listing;&lt;br&gt;
  const f = listing || { name:"", category:"clothes", price:"", desc:"", stock:1 };&lt;br&gt;
  const modal = document.createElement("div");&lt;br&gt;
  modal.className = "overlay";&lt;br&gt;
  modal.innerHTML = &lt;code&gt;&lt;br&gt;
    &amp;amp;lt;div class="modal"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="modal-head"&amp;amp;gt;&amp;amp;lt;h2&amp;amp;gt;${isEdit?"Edit listing":"New listing"}&amp;amp;lt;/h2&amp;amp;gt;&amp;amp;lt;button class="close-btn" id="modal-close"&amp;amp;gt;${icon("x",18)}&amp;amp;lt;/button&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="field"&amp;amp;gt;&amp;amp;lt;label&amp;amp;gt;Item name&amp;amp;lt;/label&amp;amp;gt;&amp;amp;lt;input id="f-name" placeholder="e.g. Leather Sneakers" value="${escapeHtml(f.name)}" /&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="field"&amp;amp;gt;&amp;amp;lt;label&amp;amp;gt;Category&amp;amp;lt;/label&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="cat-grid"&amp;amp;gt;&lt;br&gt;
          ${Object.entries(CATEGORIES).map(([k,c])=&amp;amp;gt;&lt;/code&gt;&lt;br&gt;
            &amp;lt;button class="cat-btn" data-cat="${k}" style="${f.category===k?&lt;code&gt;background:${c.color};border-color:${c.color};color:#12141C&lt;/code&gt;:''}"&amp;gt;${icon(c.icon,13)} ${c.label}&amp;lt;/button&amp;gt;&lt;br&gt;
          &lt;code&gt;).join("")}&lt;br&gt;
        &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="two-col"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="field"&amp;amp;gt;&amp;amp;lt;label&amp;amp;gt;Price (₦)&amp;amp;lt;/label&amp;amp;gt;&amp;amp;lt;input id="f-price" type="number" placeholder="15000" class="mono" value="${f.price}" /&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="field"&amp;amp;gt;&amp;amp;lt;label&amp;amp;gt;Stock&amp;amp;lt;/label&amp;amp;gt;&amp;amp;lt;input id="f-stock" type="number" placeholder="10" class="mono" value="${f.stock}" /&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="field"&amp;amp;gt;&amp;amp;lt;label&amp;amp;gt;Description&amp;amp;lt;/label&amp;amp;gt;&amp;amp;lt;textarea id="f-desc" rows="3" placeholder="What buyers should know…"&amp;amp;gt;${escapeHtml(f.desc)}&amp;amp;lt;/textarea&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;button class="btn-primary" id="modal-save"&amp;amp;gt;${isEdit?"Save changes":"Add listing"}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
  document.body.appendChild(modal);&lt;/p&gt;

&lt;p&gt;let selectedCat = f.category;&lt;br&gt;
  modal.querySelectorAll(".cat-btn").forEach(btn =&amp;gt; {&lt;br&gt;
    btn.addEventListener("click", () =&amp;gt; {&lt;br&gt;
      selectedCat = btn.dataset.cat;&lt;br&gt;
      modal.querySelectorAll(".cat-btn").forEach(b=&amp;gt;{&lt;br&gt;
        const c = CATEGORIES[b.dataset.cat];&lt;br&gt;
        if (b.dataset.cat === selectedCat) b.style.cssText = &lt;code&gt;background:${c.color};border-color:${c.color};color:#12141C&lt;/code&gt;;&lt;br&gt;
        else b.style.cssText = "";&lt;br&gt;
      });&lt;br&gt;
    });&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;modal.addEventListener("click", (e) =&amp;gt; { if (e.target === modal) modal.remove(); });&lt;br&gt;
  modal.querySelector("#modal-close").addEventListener("click", () =&amp;gt; modal.remove());&lt;br&gt;
  modal.querySelector("#modal-save").addEventListener("click", () =&amp;gt; {&lt;br&gt;
    const name = modal.querySelector("#f-name").value.trim();&lt;br&gt;
    const price = Number(modal.querySelector("#f-price").value);&lt;br&gt;
    if (!name || !price) { showToast("Add a name and price first", "err"); return; }&lt;br&gt;
    const shop = DB.shops[App.shopId];&lt;br&gt;
    const newListing = {&lt;br&gt;
      id: listing ? listing.id : uid(),&lt;br&gt;
      name, category: selectedCat,&lt;br&gt;
      price, stock: Number(modal.querySelector("#f-stock").value) || 0,&lt;br&gt;
      desc: modal.querySelector("#f-desc").value.trim(),&lt;br&gt;
      views: listing ? listing.views : 0,&lt;br&gt;
      createdAt: listing ? listing.createdAt : Date.now(),&lt;br&gt;
    };&lt;br&gt;
    if (listing) {&lt;br&gt;
      shop.listings = shop.listings.map(l =&amp;gt; l.id === listing.id ? newListing : l);&lt;br&gt;
      showToast("Listing updated");&lt;br&gt;
    } else {&lt;br&gt;
      shop.listings = [newListing, ...shop.listings];&lt;br&gt;
      showToast("Listing added");&lt;br&gt;
    }&lt;br&gt;
    modal.remove();&lt;br&gt;
    render();&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= SHARE MODAL ================= */&lt;br&gt;
function openShareModal(){&lt;br&gt;
  const shop = DB.shops[App.shopId];&lt;br&gt;
  const link = window.location.origin + window.location.pathname + "#/s/" + shop.slug;&lt;br&gt;
  const shareMsg = encodeURIComponent(&lt;code&gt;Check out ${shop.name} on SPACESHOP 🛍️ ${link}&lt;/code&gt;);&lt;br&gt;
  const modal = document.createElement("div");&lt;br&gt;
  modal.className = "overlay";&lt;br&gt;
  modal.innerHTML = &lt;code&gt;&lt;br&gt;
    &amp;amp;lt;div class="modal"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="modal-head"&amp;amp;gt;&amp;amp;lt;h2&amp;amp;gt;Share your shop&amp;amp;lt;/h2&amp;amp;gt;&amp;amp;lt;button class="close-btn" id="modal-close"&amp;amp;gt;${icon("x",18)}&amp;amp;lt;/button&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;p style="color:rgba(255,255,255,0.4);font-size:13px;margin:0 0 4px;"&amp;amp;gt;Anyone who opens this link sees your storefront.&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="share-link-box"&amp;amp;gt;&amp;amp;lt;span&amp;amp;gt;${escapeHtml(link)}&amp;amp;lt;/span&amp;amp;gt;&amp;amp;lt;button id="btn-copy2"&amp;amp;gt;${icon("copy",15)}&amp;amp;lt;/button&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="channels"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;a class="channel" target="_blank" href="https://wa.me/?text=${shareMsg}"&amp;amp;gt;${icon("wa",18,"color:#25D366")}&amp;amp;lt;span&amp;amp;gt;WhatsApp&amp;amp;lt;/span&amp;amp;gt;&amp;amp;lt;/a&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;a class="channel" target="_blank" href="https://twitter.com/intent/tweet?text=${shareMsg}"&amp;amp;gt;${icon("twitter",18)}&amp;amp;lt;span&amp;amp;gt;X / Twitter&amp;amp;lt;/span&amp;amp;gt;&amp;amp;lt;/a&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;a class="channel" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(link)}"&amp;amp;gt;${icon("fb",18,"color:#1877F2")}&amp;amp;lt;span&amp;amp;gt;Facebook&amp;amp;lt;/span&amp;amp;gt;&amp;amp;lt;/a&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;p style="color:rgba(255,255,255,0.25);font-size:11px;text-align:center;margin-top:14px;"&amp;amp;gt;Instagram doesn't allow direct share links — paste the link into your bio or story instead.&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
  document.body.appendChild(modal);&lt;br&gt;
  modal.addEventListener("click", (e)=&amp;gt;{ if (e.target===modal) modal.remove(); });&lt;br&gt;
  modal.querySelector("#modal-close").addEventListener("click", ()=&amp;gt;modal.remove());&lt;br&gt;
  modal.querySelector("#btn-copy2").addEventListener("click", async ()=&amp;gt;{&lt;br&gt;
    try { await navigator.clipboard.writeText(link); showToast("Link copied"); }&lt;br&gt;
    catch { showToast("Couldn't copy — select manually", "err"); }&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= STOREFRONT (public buyer view) ================= */&lt;br&gt;
function renderStorefront(){&lt;br&gt;
  const shop = DB.shops[App.shopId];&lt;br&gt;
  if (!shop) {&lt;br&gt;
    return &lt;code&gt;&lt;br&gt;
    &amp;amp;lt;div class="empty-shop"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div style="width:56px;height:56px;border-radius:16px;background:rgba(255,255,255,0.05);display:flex;align-items:center;justify-content:center;margin:0 auto 16px;"&amp;amp;gt;${icon("imgoff",22,"opacity:.3")}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;h1 style="font-size:18px;font-weight:700;margin:0;"&amp;amp;gt;This shop link doesn't exist&amp;amp;lt;/h1&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;p style="color:rgba(255,255,255,0.4);font-size:14px;margin-top:6px;"&amp;amp;gt;It may have been removed, or the link was typed wrong.&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
  }&lt;br&gt;
  const ranked = rankListings(shop.listings, App.storeQuery, App.storeCat);&lt;br&gt;
  const counts = {};&lt;br&gt;
  shop.listings.forEach(l =&amp;gt; counts[l.category] = (counts[l.category]||0)+1);&lt;/p&gt;

&lt;p&gt;return &lt;code&gt;&lt;br&gt;
  &amp;amp;lt;div class="screen"&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="store-header"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="store-inner"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="shop-id" style="margin-bottom:0;"&amp;amp;gt;&lt;br&gt;
          &amp;amp;lt;div class="shop-avatar" style="background:${shop.color}"&amp;amp;gt;${escapeHtml(shop.name[0].toUpperCase())}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
          &amp;amp;lt;div&amp;amp;gt;&lt;br&gt;
            &amp;amp;lt;div class="shop-name"&amp;amp;gt;${escapeHtml(shop.name)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
            &amp;amp;lt;div style="color:rgba(255,255,255,0.4);font-size:13px;"&amp;amp;gt;Powered by SPACESHOP&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
          &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="store-search"&amp;amp;gt;${icon("search",16)}&amp;amp;lt;input id="store-search" placeholder="Search this shop…" value="${escapeHtml(App.storeQuery)}" /&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="pills" style="margin-top:14px;"&amp;amp;gt;&lt;br&gt;
          &amp;amp;lt;button class="pill ${App.storeCat==='all'?'active-all':''}" data-cat="all"&amp;amp;gt;All · ${shop.listings.length}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
          ${Object.entries(CATEGORIES).map(([k,c])=&amp;amp;gt;&lt;/code&gt;&lt;br&gt;
            &amp;lt;button class="pill" data-cat="${k}" style="${App.storeCat===k?&lt;code&gt;background:${c.color};border-color:${c.color};color:#12141C&lt;/code&gt;:''}"&amp;gt;${icon(c.icon,13)} ${c.label} · ${counts[k]||0}&amp;lt;/button&amp;gt;&lt;br&gt;
          &lt;code&gt;).join("")}&lt;br&gt;
        &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="store-grid"&amp;amp;gt;&lt;br&gt;
      ${ranked.length === 0 ?&lt;/code&gt;&amp;lt;div class="empty" style="grid-column:1/-1;"&amp;gt;${App.storeQuery ? &lt;code&gt;No results for "${escapeHtml(App.storeQuery)}"&lt;/code&gt; : "No listings yet in this category"}&amp;lt;/div&amp;gt;&lt;code&gt;:&lt;br&gt;
        ranked.map(l =&amp;amp;gt; renderListingCard(l)).join("")}&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="footer-tag"&amp;amp;gt;Built with &amp;amp;lt;strong style="color:rgba(255,255,255,0.4)"&amp;amp;gt;SPACESHOP&amp;amp;lt;/strong&amp;amp;gt; — a shop that opens with just a link&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
  &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderListingCard(l){&lt;br&gt;
  const c = CATEGORIES[l.category];&lt;br&gt;
  return &lt;code&gt;&lt;br&gt;
  &amp;amp;lt;button class="listing-card" data-id="${l.id}"&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div class="beacon" style="background:${c.color}"&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;div style="flex:1;min-width:0;"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="lc-title-line"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;div class="lc-title"&amp;amp;gt;${escapeHtml(l.name)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
        ${l.stock&amp;amp;lt;=0 ? '&amp;amp;lt;span class="badge-sold"&amp;amp;gt;Sold out&amp;amp;lt;/span&amp;amp;gt;' : ''}&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="lc-desc"&amp;amp;gt;${escapeHtml(l.desc)}&amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="lc-bottom"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span class="lc-price mono" style="color:${c.color}"&amp;amp;gt;${formatNaira(l.price)}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span class="lc-views"&amp;amp;gt;${icon("eye",11)} ${l.views}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
  &amp;amp;lt;/button&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function openListingSheet(listing){&lt;br&gt;
  const shop = DB.shops[App.shopId];&lt;br&gt;
  const c = CATEGORIES[listing.category];&lt;br&gt;
  listing.views = (listing.views||0) + 1;&lt;br&gt;
  const waNumber = (shop.whatsapp||"").replace(/[^0-9]/g,"");&lt;br&gt;
  const msg = encodeURIComponent(&lt;code&gt;Hi ${shop.name}! I'd like to order:\n\n${listing.name} — ${formatNaira(listing.price)}\n\nIs it available?&lt;/code&gt;);&lt;br&gt;
  const waLink = waNumber ? &lt;code&gt;https://wa.me/${waNumber}?text=${msg}&lt;/code&gt; : null;&lt;/p&gt;

&lt;p&gt;const modal = document.createElement("div");&lt;br&gt;
  modal.className = "overlay";&lt;br&gt;
  modal.innerHTML = &lt;code&gt;&lt;br&gt;
    &amp;amp;lt;div class="modal"&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div class="modal-head"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span style="font-size:11px;font-weight:700;text-transform:uppercase;padding:5px 10px;border-radius:999px;background:${c.color}22;color:${c.color}"&amp;amp;gt;${c.label}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;button class="close-btn" id="modal-close"&amp;amp;gt;${icon("x",18)}&amp;amp;lt;/button&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;h2 style="font-size:20px;font-weight:700;margin:10px 0 0;line-height:1.25;"&amp;amp;gt;${escapeHtml(listing.name)}&amp;amp;lt;/h2&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;p style="color:rgba(255,255,255,0.5);font-size:14px;margin-top:8px;line-height:1.5;"&amp;amp;gt;${escapeHtml(listing.desc)}&amp;amp;lt;/p&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;div style="display:flex;justify-content:space-between;align-items:center;margin-top:16px;"&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span class="mono" style="font-size:24px;font-weight:700;color:${c.color}"&amp;amp;gt;${formatNaira(listing.price)}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
        &amp;amp;lt;span style="font-size:12px;font-weight:500;color:${listing.stock&amp;amp;gt;0?'#3DDC8A':'rgba(255,255,255,0.3)'}"&amp;amp;gt;${listing.stock&amp;amp;gt;0 ? listing.stock+" available" : "Out of stock"}&amp;amp;lt;/span&amp;amp;gt;&lt;br&gt;
      &amp;amp;lt;/div&amp;amp;gt;&lt;br&gt;
      ${waLink ?&lt;/code&gt;&amp;lt;a class="wa-btn" target="_blank" href="${waLink}"&amp;gt;${icon("wa",18)} Order on WhatsApp&amp;lt;/a&amp;gt;&lt;code&gt;&lt;br&gt;
                :&lt;/code&gt;&amp;lt;div style="margin-top:18px;text-align:center;color:rgba(255,255,255,0.4);font-size:14px;padding:14px;border:1px solid var(--line);border-radius:16px;"&amp;gt;Seller hasn't added a WhatsApp number yet&amp;lt;/div&amp;gt;&lt;code&gt;}&lt;br&gt;
    &amp;amp;lt;/div&amp;amp;gt;&lt;/code&gt;;&lt;br&gt;
  document.body.appendChild(modal);&lt;br&gt;
  modal.addEventListener("click", (e)=&amp;gt;{ if (e.target===modal) { modal.remove(); render(); } });&lt;br&gt;
  modal.querySelector("#modal-close").addEventListener("click", ()=&amp;gt;{ modal.remove(); render(); });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= EVENT BINDING ================= */&lt;br&gt;
function bindEvents(){&lt;br&gt;
  if (App.view === "onboard") {&lt;br&gt;
    const nameInput = document.getElementById("in-name");&lt;br&gt;
    nameInput.addEventListener("input", () =&amp;gt; {&lt;br&gt;
      document.getElementById("slug-preview").textContent = nameInput.value ? "yoursite.com/#/s/" + slugify(nameInput.value) : "";&lt;br&gt;
    });&lt;br&gt;
    document.getElementById("btn-create").addEventListener("click", async () =&amp;gt; {&lt;br&gt;
      const name = nameInput.value.trim();&lt;br&gt;
      const wa = document.getElementById("in-wa").value.trim();&lt;br&gt;
      if (!name) { showToast("Give your shop a name first", "err"); return; }&lt;br&gt;
      const slug = await reserveUniqueSlug(name);&lt;br&gt;
      const id = uid();&lt;br&gt;
      const shop = { id, name, whatsapp: wa, slug, color: "#FF6A3D", listings: SEED(), createdAt: Date.now() };&lt;br&gt;
      DB.shops[id] = shop;&lt;br&gt;
      DB.slugs[slug] = id;&lt;br&gt;
      App.shopId = id;&lt;br&gt;
      App.view = "dashboard";&lt;br&gt;
      window.location.hash = "#/dash/" + id;&lt;br&gt;
      render();&lt;br&gt;
    });&lt;br&gt;
    return;&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;if (App.view === "dashboard") {&lt;br&gt;
    document.getElementById("dash-search").addEventListener("input", (e) =&amp;gt; { App.dashQuery = e.target.value; render(); });&lt;br&gt;
    document.querySelectorAll(".pill").forEach(p =&amp;gt; p.addEventListener("click", () =&amp;gt; { App.dashCat = p.dataset.cat; render(); }));&lt;br&gt;
    document.getElementById("btn-add-listing").addEventListener("click", () =&amp;gt; openListingModal(null));&lt;br&gt;
    document.getElementById("btn-share").addEventListener("click", openShareModal);&lt;br&gt;
    document.getElementById("btn-copy-link").addEventListener("click", async () =&amp;gt; {&lt;br&gt;
      const shop = DB.shops[App.shopId];&lt;br&gt;
      const link = window.location.origin + window.location.pathname + "#/s/" + shop.slug;&lt;br&gt;
      try { await navigator.clipboard.writeText(link); showToast("Link copied"); } catch { showToast("Couldn't copy", "err"); }&lt;br&gt;
    });&lt;br&gt;
    document.querySelectorAll(".btn-edit").forEach(b =&amp;gt; b.addEventListener("click", () =&amp;gt; {&lt;br&gt;
      const shop = DB.shops[App.shopId];&lt;br&gt;
      const listing = shop.listings.find(l =&amp;gt; l.id === b.dataset.id);&lt;br&gt;
      openListingModal(listing);&lt;br&gt;
    }));&lt;br&gt;
    document.querySelectorAll(".btn-delete").forEach(b =&amp;gt; b.addEventListener("click", () =&amp;gt; {&lt;br&gt;
      const shop = DB.shops[App.shopId];&lt;br&gt;
      shop.listings = shop.listings.filter(l =&amp;gt; l.id !== b.dataset.id);&lt;br&gt;
      showToast("Listing removed");&lt;br&gt;
      render();&lt;br&gt;
    }));&lt;br&gt;
    // preserve focus/cursor on search re-render&lt;br&gt;
    const s = document.getElementById("dash-search");&lt;br&gt;
    if (s &amp;amp;&amp;amp; document.activeElement !== s &amp;amp;&amp;amp; App.dashQuery) { /* no-op, cursor UX best-effort in vanilla JS */ }&lt;br&gt;
    return;&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;if (App.view === "storefront") {&lt;br&gt;
    const search = document.getElementById("store-search");&lt;br&gt;
    if (search) search.addEventListener("input", (e) =&amp;gt; { App.storeQuery = e.target.value; render(); document.getElementById("store-search").focus(); });&lt;br&gt;
    document.querySelectorAll(".pill").forEach(p =&amp;gt; p.addEventListener("click", () =&amp;gt; { App.storeCat = p.dataset.cat; render(); }));&lt;br&gt;
    document.querySelectorAll(".listing-card").forEach(card =&amp;gt; card.addEventListener("click", () =&amp;gt; {&lt;br&gt;
      const shop = DB.shops[App.shopId];&lt;br&gt;
      const listing = shop.listings.find(l =&amp;gt; l.id === card.dataset.id);&lt;br&gt;
      openListingSheet(listing);&lt;br&gt;
    }));&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ================= ROUTER ================= */&lt;br&gt;
function handleRoute(){&lt;br&gt;
  const hash = window.location.hash;&lt;br&gt;
  const storeMatch = hash.match(/^#\/s\/(.+)$/);&lt;br&gt;
  const dashMatch = hash.match(/^#\/dash\/(.+)$/);&lt;/p&gt;

&lt;p&gt;if (storeMatch) {&lt;br&gt;
    const slug = storeMatch[1];&lt;br&gt;
    App.shopId = DB.slugs[slug] || null;&lt;br&gt;
    App.view = "storefront";&lt;br&gt;
  } else if (dashMatch &amp;amp;&amp;amp; DB.shops[dashMatch[1]]) {&lt;br&gt;
    App.shopId = dashMatch[1];&lt;br&gt;
    App.view = "dashboard";&lt;br&gt;
  } else if (!App.shopId) {&lt;br&gt;
    App.view = "onboard";&lt;br&gt;
  }&lt;br&gt;
  render();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;window.addEventListener("hashchange", handleRoute);&lt;br&gt;
handleRoute();&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
 is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.*&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>AIRTIMES GLOBAL</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Mon, 03 Aug 2026 19:32:03 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/airtimes-global-2l4l</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/airtimes-global-2l4l</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
AIRTIMES — Global Network of Data &amp;amp; Free Services
&lt;br&gt;


&lt;p&gt;:root {&lt;br&gt;
  --bg: #020508; --sf: #060d14; --sf2: #0a1520;&lt;br&gt;
  --ac: #00ffe0; --ac2: #0077ff; --ac3: #ff4d6d;&lt;br&gt;
  --gd: #f5c518; --gd2: #ff9f00;&lt;br&gt;
  --tx: #c8e8ff; --mt: #3a5570;&lt;br&gt;
}&lt;br&gt;
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }&lt;br&gt;
html { scroll-behavior: smooth; }&lt;br&gt;
body { background: var(--bg); color: var(--tx); font-family: 'Space Mono', monospace; overflow-x: hidden; }&lt;/p&gt;

&lt;p&gt;.bg-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none;&lt;br&gt;
  background: radial-gradient(ellipse 80% 60% at 20% 10%, rgba(0,119,255,.07), transparent 70%),&lt;br&gt;
  radial-gradient(ellipse 60% 50% at 80% 80%, rgba(0,255,224,.06), transparent 70%),&lt;br&gt;
  radial-gradient(ellipse 40% 40% at 50% 50%, rgba(245,197,24,.03), transparent 70%); }&lt;br&gt;
.grid-overlay { position: fixed; inset: 0; z-index: 0; pointer-events: none;&lt;br&gt;
  background-image: linear-gradient(rgba(0,255,224,.04) 1px, transparent 1px),&lt;br&gt;
  linear-gradient(90deg, rgba(0,255,224,.04) 1px, transparent 1px);&lt;br&gt;
  background-size: 48px 48px; animation: gridDrift 30s linear infinite; }&lt;br&gt;
@keyframes gridDrift { to { background-position: 48px 48px; } }&lt;br&gt;
.scanline { position: fixed; inset: 0; z-index: 1; pointer-events: none;&lt;br&gt;
  background: repeating-linear-gradient(to bottom, transparent 0, transparent 3px, rgba(0,0,0,.06) 3px, rgba(0,0,0,.06) 4px); }&lt;br&gt;
.shell { position: relative; z-index: 2; }&lt;/p&gt;

&lt;p&gt;/* NAV */&lt;br&gt;
nav { display: flex; align-items: center; justify-content: space-between;&lt;br&gt;
  padding: 15px 38px; border-bottom: 1px solid rgba(0,255,224,.1);&lt;br&gt;
  backdrop-filter: blur(14px); position: sticky; top: 0; z-index: 200; background: rgba(2,5,8,.9); }&lt;br&gt;
.logo { font-family: 'Bebas Neue'; font-size: 1.85rem; letter-spacing: .25em;&lt;br&gt;
  color: var(--ac); text-shadow: 0 0 24px rgba(0,255,224,.5); cursor: pointer; position: relative; }&lt;br&gt;
.logo::after { content: '●'; font-size: .35em; color: var(--ac3);&lt;br&gt;
  position: absolute; top: 4px; right: -14px; animation: blink 1.4s step-end infinite; }&lt;br&gt;
@keyframes blink { 50% { opacity: 0; } }&lt;br&gt;
.nl { display: flex; gap: 22px; list-style: none; }&lt;br&gt;
.nl a { color: var(--mt); text-decoration: none; font-size: .65rem; letter-spacing: .14em;&lt;br&gt;
  text-transform: uppercase; transition: color .2s; cursor: pointer; }&lt;br&gt;
.nl a:hover { color: var(--ac); }&lt;br&gt;
.nl a.ga { color: var(--gd); }&lt;br&gt;
.nl a.ga:hover { color: var(--gd2); }&lt;br&gt;
.sp { display: flex; align-items: center; gap: 8px; font-size: .6rem; color: var(--ac); letter-spacing: .12em; }&lt;br&gt;
.pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--ac);&lt;br&gt;
  box-shadow: 0 0 8px var(--ac); animation: blink 1.4s step-end infinite; }&lt;/p&gt;

&lt;p&gt;/* TABS */&lt;br&gt;
.tab-bar { display: flex; border-bottom: 1px solid rgba(0,255,224,.08); background: rgba(2,5,8,.75);&lt;br&gt;
  position: sticky; top: 61px; z-index: 100; backdrop-filter: blur(10px); overflow-x: auto; }&lt;br&gt;
.tab { padding: 13px 22px; font-size: .62rem; letter-spacing: .15em; text-transform: uppercase;&lt;br&gt;
  color: var(--mt); cursor: pointer; border-bottom: 2px solid transparent; transition: all .25s; white-space: nowrap; flex-shrink: 0; }&lt;br&gt;
.tab:hover { color: var(--tx); }&lt;br&gt;
.tab.active { color: var(--ac); border-bottom-color: var(--ac); }&lt;br&gt;
.tab.gt { color: rgba(245,197,24,.5); }&lt;br&gt;
.tab.gt:hover { color: var(--gd); }&lt;br&gt;
.tab.gt.active { color: var(--gd); border-bottom-color: var(--gd); }&lt;/p&gt;

&lt;p&gt;/* PAGES */&lt;br&gt;
.page { display: none; animation: pi .3s ease; }&lt;br&gt;
.page.active { display: block; }&lt;br&gt;
@keyframes pi { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }&lt;br&gt;
.sec { max-width: 1300px; margin: 0 auto; padding: 56px 40px; }&lt;br&gt;
.sl { font-size: .58rem; color: var(--ac); letter-spacing: .28em; text-transform: uppercase; margin-bottom: 8px; }&lt;br&gt;
.sl.gd { color: var(--gd); }&lt;br&gt;
.st { font-family: 'Bebas Neue'; font-size: 2.8rem; color: #fff; letter-spacing: .05em; margin-bottom: 28px; line-height: 1; }&lt;br&gt;
.sub { font-size: .76rem; color: var(--mt); line-height: 1.9; max-width: 640px; margin-bottom: 36px; }&lt;/p&gt;

&lt;p&gt;/* HERO */&lt;br&gt;
.hero { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center;&lt;br&gt;
  padding: 76px 40px 56px; max-width: 1300px; margin: 0 auto; }&lt;br&gt;
.htag { font-size: .62rem; letter-spacing: .24em; text-transform: uppercase;&lt;br&gt;
  color: var(--ac2); margin-bottom: 12px; display: flex; align-items: center; gap: 10px; }&lt;br&gt;
.htag::before { content: ''; width: 30px; height: 1px; background: var(--ac2); }&lt;br&gt;
h1 { font-family: 'Bebas Neue'; font-size: clamp(3.2rem,6.5vw,6rem);&lt;br&gt;
  line-height: .92; letter-spacing: .05em; color: #fff; margin-bottom: 20px; }&lt;br&gt;
h1 .t { color: var(--ac); text-shadow: 0 0 25px rgba(0,255,224,.4); }&lt;br&gt;
h1 .r { color: var(--ac3); }&lt;br&gt;
h1 .g { color: var(--gd); }&lt;br&gt;
.hd { font-size: .78rem; line-height: 1.9; color: var(--mt); max-width: 420px; margin-bottom: 32px; }&lt;br&gt;
.ha { display: flex; gap: 10px; flex-wrap: wrap; }&lt;/p&gt;

&lt;p&gt;/* BUTTONS */&lt;br&gt;
.btn { border: none; padding: 12px 24px; font-family: 'Space Mono'; font-size: .68rem;&lt;br&gt;
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase; cursor: pointer; transition: all .25s; }&lt;br&gt;
.bp { background: var(--ac); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }&lt;br&gt;
.bp:hover { box-shadow: 0 0 28px rgba(0,255,224,.3); transform: translateY(-2px); }&lt;br&gt;
.bg { background: var(--gd); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }&lt;br&gt;
.bg:hover { box-shadow: 0 0 28px rgba(245,197,24,.3); transform: translateY(-2px); }&lt;br&gt;
.bs { background: transparent; color: var(--tx); border: 1px solid var(--mt); }&lt;br&gt;
.bs:hover { border-color: var(--ac); color: var(--ac); }&lt;br&gt;
.bsm { padding: 8px 16px; font-size: .6rem; }&lt;/p&gt;

&lt;p&gt;/* GLOBE */&lt;br&gt;
.gw { position: relative; display: flex; align-items: center; justify-content: center; height: 400px; }&lt;br&gt;
.globe { width: 290px; height: 290px; border-radius: 50%;&lt;br&gt;
  background: radial-gradient(circle at 35% 35%, rgba(0,119,255,.25), transparent 60%),&lt;br&gt;
  radial-gradient(circle at 50% 50%, #020d1a 40%, #010608 100%);&lt;br&gt;
  border: 1px solid rgba(0,255,224,.15);&lt;br&gt;
  box-shadow: 0 0 80px rgba(0,119,255,.2), inset 0 0 60px rgba(0,119,255,.1);&lt;br&gt;
  animation: gp 6s ease-in-out infinite; }&lt;br&gt;
@keyframes gp { 50% { box-shadow: 0 0 120px rgba(0,119,255,.35), inset 0 0 80px rgba(0,119,255,.15); } }&lt;br&gt;
.orb { position: absolute; border-radius: 50%; border: 1px solid rgba(0,255,224,.12); }&lt;br&gt;
.o1 { width: 370px; height: 370px; animation: os 12s linear infinite; }&lt;br&gt;
.o2 { width: 450px; height: 450px; border-color: rgba(0,119,255,.08); animation: os 20s linear infinite reverse; }&lt;br&gt;
@keyframes os { to { transform: rotate(360deg); } }&lt;br&gt;
.on { position: absolute; width: 8px; height: 8px; border-radius: 50%;&lt;br&gt;
  background: var(--ac); box-shadow: 0 0 12px var(--ac); top: -4px; left: 50%; transform: translateX(-50%); }&lt;br&gt;
.o2 .on { background: var(--ac2); box-shadow: 0 0 12px var(--ac2); }&lt;br&gt;
.sig { position: absolute; top: 50%; left: 50%; height: 1px;&lt;br&gt;
  background: linear-gradient(90deg, rgba(0,255,224,.5), transparent);&lt;br&gt;
  transform-origin: left; animation: sp 3s ease-in-out infinite; }&lt;br&gt;
@keyframes sp { 0% { opacity: 0; width: 0; } 40% { opacity: 1; } 100% { opacity: 0; width: 150px; } }&lt;br&gt;
.s1 { transform: rotate(35deg); }&lt;br&gt;
.s2 { transform: rotate(135deg); animation-delay: .8s; }&lt;br&gt;
.s3 { transform: rotate(220deg); animation-delay: 1.6s; }&lt;br&gt;
.s4 { transform: rotate(310deg); animation-delay: 2.4s; }&lt;br&gt;
.db { position: absolute; background: rgba(6,13,20,.92); border: 1px solid rgba(0,255,224,.2);&lt;br&gt;
  padding: 7px 12px; font-size: .57rem; color: var(--ac); letter-spacing: .1em;&lt;br&gt;
  backdrop-filter: blur(8px); animation: fu 4s ease-in-out infinite; }&lt;br&gt;
.db.b1 { top: 46px; left: 4px; }&lt;br&gt;
.db.b2 { bottom: 66px; right: -2px; animation-delay: 1.5s; color: var(--ac2); border-color: rgba(0,119,255,.3); }&lt;br&gt;
.db.b3 { top: 166px; right: -4px; animation-delay: .8s; color: var(--gd); border-color: rgba(245,197,24,.3); }&lt;br&gt;
@keyframes fu { 50% { transform: translateY(-8px); } }&lt;/p&gt;

&lt;p&gt;/* TICKER */&lt;br&gt;
.tw { overflow: hidden; padding: 10px 0; border-top: 1px solid rgba(0,255,224,.07);&lt;br&gt;
  border-bottom: 1px solid rgba(0,255,224,.07); background: rgba(0,255,224,.02); }&lt;br&gt;
.tt { display: flex; gap: 56px; white-space: nowrap; animation: ts 35s linear infinite; }&lt;br&gt;
@keyframes ts { to { transform: translateX(-50%); } }&lt;br&gt;
.ti { display: flex; align-items: center; gap: 10px; font-size: .62rem;&lt;br&gt;
  letter-spacing: .14em; text-transform: uppercase; color: var(--mt); }&lt;br&gt;
.dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ac); }&lt;br&gt;
.dot.gd { background: var(--gd); }&lt;/p&gt;

&lt;p&gt;/* STATS ROW */&lt;br&gt;
.sr { max-width: 1300px; margin: 50px auto; padding: 0 40px;&lt;br&gt;
  display: grid; grid-template-columns: repeat(4,1fr); gap: 2px; }&lt;br&gt;
.sc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);&lt;br&gt;
  padding: 26px 22px; position: relative; overflow: hidden; transition: border-color .3s; }&lt;br&gt;
.sc:hover { border-color: rgba(0,255,224,.2); }&lt;br&gt;
.sc::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;&lt;br&gt;
  background: linear-gradient(90deg, transparent, var(--ac), transparent); opacity: 0; transition: opacity .3s; }&lt;br&gt;
.sc:hover::before { opacity: 1; }&lt;br&gt;
.sn { font-family: 'Bebas Neue'; font-size: 2.6rem; color: var(--ac); line-height: 1; margin-bottom: 4px; }&lt;br&gt;
.sn.b { color: var(--ac2); } .sn.r { color: var(--ac3); } .sn.gd { color: var(--gd); }&lt;br&gt;
.sl2 { font-size: .56rem; color: var(--mt); letter-spacing: .18em; text-transform: uppercase; }&lt;/p&gt;

&lt;p&gt;/* CARDS */&lt;br&gt;
.cg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; }&lt;br&gt;
.card { background: var(--sf); border: 1px solid rgba(0,255,224,.07);&lt;br&gt;
  padding: 30px 24px; position: relative; overflow: hidden; cursor: pointer; transition: all .3s; }&lt;br&gt;
.card:hover { background: var(--sf2); border-color: rgba(0,255,224,.2); transform: translateY(-3px); }&lt;br&gt;
.card::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px;&lt;br&gt;
  background: linear-gradient(90deg, var(--ac2), var(--ac));&lt;br&gt;
  transform: scaleX(0); transform-origin: left; transition: transform .4s; }&lt;br&gt;
.card:hover::after { transform: scaleX(1); }&lt;br&gt;
.ci { font-size: 1.7rem; margin-bottom: 16px; }&lt;br&gt;
.cn { font-family: 'Syne'; font-size: .95rem; font-weight: 700; color: #fff; margin-bottom: 8px; }&lt;br&gt;
.cd { font-size: .68rem; line-height: 1.8; color: var(--mt); }&lt;br&gt;
.ctag { display: inline-block; margin-top: 14px; font-size: .54rem; letter-spacing: .17em;&lt;br&gt;
  text-transform: uppercase; color: var(--ac); border: 1px solid rgba(0,255,224,.2); padding: 3px 9px; }&lt;br&gt;
.ctag.b { color: var(--ac2); border-color: rgba(0,119,255,.25); }&lt;br&gt;
.ctag.r { color: var(--ac3); border-color: rgba(255,77,109,.25); }&lt;br&gt;
.ctag.gd { color: var(--gd); border-color: rgba(245,197,24,.25); }&lt;/p&gt;

&lt;p&gt;/* FORMS */&lt;br&gt;
.fp { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; }&lt;br&gt;
.fp.gp { border-color: rgba(245,197,24,.2); }&lt;br&gt;
.fg { margin-bottom: 14px; }&lt;br&gt;
.fl { font-size: .56rem; color: var(--mt); letter-spacing: .2em; text-transform: uppercase; margin-bottom: 6px; display: block; }&lt;br&gt;
.fi { background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);&lt;br&gt;
  color: var(--tx); padding: 10px 14px; font-family: 'Space Mono'; font-size: .7rem;&lt;br&gt;
  outline: none; width: 100%; transition: border-color .2s; }&lt;br&gt;
.fi:focus { border-color: var(--ac); }&lt;br&gt;
.fi.gi { border-color: rgba(245,197,24,.2); }&lt;br&gt;
.fi.gi:focus { border-color: var(--gd); }&lt;br&gt;
.fi::placeholder { color: var(--mt); }&lt;br&gt;
select.fi option { background: #020508; }&lt;br&gt;
.rb { margin-top: 16px; background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);&lt;br&gt;
  padding: 16px; font-size: .67rem; line-height: 2.2; display: none; }&lt;br&gt;
.rb.show { display: block; }&lt;br&gt;
.rb.gb { border-color: rgba(245,197,24,.2); background: rgba(245,197,24,.02); }&lt;br&gt;
.rk { color: var(--mt); } .rv { color: var(--ac); }&lt;br&gt;
.rv.gd { color: var(--gd); } .rv.g { color: #4ade80; }&lt;/p&gt;

&lt;p&gt;/* WALLET GRID */&lt;br&gt;
.wg { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 28px; }&lt;br&gt;
.wc { background: var(--sf); border: 1px solid rgba(245,197,24,.12);&lt;br&gt;
  padding: 22px 16px; text-align: center; cursor: pointer; transition: all .3s; position: relative; }&lt;br&gt;
.wc:hover { border-color: rgba(245,197,24,.4); background: rgba(245,197,24,.04); }&lt;br&gt;
.wc.sel { border-color: var(--gd); background: rgba(245,197,24,.06); }&lt;br&gt;
.wc.sel::before { content: '✓'; position: absolute; top: 7px; right: 9px; font-size: .58rem; color: var(--gd); }&lt;br&gt;
.wi { font-size: 1.9rem; margin-bottom: 7px; }&lt;br&gt;
.wco { font-family: 'Bebas Neue'; font-size: 1.15rem; color: var(--gd); letter-spacing: .1em; }&lt;br&gt;
.wna { font-size: .55rem; color: var(--mt); letter-spacing: .1em; margin-top: 2px; }&lt;br&gt;
.wad { font-size: .46rem; color: rgba(58,85,112,.55); margin-top: 7px; word-break: break-all; line-height: 1.4; }&lt;/p&gt;

&lt;p&gt;/* BUNDLE GRID */&lt;br&gt;
.bg2 { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 22px; }&lt;br&gt;
.bc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);&lt;br&gt;
  padding: 20px 14px; text-align: center; cursor: pointer; transition: all .3s; }&lt;br&gt;
.bc:hover { border-color: rgba(0,255,224,.25); }&lt;br&gt;
.bc.sel { border-color: var(--ac); background: rgba(0,255,224,.04); }&lt;br&gt;
.bn { font-family: 'Bebas Neue'; font-size: 1rem; color: #fff; letter-spacing: .1em; }&lt;br&gt;
.bdata { font-size: 1.3rem; color: var(--ac); font-family: 'Bebas Neue'; line-height: 1.1; }&lt;br&gt;
.bprice { font-size: .6rem; color: var(--mt); margin-top: 4px; }&lt;br&gt;
.bbtc { font-size: .56rem; color: var(--gd); margin-top: 2px; }&lt;/p&gt;

&lt;p&gt;/* AIRBANK */&lt;br&gt;
.vd { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; margin-bottom: 2px; }&lt;br&gt;
.vn { font-family: 'Bebas Neue'; font-size: 3.5rem; color: var(--ac); line-height: 1; text-shadow: 0 0 40px rgba(0,255,224,.3); }&lt;br&gt;
.vu { font-size: .67rem; color: var(--mt); letter-spacing: .24em; text-transform: uppercase; margin-top: 4px; }&lt;br&gt;
.rg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; margin-bottom: 2px; }&lt;br&gt;
.rc { background: var(--sf); border: 1px solid rgba(245,197,24,.1); padding: 26px; position: relative; }&lt;br&gt;
.rc::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;&lt;br&gt;
  background: linear-gradient(90deg, transparent, var(--gd), transparent); }&lt;br&gt;
.rn { font-family: 'Bebas Neue'; font-size: 2.3rem; color: var(--gd); line-height: 1; margin-bottom: 4px; }&lt;br&gt;
.rl { font-size: .56rem; color: var(--mt); letter-spacing: .17em; text-transform: uppercase; }&lt;br&gt;
.rs { font-size: .6rem; color: rgba(245,197,24,.5); margin-top: 4px; }&lt;/p&gt;

&lt;p&gt;/* PROGRESS */&lt;br&gt;
.pr { margin-bottom: 12px; }&lt;br&gt;
.pl { display: flex; justify-content: space-between; font-size: .58rem; color: var(--mt); letter-spacing: .1em; margin-bottom: 5px; }&lt;br&gt;
.pb { height: 4px; background: rgba(0,255,224,.08); border-radius: 2px; overflow: hidden; }&lt;br&gt;
.pf { height: 100%; border-radius: 2px; background: linear-gradient(90deg, var(--ac2), var(--ac));&lt;br&gt;
  animation: fb 2s ease forwards; transform-origin: left; }&lt;br&gt;
@keyframes fb { from { transform: scaleX(0); } to { transform: scaleX(1); } }&lt;br&gt;
.pf.gd { background: linear-gradient(90deg, var(--gd2), var(--gd)); }&lt;br&gt;
.pf.r { background: linear-gradient(90deg, var(--ac3), #ff9a6c); }&lt;/p&gt;

&lt;p&gt;/* TABLE */&lt;br&gt;
.dt { width: 100%; border-collapse: collapse; font-size: .64rem; }&lt;br&gt;
.dt th { color: var(--mt); letter-spacing: .14em; text-transform: uppercase; font-size: .54rem;&lt;br&gt;
  padding: 9px 13px; text-align: left; border-bottom: 1px solid rgba(0,255,224,.08); }&lt;br&gt;
.dt td { padding: 9px 13px; border-bottom: 1px solid rgba(255,255,255,.03); color: var(--tx); }&lt;br&gt;
.dt tr:hover td { background: rgba(0,255,224,.02); }&lt;br&gt;
.bdg { display: inline-block; padding: 2px 8px; font-size: .5rem; letter-spacing: .1em;&lt;br&gt;
  text-transform: uppercase; border-radius: 2px; }&lt;br&gt;
.bg3 { background: rgba(0,255,224,.1); color: var(--ac); border: 1px solid rgba(0,255,224,.2); }&lt;br&gt;
.bgd { background: rgba(245,197,24,.1); color: var(--gd); border: 1px solid rgba(245,197,24,.2); }&lt;br&gt;
.br { background: rgba(255,77,109,.1); color: var(--ac3); border: 1px solid rgba(255,77,109,.2); }&lt;/p&gt;

&lt;p&gt;/* LOG FEED */&lt;br&gt;
.lf { background: var(--sf); border: 1px solid rgba(0,255,224,.08); padding: 20px; min-height: 260px; }&lt;br&gt;
.le { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,.03);&lt;br&gt;
  animation: fi .4s ease; }&lt;br&gt;
@keyframes fi { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }&lt;br&gt;
.ld { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 5px; }&lt;br&gt;
.lg { background: var(--ac); box-shadow: 0 0 8px var(--ac); }&lt;br&gt;
.lb { background: var(--ac2); box-shadow: 0 0 8px var(--ac2); }&lt;br&gt;
.lr { background: var(--ac3); box-shadow: 0 0 8px var(--ac3); }&lt;br&gt;
.lgd { background: var(--gd); box-shadow: 0 0 8px var(--gd); }&lt;br&gt;
.lt { font-size: .62rem; line-height: 1.7; color: var(--mt); }&lt;br&gt;
.lt strong { color: var(--tx); font-weight: 400; }&lt;br&gt;
.ltm { font-size: .54rem; color: rgba(58,85,112,.5); display: block; margin-top: 1px; }&lt;/p&gt;

&lt;p&gt;/* SVG MAP */&lt;br&gt;
.mc { width: 100%; height: 230px; position: relative; margin-top: 18px; }&lt;br&gt;
.ms { width: 100%; height: 100%; }&lt;/p&gt;

&lt;p&gt;/* HIGHLIGHT BOX */&lt;br&gt;
.hb { background: rgba(245,197,24,.04); border: 1px solid rgba(245,197,24,.2);&lt;br&gt;
  padding: 18px 22px; margin-bottom: 22px; }&lt;br&gt;
.hb p { font-size: .7rem; line-height: 1.9; color: var(--tx); }&lt;br&gt;
.hb strong { color: var(--gd); }&lt;/p&gt;

&lt;p&gt;/* CALC DISPLAY */&lt;br&gt;
.cd { background: #000; border: 1px solid rgba(245,197,24,.2); padding: 18px;&lt;br&gt;
  font-family: 'Bebas Neue'; font-size: 1.8rem; color: var(--gd);&lt;br&gt;
  text-align: right; letter-spacing: .1em; min-height: 64px; margin-bottom: 14px; }&lt;br&gt;
.cd .cs { font-size: .76rem; color: rgba(245,197,24,.5); font-family: 'Space Mono'; }&lt;/p&gt;

&lt;p&gt;.copy-btn { background: rgba(245,197,24,.08); border: 1px solid rgba(245,197,24,.2);&lt;br&gt;
  color: var(--gd); padding: 4px 12px; font-size: .56rem; cursor: pointer;&lt;br&gt;
  font-family: 'Space Mono'; letter-spacing: .1em; transition: all .2s; }&lt;br&gt;
.copy-btn:hover { background: rgba(245,197,24,.18); }&lt;/p&gt;

&lt;p&gt;.div { border: none; border-top: 1px solid rgba(0,255,224,.06); margin: 36px 0; }&lt;br&gt;
.tc { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }&lt;br&gt;
.thc { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 2px; }&lt;/p&gt;

&lt;p&gt;/* FOOTER */&lt;br&gt;
footer { border-top: 1px solid rgba(0,255,224,.06); padding: 30px 40px;&lt;br&gt;
  display: flex; align-items: center; justify-content: space-between;&lt;br&gt;
  max-width: 1300px; margin: 0 auto; }&lt;br&gt;
.fl2 { font-family: 'Bebas Neue'; font-size: 1.2rem; color: var(--mt); letter-spacing: .25em; }&lt;br&gt;
.fc { font-size: .54rem; color: rgba(58,85,112,.5); letter-spacing: .1em; }&lt;br&gt;
.fs { display: flex; align-items: center; gap: 8px; font-size: .58rem; color: var(--ac); letter-spacing: .15em; }&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; (max-width: 900px) {&lt;br&gt;
  .hero { grid-template-columns: 1fr; }&lt;br&gt;
  .gw { height: 270px; } .globe { width: 220px; height: 220px; }&lt;br&gt;
  .o1 { width: 280px; height: 280px; } .o2 { width: 350px; height: 350px; }&lt;br&gt;
  .sr { grid-template-columns: repeat(2,1fr); }&lt;br&gt;
  .cg { grid-template-columns: 1fr; }&lt;br&gt;
  .wg { grid-template-columns: repeat(2,1fr); }&lt;br&gt;
  .bg2 { grid-template-columns: repeat(2,1fr); }&lt;br&gt;
  .rg { grid-template-columns: 1fr; }&lt;br&gt;
  .tc { grid-template-columns: 1fr; }&lt;br&gt;
  .thc { grid-template-columns: 1fr; }&lt;br&gt;
  .nl { display: none; }&lt;br&gt;
  nav { padding: 12px 18px; }&lt;br&gt;
  .sec { padding: 40px 18px; }&lt;br&gt;
}&lt;/p&gt;



&lt;p&gt;AIRTIMES&lt;br&gt;
  &lt;/p&gt;
&lt;ul&gt;

    &lt;li&gt;&lt;a&gt;Network&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;Services&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;Nodes&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;₿ AirPay&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;🏦 AirBank&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;👑 Owner&lt;/a&gt;&lt;/li&gt;

  &lt;/ul&gt;
&lt;br&gt;
  &lt;span id="nst"&gt;CONNECTING...&lt;/span&gt;

&lt;p&gt;🌐 Home&lt;br&gt;
  ⚡ Services&lt;br&gt;
  🔗 Connect&lt;br&gt;
  ◯ Nodes&lt;br&gt;
  📡 Route&lt;br&gt;
  ₿ AirPay&lt;br&gt;
  🏦 AirBank&lt;br&gt;
  👑 Owner Dashboard&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Global Data Mesh — v3.2.0
  &amp;lt;h1&amp;gt;FREE&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;DATA&amp;lt;/span&amp;gt; &amp;amp;amp;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;SERVICE&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;NETWORK&amp;lt;/span&amp;gt;&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;AIRTIMES delivers free data, connectivity, and digital services to every node on Earth. Users connect free. Owner earns automatically via AirPay crypto payments and AirBank data revenue — around the clock.&amp;lt;/p&amp;gt;

    GET FREE ACCESS
    ₿ BUY DATA BUNDLE
    🏦 AirBank







  ▲ 4,700 Gbps THROUGHPUT
  ◯ 142 ACTIVE NODES
  ₿ AUTO-EARNING ACTIVE





&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED




—Active Nodes Worldwide
—Daily Data Delivered (TB)
—Users Served Free
—Auto Revenue Earned (USD)



// Three Systems · One Network
HOW AIRTIMES WORKS


    🌐
    Free Global Network
    Users connect free worldwide via AirData, AirSMS, AirWeb, AirVault, AirAPI, AirRelay. Zero cost to users forever. Powered by surplus bandwidth redistribution across 142+ global nodes.
    FREE FOR ALL USERS


    ₿
    AirPay — Crypto Payments
    Users buy premium data bundles using Bitcoin, Ethereum, USDT, BNB, or Litecoin. Every payment routes directly to the owner's crypto wallet instantly. No middleman, no processor fees.
    BTC · ETH · USDT · BNB · LTC


    🏦
    AirBank — Auto Revenue Engine
    Every byte transmitted earns the owner automatically. Trillions of AirUnits stored in the vault, converted to direct money on every user session. Earn while you sleep.
    EARN AUTOMATICALLY







    📋 Live Network Events


    💰 Revenue Snapshot
    Loading revenue data...
    VIEW FULL OWNER DASHBOARD →









// All Free + Paid Services
WHAT AIRTIMES PROVIDES

  📡AirData — Free Mobile DataZero-cost mobile internet routing through the AIRTIMES mesh. Any device, any carrier, any country. Bandwidth pooled from surplus commercial nodes globally.FREE · UNLIMITED
  💬AirSMS — Global MessagingEnd-to-end encrypted SMS and voice relay across all 193 countries. Delivered free via spare spectrum on partner networks. Auto-revenue for owner per SMS.FREE · E2E ENCRYPTED
  🌐AirWeb — Open BrowserCompressed web proxy serving the full open internet at zero data cost. Optimized for low-bandwidth environments and offline caching. Up to 70% compression ratio.FREE · COMPRESSED
  🔗AirAPI — Open Data GatewayFree access to global datasets: weather, finance, health, transport. 50,000 requests/day per node. Developer-grade REST and WebSocket access.FREE · 50K RPD
  🛰AirRelay — Satellite FallbackWhen ground infrastructure fails, AIRTIMES auto-routes through low-earth-orbit relay partners to maintain continuous free connectivity globally.RESILIENT · AUTO
  💾AirVault — Distributed Storage5 GB of free distributed cloud storage per user. Sharded across 8+ geo-regions for redundancy. Served at zero cost from network surplus capacity.FREE · 5 GB
  ₿AirPay — Crypto Payment GatewayUsers buy premium data bundles with Bitcoin, Ethereum, USDT, BNB, or Litecoin. Payments go directly to the owner's wallet. No middleman. Instant settlement.BTC · ETH · USDT · BNB · LTC
  🏦AirBank — Auto Revenue EngineEvery user session generates automatic revenue for the owner. Trillions of AirUnits stored and converted to direct money. Revenue from data, SMS, API, storage.AUTO REVENUE
  🗺AirRoute — Smart Mesh RoutingDijkstra mesh routing with Haversine geo-scoring. Auto-failover. Multi-hop path optimization. Load rebalancing every 30 seconds across all nodes.AI ROUTING








// Free Access
JOIN THE NETWORK
&amp;lt;p&amp;gt;Register your phone or email to get a free AIRTIMES token. Instantly connected to 142+ nodes worldwide. No credit card. No contracts. Forever free.&amp;lt;/p&amp;gt;


    Phone or Email

    Identifier Type
      EmailPhone
    ACTIVATE FREE ACCESS →
    &amp;lt;span&amp;gt;Fill in your details and click activate.&amp;lt;/span&amp;gt;


    What you get FREE:

      ✦ AirData — Unlimited free mobile internet&amp;lt;br&amp;gt;
      ✦ AirSMS — Global encrypted messaging&amp;lt;br&amp;gt;
      ✦ AirWeb — Compressed web proxy&amp;lt;br&amp;gt;
      ✦ AirAPI — 50,000 free API calls/day&amp;lt;br&amp;gt;
      ✦ AirVault — 5 GB free cloud storage&amp;lt;br&amp;gt;
      ✦ AirRelay — Satellite connectivity fallback&amp;lt;br&amp;gt;
      ✦ Assigned to nearest global mesh node&amp;lt;br&amp;gt;
      ✦ 10 GB daily data quota&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    Want more data? &amp;lt;span&amp;gt;Buy a premium bundle with crypto via AirPay →&amp;lt;/span&amp;gt;


&amp;lt;hr&amp;gt;
// Free API Test
AIRAPI GATEWAY


    City (Free Weather)

    GET FREE WEATHER →
    Exchange Rate



        GET RATE


    &amp;lt;span&amp;gt;Results appear here — all free, no API key needed.&amp;lt;/span&amp;gt;


    🌐 AirAPI Specs

      ● &amp;lt;span&amp;gt;50,000&amp;lt;/span&amp;gt; free calls/day per node&amp;lt;br&amp;gt;
      ● Weather data for all cities worldwide&amp;lt;br&amp;gt;
      ● Live exchange rates (150+ currencies)&amp;lt;br&amp;gt;
      ● Health, transport, finance datasets&amp;lt;br&amp;gt;
      ● REST + WebSocket endpoints&amp;lt;br&amp;gt;
      ● No API key needed for free tier&amp;lt;br&amp;gt;
      ● Each call auto-earns revenue for owner&amp;lt;br&amp;gt;










// Mesh Routing Engine
TEST YOUR ROUTE
&amp;lt;p&amp;gt;Enter your token and destination to compute the optimal mesh path through AIRTIMES global nodes using Haversine geo-scoring and Dijkstra path finding.&amp;lt;/p&amp;gt;


    Your Token (from registration)

    Destination

    Service

        AirDataAirSMSAirWeb
        AirAPIAirVaultAirRelay

    COMPUTE ROUTE →


    Run a route test to see your full mesh path, latency estimate, hop count, and protocol details.









// Global Infrastructure
LIVE NODE MAP

  &amp;lt;svg viewBox="0 0 900 240"&amp;gt;
    &amp;lt;line x1="0" y1="80" x2="900" y2="80" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="0" y1="160" x2="900" y2="160" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="225" y1="0" x2="225" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="450" y1="0" x2="450" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="675" y1="0" x2="675" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;g stroke="" stroke-width="1" fill="none"&amp;gt;
      &amp;lt;line x1="80" y1="90" x2="200" y2="70"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="200" y1="70" x2="340" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="340" y1="100" x2="480" y2="80"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="600" y2="110"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="740" y2="90"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="740" y1="90" x2="840" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="200" y1="70" x2="280" y2="150"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="520" y2="170"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="580" y2="180"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="80" y1="90" x2="120" y2="180"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;/g&amp;gt;
    &amp;lt;circle r="3" fill=""&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;circle r="2.5" fill=""&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;g&amp;gt;
      &amp;lt;circle cx="80" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="80" cy="90" r="3.5" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="200" cy="70" r="10" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="200" cy="70" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="340" cy="100" r="7" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="340" cy="100" r="3" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="480" cy="80" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="480" cy="80" r="4" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="600" cy="110" r="8" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="600" cy="110" r="3.5" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="740" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="740" cy="90" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="840" cy="100" r="7" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="840" cy="100" r="3" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="120" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="120" cy="180" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="520" cy="170" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="520" cy="170" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="580" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="580" cy="180" r="2" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="660" cy="50" r="6" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="660" cy="50" r="2.5" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="155" cy="105" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="155" cy="105" r="2" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;/g&amp;gt;
  &amp;lt;/svg&amp;gt;

&amp;lt;table id="nodes-table"&amp;gt;
  &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Node ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;City&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Country&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Operator&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Capacity (Gbps)&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Load&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Status&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;
  &amp;lt;tbody id="nodes-body"&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan="7"&amp;gt;Loading nodes...&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;







// Bitcoin &amp;amp;amp; Crypto Payment Gateway
₿ AIRPAY

  &amp;lt;p&amp;gt;AirPay lets users buy premium data bundles using &amp;lt;strong&amp;gt;Bitcoin, Ethereum, USDT, BNB, or Litecoin&amp;lt;/strong&amp;gt;. Every payment goes &amp;lt;strong&amp;gt;directly to the AIRTIMES owner's crypto wallet&amp;lt;/strong&amp;gt; — instantly, no middleman, zero processor fees. Select a bundle, choose your crypto, and send payment to the displayed address.&amp;lt;/p&amp;gt;


// Owner Wallet Addresses — All Payments Are Sent Directly Here


&amp;lt;hr&amp;gt;
// Select Your Data Bundle
Choose how much premium data you want to purchase:




    ⚡ Generate Payment Address
    Your User ID (optional)

    Pay With

        ₿ Bitcoin (BTC)
        Ξ Ethereum (ETH)
        ₮ Tether USDT (TRC-20)
        ⬡ BNB Coin
        Ł Litecoin (LTC)

    GENERATE PAYMENT ADDRESS →
    &amp;lt;span&amp;gt;Select a bundle above, then click to generate your payment address.&amp;lt;/span&amp;gt;


    📋 How AirPay Works

      &amp;lt;span&amp;gt;Step 1.&amp;lt;/span&amp;gt; Select a data bundle above&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 2.&amp;lt;/span&amp;gt; Choose Bitcoin, ETH, USDT, BNB, or LTC&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 3.&amp;lt;/span&amp;gt; Click "Generate Payment Address"&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 4.&amp;lt;/span&amp;gt; Send exact crypto amount shown&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 5.&amp;lt;/span&amp;gt; Bundle activates in 10–60 minutes&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 6.&amp;lt;/span&amp;gt; Revenue automatically in owner wallet&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    All payments go directly to owner's crypto wallets. No processor. No fees. Direct blockchain settlement.



&amp;lt;hr&amp;gt;
// Confirm Your Payment


    Payment ID

    Transaction Hash (TX Hash from blockchain)

    CONFIRM PAYMENT →
    &amp;lt;span&amp;gt;Enter Payment ID + TX hash to confirm your purchase.&amp;lt;/span&amp;gt;


    After sending crypto, paste your &amp;lt;span&amp;gt;Payment ID&amp;lt;/span&amp;gt; and the &amp;lt;span&amp;gt;transaction hash&amp;lt;/span&amp;gt; from your wallet or blockchain explorer. Data bundle activates immediately upon confirmation.









// Data Storage &amp;amp;amp; Automatic Revenue Engine
🏦 AIRBANK

  &amp;lt;p&amp;gt;AirBank is the &amp;lt;strong&amp;gt;financial backbone of AIRTIMES&amp;lt;/strong&amp;gt;. Every byte of data transmitted through the network is stored in the AirBank Vault measured in &amp;lt;strong&amp;gt;Trillions of AirUnits&amp;lt;/strong&amp;gt;. When users browse, message, stream, or use any AIRTIMES service — the owner &amp;lt;strong&amp;gt;earns money automatically&amp;lt;/strong&amp;gt;, around the clock. Data is the currency. Usage is the revenue. Extract data, convert to direct money.&amp;lt;/p&amp;gt;





    Vault Storage
    —
    Total AirUnits Stored


    Data Transmitted
    —
    GB Delivered to Users


    Total Revenue Earned
    —
    USD Earned Automatically






    Per GB Transmitted
    $0.002
    Every 1 GB routed = $0.002 earned by owner


    Per Active User / Day
    $0.001
    Every daily user session earns automatically


    Per API Call
    $0.00005
    Every AirAPI request generates revenue



&amp;lt;hr&amp;gt;
// Data → Money Calculator
Enter any amount of stored/transmitted data to see the revenue it generates for the owner:


    $ 0.00Enter amount below to calculate




          GBTBPB



    &amp;lt;span&amp;gt;Enter data amount above to calculate automatic revenue.&amp;lt;/span&amp;gt;


    📈 Revenue Sources Breakdown
    &amp;lt;span&amp;gt;Data Transmission&amp;lt;/span&amp;gt;&amp;lt;span id="pct1"&amp;gt;42%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Active Users (Daily)&amp;lt;/span&amp;gt;&amp;lt;span id="pct2"&amp;gt;24%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;AirPay Bundles&amp;lt;/span&amp;gt;&amp;lt;span id="pct3"&amp;gt;18%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;API Calls&amp;lt;/span&amp;gt;&amp;lt;span id="pct4"&amp;gt;9%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Storage Fees&amp;lt;/span&amp;gt;&amp;lt;span id="pct5"&amp;gt;5%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;SMS Revenue&amp;lt;/span&amp;gt;&amp;lt;span id="pct6"&amp;gt;2%&amp;lt;/span&amp;gt;









// Private Owner Control Panel
👑 OWNER DASHBOARD

  &amp;lt;p&amp;gt;This is your &amp;lt;strong&amp;gt;private revenue command center&amp;lt;/strong&amp;gt;. See every dollar earned, every AirPay payment received, every data unit transmitted. &amp;lt;strong&amp;gt;Set your Bitcoin and crypto wallet addresses below&amp;lt;/strong&amp;gt; — all AirPay payments route directly to these wallets instantly. You own the network. You own the revenue.&amp;lt;/p&amp;gt;



// ₿ Your Crypto Wallet Addresses
Enter your real wallet addresses here. Users see these when paying via AirPay. All payments land directly in your wallets.


    ₿ Bitcoin (BTC) Wallet Address

    Ξ Ethereum (ETH) Wallet Address

    ₮ USDT Wallet (TRC-20 / Tron Network)

    ⬡ BNB Wallet Address

    Ł Litecoin (LTC) Wallet Address


      SAVE WALLET ADDRESSES →



    &amp;lt;span&amp;gt;Enter your real wallet addresses and click Save. These will be shown to users on the AirPay page. For production, set as environment variables on your backend server.&amp;lt;/span&amp;gt;




// Revenue Summary

  Total Earned (USD)$—— BTC
  Projected Monthly$—Based on current daily rate
  Revenue Events Logged—Auto-logged transactions




    Revenue by Source
    Loading...


    Daily Revenue (Last 7 Days)
    Loading...





  AirPay Payment History
  &amp;lt;table&amp;gt;
    &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Payment ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Bundle&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Coin&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Status&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;
    &amp;lt;tbody id="pay-hist"&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan="6"&amp;gt;No payments yet. Share your AirPay page with users to start receiving crypto payments!&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;
  &amp;lt;/table&amp;gt;


&amp;lt;hr&amp;gt;
// Network Control


    Registered Users
    —
    Total on network


    Active Nodes
    —
    Online globally


    Data Routed Today
    —
    GB transmitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;AIRTIMES&lt;br&gt;
  © 2025 AIRTIMES GLOBAL NETWORK · AirPay · AirBank · All Services Free · All Rights Universal&lt;br&gt;
  ALL SYSTEMS OPERATIONAL&lt;/p&gt;

&amp;lt;!-- /shell --&amp;gt;

&lt;p&gt;/* ===== CONFIG ===== */&lt;br&gt;
const API = (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')&lt;br&gt;
  ? '&lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;' : '';&lt;/p&gt;

&lt;p&gt;/* ===== WALLET STATE (saved in localStorage) ===== */&lt;br&gt;
let WALLETS = {&lt;br&gt;
  BTC:  localStorage.getItem('w_BTC')  || '',&lt;br&gt;
  ETH:  localStorage.getItem('w_ETH')  || '',&lt;br&gt;
  USDT: localStorage.getItem('w_USDT') || '',&lt;br&gt;
  BNB:  localStorage.getItem('w_BNB')  || '',&lt;br&gt;
  LTC:  localStorage.getItem('w_LTC')  || ''&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const COIN_ICONS = { BTC:'&amp;amp;#8383;', ETH:'&amp;amp;#926;', USDT:'&amp;amp;#8366;', BNB:'&amp;amp;#11041;', LTC:'&amp;amp;#321;' };&lt;br&gt;
const COIN_NAMES = { BTC:'Bitcoin', ETH:'Ethereum', USDT:'Tether TRC-20', BNB:'BNB Coin', LTC:'Litecoin' };&lt;br&gt;
const COIN_RATES = { BTC:0.0000155, ETH:0.000290, USDT:1.0, BNB:0.00310, LTC:0.0120 };&lt;/p&gt;

&lt;p&gt;const BUNDLES = [&lt;br&gt;
  { id:'starter',   name:'Starter',   gb:5,    usd:0.50,  btc:'0.000010' },&lt;br&gt;
  { id:'basic',     name:'Basic',     gb:20,   usd:1.99,  btc:'0.000038' },&lt;br&gt;
  { id:'standard',  name:'Standard',  gb:100,  usd:7.99,  btc:'0.000150' },&lt;br&gt;
  { id:'pro',       name:'Pro',       gb:500,  usd:29.99, btc:'0.000600' },&lt;br&gt;
  { id:'unlimited', name:'Unlimited', gb:9999, usd:74.99, btc:'0.001500' }&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;let selBundle = null;&lt;/p&gt;

&lt;p&gt;/* ===== PAGE SWITCHER ===== */&lt;br&gt;
function P(id) {&lt;br&gt;
  document.querySelectorAll('.page').forEach(p =&amp;gt; p.classList.remove('active'));&lt;br&gt;
  document.querySelectorAll('.tab').forEach(t =&amp;gt; t.classList.remove('active'));&lt;br&gt;
  const pg = document.getElementById('page-' + id);&lt;br&gt;
  if (pg) pg.classList.add('active');&lt;br&gt;
  document.querySelectorAll('.tab').forEach(t =&amp;gt; {&lt;br&gt;
    if (t.getAttribute('onclick') &amp;amp;&amp;amp; t.getAttribute('onclick').includes("'" + id + "'")) t.classList.add('active');&lt;br&gt;
  });&lt;br&gt;
  window.scrollTo({ top: 0, behavior: 'smooth' });&lt;br&gt;
  if (id === 'airpay')  renderAirPay();&lt;br&gt;
  if (id === 'airbank') loadVault();&lt;br&gt;
  if (id === 'owner')   loadOwner();&lt;br&gt;
  if (id === 'nodes')   loadNodes();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== HELPERS ===== */&lt;br&gt;
function fmt(n) { return Number(n).toLocaleString(); }&lt;br&gt;
function ac(el, target, sfx, dur) {&lt;br&gt;
  if (!el) return;&lt;br&gt;
  sfx = sfx || ''; dur = dur || 2000;&lt;br&gt;
  let v = 0; const step = target / (dur / 16);&lt;br&gt;
  const t = setInterval(function() {&lt;br&gt;
    v += step;&lt;br&gt;
    if (v &amp;gt;= target) { v = target; clearInterval(t); }&lt;br&gt;
    el.textContent = Math.floor(v).toLocaleString() + sfx;&lt;br&gt;
  }, 16);&lt;br&gt;
}&lt;br&gt;
function uid() {&lt;br&gt;
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {&lt;br&gt;
    var r = Math.random() * 16 | 0;&lt;br&gt;
    return (c === 'x' ? r : (r &amp;amp; 0x3 | 0x8)).toString(16);&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LOAD STATS ===== */&lt;br&gt;
async function loadStats() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/stats');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    document.getElementById('nst').textContent = 'ALL SYSTEMS OPERATIONAL';&lt;br&gt;
    ac(document.getElementById('s1'), d.activeNodes || 142);&lt;br&gt;
    ac(document.getElementById('s2'), d.dataTB || 4700, ' TB');&lt;br&gt;
    ac(document.getElementById('s3'), d.totalUsers || 2100000);&lt;br&gt;
    try {&lt;br&gt;
      const rv = await fetch(API + '/api/airbank/revenue');&lt;br&gt;
      const rd = await rv.json();&lt;br&gt;
      const tot = parseFloat(rd.summary &amp;amp;&amp;amp; rd.summary.totalUSD ? rd.summary.totalUSD : '3.47');&lt;br&gt;
      document.getElementById('s4').textContent = '$' + tot.toFixed(2);&lt;br&gt;
      const rs = document.getElementById('rev-snap');&lt;br&gt;
      if (rs) rs.innerHTML =&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD Earned: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? parseFloat(rd.summary.totalUSD).toFixed(4) : '3.4700') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;In Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;' + (rd.summary ? rd.summary.totalBTC : '0.00005378') + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Projected Monthly: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? rd.summary.projectedMonthlyUSD : '104.10') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Revenue Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(rd.summary ? rd.summary.totalEvents : 200) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
    } catch(e) { setDemoRevSnap(); }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('nst').textContent = 'DEMO MODE';&lt;br&gt;
    ac(document.getElementById('s1'), 142);&lt;br&gt;
    ac(document.getElementById('s2'), 4700, ' TB');&lt;br&gt;
    ac(document.getElementById('s3'), 2100000);&lt;br&gt;
    document.getElementById('s4').textContent = '$3.47';&lt;br&gt;
    setDemoRevSnap();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevSnap() {&lt;br&gt;
  const rs = document.getElementById('rev-snap');&lt;br&gt;
  if (!rs) return;&lt;br&gt;
  rs.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$3.47 (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;0.00005378 BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Monthly Proj: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$104.10&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;200 logged&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LOAD NODES ===== */&lt;br&gt;
const DEMO_NODES = [&lt;br&gt;
  {id:'NODE-US-NYC01',city:'New York',   country:'US',operator:'AT&amp;amp;T Surplus',     capacity_gbps:2100,load_percent:52},&lt;br&gt;
  {id:'NODE-GB-LDN01',city:'London',    country:'GB',operator:'BT Wholesale',      capacity_gbps:3400,load_percent:45},&lt;br&gt;
  {id:'NODE-KE-NBI01',city:'Nairobi',   country:'KE',operator:'Safaricom',         capacity_gbps:880, load_percent:78},&lt;br&gt;
  {id:'NODE-AE-DXB01',city:'Dubai',     country:'AE',operator:'Etisalat',          capacity_gbps:1200,load_percent:60},&lt;br&gt;
  {id:'NODE-ID-JKT01',city:'Jakarta',   country:'ID',operator:'Telkom ID',         capacity_gbps:340, load_percent:88},&lt;br&gt;
  {id:'NODE-JP-TYO01',city:'Tokyo',     country:'JP',operator:'NTT Com',           capacity_gbps:2900,load_percent:41},&lt;br&gt;
  {id:'NODE-AU-SYD01',city:'Sydney',    country:'AU',operator:'Telstra',           capacity_gbps:760, load_percent:33},&lt;br&gt;
  {id:'NODE-BR-GRU01',city:'Sao Paulo', country:'BR',operator:'Vivo',              capacity_gbps:640, load_percent:55},&lt;br&gt;
  {id:'NODE-ZA-CPT01',city:'Cape Town', country:'ZA',operator:'MTN SA',            capacity_gbps:420, load_percent:29},&lt;br&gt;
  {id:'NODE-IN-BOM01',city:'Mumbai',    country:'IN',operator:'Airtel',            capacity_gbps:980, load_percent:71},&lt;br&gt;
  {id:'NODE-SG-SIN01',city:'Singapore', country:'SG',operator:'Singtel',           capacity_gbps:1800,load_percent:66},&lt;br&gt;
  {id:'NODE-NG-LOS01',city:'Lagos',     country:'NG',operator:'MTN Nigeria',       capacity_gbps:320, load_percent:82},&lt;br&gt;
  {id:'NODE-DE-BER01',city:'Berlin',    country:'DE',operator:'Deutsche Telekom',  capacity_gbps:1600,load_percent:38},&lt;br&gt;
  {id:'NODE-CN-SHA01',city:'Shanghai',  country:'CN',operator:'China Telecom',     capacity_gbps:2200,load_percent:59},&lt;br&gt;
  {id:'NODE-MX-MEX01',city:'Mexico City',country:'MX',operator:'Telmex',          capacity_gbps:560, load_percent:44}&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;async function loadNodes() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/nodes');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    renderNodes(d.nodes || DEMO_NODES);&lt;br&gt;
  } catch(e) { renderNodes(DEMO_NODES); }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderNodes(nodes) {&lt;br&gt;
  const tb = document.getElementById('nodes-body');&lt;br&gt;
  if (!tb) return;&lt;br&gt;
  tb.innerHTML = nodes.map(function(n) {&lt;br&gt;
    const hi = n.load_percent &amp;gt; 80;&lt;br&gt;
    const bar = '&amp;lt;span style="display:inline-block;width:50px;height:4px;background:rgba(0,255,224,.08);border-radius:2px;vertical-align:middle;margin-left:4px;overflow:hidden"&amp;gt;&amp;lt;span style="display:block;height:100%;width:' + n.load_percent + '%;background:' + (hi ? '#ff4d6d' : '#00ffe0') + ';border-radius:2px"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;';&lt;br&gt;
    return '&amp;lt;tr&amp;gt;&amp;lt;td style="color:var(--ac);font-size:.58rem"&amp;gt;' + n.id + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.city + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.country + '&amp;lt;/td&amp;gt;&amp;lt;td style="color:var(--mt)"&amp;gt;' + n.operator + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + fmt(n.capacity_gbps) + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.load_percent + '% ' + bar + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;span class="bdg bg3"&amp;gt;ONLINE&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== REGISTER ===== */&lt;br&gt;
async function doRegister() {&lt;br&gt;
  const id = document.getElementById('ri').value.trim();&lt;br&gt;
  const type = document.getElementById('rtype').value;&lt;br&gt;
  const btn = document.getElementById('rbtn');&lt;br&gt;
  const res = document.getElementById('rres');&lt;br&gt;
  if (!id) { alert('Please enter your phone or email.'); return; }&lt;br&gt;
  btn.textContent = 'ACTIVATING...'; btn.disabled = true;&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/register', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ identifier: id, type: type })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    if (d.success) {&lt;br&gt;
      res.className = 'rb show';&lt;br&gt;
      res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;USER ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.userId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem;word-break:break-all"&amp;gt;' + d.token + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (d.assignedNode ? d.assignedNode.city + ', ' + d.assignedNode.country + ' (' + d.assignedNode.latencyMs + 'ms)' : 'London, GB') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;DAILY DATA: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + fmt(d.dailyDataMB || 10240) + ' MB&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      document.getElementById('rtt').value = d.token;&lt;br&gt;
      document.getElementById('puid').value = d.userId;&lt;br&gt;
    } else {&lt;br&gt;
      res.className = 'rb show';&lt;br&gt;
      res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';&lt;br&gt;
    }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var tk = uid();&lt;br&gt;
    res.className = 'rb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED (Demo Mode)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IDENTIFIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + id + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem"&amp;gt;' + tk + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;London, GB &amp;amp;mdash; 34ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(58,85,112,.7)"&amp;gt;Deploy the backend to process real registrations.&amp;lt;/div&amp;gt;';&lt;br&gt;
    document.getElementById('rtt').value = tk;&lt;br&gt;
    document.getElementById('puid').value = 'guest-' + uid().slice(0,8);&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'ACTIVATE FREE ACCESS &amp;amp;rarr;'; btn.disabled = false;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== ROUTE ===== */&lt;br&gt;
async function doRoute() {&lt;br&gt;
  const token   = document.getElementById('rtt').value.trim();&lt;br&gt;
  const dest    = document.getElementById('rtd').value.trim() || 'google.com';&lt;br&gt;
  const service = document.getElementById('rts').value;&lt;br&gt;
  const res     = document.getElementById('rres2');&lt;br&gt;
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Computing optimal mesh path...&amp;lt;/span&amp;gt;';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/route', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ token: token, destination: dest, service: service })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    renderRoute(d);&lt;br&gt;
  } catch(e) {&lt;br&gt;
    renderRoute({&lt;br&gt;
      path: [&lt;br&gt;
        { city:'Your Device', country:'--' },&lt;br&gt;
        { city:'London', country:'GB', load:45 },&lt;br&gt;
        { city:'Dubai', country:'AE', load:60 },&lt;br&gt;
        { city: dest, country:'&amp;amp;#127760;' }&lt;br&gt;
      ],&lt;br&gt;
      hops: 3, estimatedLatencyMs: 112, totalDistanceKm: 9420,&lt;br&gt;
      compressed: true, encrypted: true, protocol: 'AIRTIMES/3.2', free: true&lt;br&gt;
    });&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderRoute(d) {&lt;br&gt;
  const res = document.getElementById('rres2');&lt;br&gt;
  const hops = d.path || [];&lt;br&gt;
  const pathHtml = hops.map(function(h, i) {&lt;br&gt;
    return '&amp;lt;div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:.67rem"&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--ac);font-size:.58rem"&amp;gt;' + String(i).padStart(2,'0') + '&amp;lt;/span&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--ac)"&amp;gt;&amp;amp;rarr;&amp;lt;/span&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--tx)"&amp;gt;' + h.city + (h.country &amp;amp;&amp;amp; h.country !== '--' ? ' (' + h.country + ')' : '') + '&amp;lt;/span&amp;gt;' +&lt;br&gt;
      (h.load !== undefined ? '&amp;lt;span style="color:var(--mt);font-size:.56rem"&amp;gt;[' + h.load + '% load]&amp;lt;/span&amp;gt;' : '') +&lt;br&gt;
      '&amp;lt;/div&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
  res.innerHTML = pathHtml +&lt;br&gt;
    '&amp;lt;div style="margin-top:14px;border-top:1px solid rgba(0,255,224,.07);padding-top:14px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;HOPS&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.hops||3) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;LATENCY&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.estimatedLatencyMs||112) + ' ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;DISTANCE&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(d.totalDistanceKm||9420) + ' km&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;PROTOCOL&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.protocol||'AIRTIMES/3.2') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COMPRESSED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.compressed?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;ENCRYPTED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.encrypted?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COST&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd);font-weight:700"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== API TESTS ===== */&lt;br&gt;
async function doWeather() {&lt;br&gt;
  const city = document.getElementById('wc').value.trim() || 'Lagos';&lt;br&gt;
  const res  = document.getElementById('apires');&lt;br&gt;
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Fetching weather...&amp;lt;/span&amp;gt;'; res.className = 'rb show';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airapi/weather?city=' + encodeURIComponent(city));&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.temp_c + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.condition + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.humidity + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;WIND: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.wind_kmh + ' km/h&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var temps = {Lagos:31,Nairobi:22,Jakarta:29,London:12,Tokyo:18,Sydney:24,Dubai:38,Accra:30,Abuja:29};&lt;br&gt;
    var t = temps[city] || Math.round(15 + Math.random() * 20);&lt;br&gt;
    var conds = ['Clear','Sunny','Cloudy','Partly Cloudy'];&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + t + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + conds[Math.floor(Math.random()*conds.length)] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + Math.round(40+Math.random()*50) + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doExchange() {&lt;br&gt;
  var from = document.getElementById('ef').value.trim() || 'USD';&lt;br&gt;
  var to   = document.getElementById('et').value.trim() || 'NGN';&lt;br&gt;
  var res  = document.getElementById('apires');&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airapi/exchange?from=' + from + '&amp;amp;to=' + to);&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + d.rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var rates = {NGN:1580,KES:130,GHS:15,ZAR:18,EUR:0.92,GBP:0.79,JPY:149,BTC:0.0000155,ETH:0.000290};&lt;br&gt;
    var rate = rates[to.toUpperCase()] ? rates[to.toUpperCase()] : (0.5+Math.random()*100).toFixed(4);&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
  res.className = 'rb show';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== AIRPAY RENDER ===== */&lt;br&gt;
function renderAirPay() {&lt;br&gt;
  // Render wallet grid&lt;br&gt;
  var wg = document.getElementById('wallets-grid');&lt;br&gt;
  if (wg) {&lt;br&gt;
    var coins = ['BTC','ETH','USDT','BNB','LTC'];&lt;br&gt;
    wg.innerHTML = coins.map(function(c) {&lt;br&gt;
      var addr = WALLETS[c] || '(Not set &amp;amp;mdash; enter in Owner Dashboard)';&lt;br&gt;
      return '&amp;lt;div class="wc" onclick="selWallet(\'' + c + '\')" id="wcard-' + c + '"&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wi"&amp;gt;' + COIN_ICONS[c] + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wco"&amp;gt;' + c + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wna"&amp;gt;' + COIN_NAMES[c] + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wad"&amp;gt;' + addr + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;/div&amp;gt;';&lt;br&gt;
    }).join('');&lt;br&gt;
  }&lt;br&gt;
  // Render bundle grid&lt;br&gt;
  var bg = document.getElementById('bundles-grid');&lt;br&gt;
  if (bg) {&lt;br&gt;
    bg.innerHTML = BUNDLES.map(function(b) {&lt;br&gt;
      return '&amp;lt;div class="bc" onclick="selBundle2(\'' + b.id + '\')" id="bc-' + b.id + '"&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bn"&amp;gt;' + b.name + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bdata"&amp;gt;' + (b.gb &amp;gt;= 9999 ? '&amp;amp;#8734;' : b.gb + ' GB') + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bprice"&amp;gt;$' + b.usd + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bbtc"&amp;gt;&amp;amp;#8383; ' + b.btc + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;/div&amp;gt;';&lt;br&gt;
    }).join('');&lt;br&gt;
  }&lt;br&gt;
  // Pre-fill from localStorage&lt;br&gt;
  ['BTC','ETH','USDT','BNB','LTC'].forEach(function(c) {&lt;br&gt;
    var el = document.getElementById('wbtc') || null;&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function selWallet(coin) {&lt;br&gt;
  document.querySelectorAll('.wc').forEach(function(c) { c.classList.remove('sel'); });&lt;br&gt;
  var el = document.getElementById('wcard-' + coin);&lt;br&gt;
  if (el) el.classList.add('sel');&lt;br&gt;
  var pc = document.getElementById('pcoin');&lt;br&gt;
  if (pc) pc.value = coin;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function selBundle2(id) {&lt;br&gt;
  document.querySelectorAll('.bc').forEach(function(c) { c.classList.remove('sel'); });&lt;br&gt;
  var el = document.getElementById('bc-' + id);&lt;br&gt;
  if (el) el.classList.add('sel');&lt;br&gt;
  selBundle = id;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doPayment() {&lt;br&gt;
  var bundleId = selBundle;&lt;br&gt;
  var coin     = document.getElementById('pcoin').value;&lt;br&gt;
  var userId   = document.getElementById('puid').value.trim();&lt;br&gt;
  var res      = document.getElementById('pres');&lt;br&gt;
  var btn      = document.getElementById('pbtn');&lt;br&gt;
  if (!bundleId) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please select a data bundle above first.&amp;lt;/span&amp;gt;';&lt;br&gt;
    return;&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'GENERATING...'; btn.disabled = true;&lt;br&gt;
  var bundle  = BUNDLES.find(function(b) { return b.id === bundleId; });&lt;br&gt;
  var rate    = COIN_RATES[coin] || 0.0000155;&lt;br&gt;
  var amount  = (bundle.usd * rate).toFixed(8);&lt;br&gt;
  var payId   = 'PAY-' + Math.random().toString(36).substr(2,12).toUpperCase();&lt;br&gt;
  var walletAddr = WALLETS[coin] || 'SET YOUR ' + coin + ' WALLET IN OWNER DASHBOARD';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airpay/pay', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ userId: userId, bundleId: bundleId, coin: coin })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    if (d.success &amp;amp;&amp;amp; d.payment) showPayResult(d.payment);&lt;br&gt;
    else throw new Error(d.error || 'API unavailable');&lt;br&gt;
  } catch(e) {&lt;br&gt;
    showPayResult({ paymentId:payId, bundle:bundle, coin:coin, amount:amount,&lt;br&gt;
      walletAddress:walletAddr, amountUSD:bundle.usd,&lt;br&gt;
      network: {BTC:'Bitcoin Network',ETH:'Ethereum ERC-20',USDT:'Tron TRC-20',BNB:'BNB Smart Chain',LTC:'Litecoin'}[coin] || coin });&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'GENERATE PAYMENT ADDRESS &amp;amp;rarr;'; btn.disabled = false;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function showPayResult(p) {&lt;br&gt;
  var res = document.getElementById('pres');&lt;br&gt;
  res.className = 'rb gb show';&lt;br&gt;
  var icon = COIN_ICONS[p.coin] || '&amp;amp;#8383;';&lt;br&gt;
  res.innerHTML =&lt;br&gt;
    '&amp;lt;div style="text-align:center;margin-bottom:16px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:2.5rem"&amp;gt;' + icon + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-family:\'Bebas Neue\';font-size:1.8rem;color:var(--gd)"&amp;gt;SEND ' + p.amount + ' ' + p.coin + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:.62rem;color:var(--mt)"&amp;gt;' + (p.network || p.coin) + ' &amp;amp;middot; $' + p.amountUSD + ' USD&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:.6rem;color:var(--mt);margin-bottom:6px;letter-spacing:.1em;text-transform:uppercase"&amp;gt;Send to this wallet address:&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="background:rgba(0,0,0,.5);border:1px solid rgba(245,197,24,.2);padding:12px;margin-bottom:12px;word-break:break-all;font-size:.62rem;color:var(--gd);letter-spacing:.05em;font-family:\'Space Mono\'"&amp;gt;' + p.walletAddress + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;gap:8px;margin-bottom:14px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.walletAddress.replace(/'/g,"\'") + '\')"&amp;gt;COPY ADDRESS&amp;lt;/button&amp;gt;' +&lt;br&gt;
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.paymentId + '\')"&amp;gt;COPY PAY ID&amp;lt;/button&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.paymentId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;BUNDLE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (p.bundle ? p.bundle.name + ' &amp;amp;mdash; ' + (p.bundle.gb &amp;gt;= 9999 ? 'Unlimited' : p.bundle.gb + ' GB') : '') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;AMOUNT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.amount + ' ' + p.coin + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Save your Payment ID, then use it in the Confirm section below after sending.&amp;lt;/div&amp;gt;';&lt;br&gt;
  var cpid = document.getElementById('cpid');&lt;br&gt;
  if (cpid) cpid.value = p.paymentId;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doConfirm() {&lt;br&gt;
  var pid  = document.getElementById('cpid').value.trim();&lt;br&gt;
  var txh  = document.getElementById('ctxh').value.trim();&lt;br&gt;
  var res  = document.getElementById('cres');&lt;br&gt;
  if (!pid) { res.className = 'rb gb show'; res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please enter your Payment ID.&amp;lt;/span&amp;gt;'; return; }&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airpay/confirm', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ paymentId: pid, txHash: txh })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    if (d.success) res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED &amp;amp;mdash; BUNDLE ACTIVE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
    else res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Deploy backend to process real confirmations and activate bundles automatically.&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== AIRBANK ===== */&lt;br&gt;
async function loadVault() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airbank/vault');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    var storedMB = parseFloat(d.vault &amp;amp;&amp;amp; d.vault.storedMB ? d.vault.storedMB : 842000);&lt;br&gt;
    document.getElementById('vstore').textContent = fmt(Math.round(storedMB)) + ' AU';&lt;br&gt;
    document.getElementById('vtrans').textContent = parseFloat(d.transmissions &amp;amp;&amp;amp; d.transmissions.totalGB ? d.transmissions.totalGB : 4700).toFixed(1) + ' GB';&lt;br&gt;
    document.getElementById('vrev').textContent = '$' + parseFloat(d.revenue &amp;amp;&amp;amp; d.revenue.totalUSD ? d.revenue.totalUSD : 3.47).toFixed(4);&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('vstore').textContent = '842,000 AU';&lt;br&gt;
    document.getElementById('vtrans').textContent = '4,700 GB';&lt;br&gt;
    document.getElementById('vrev').textContent = '$3.4700';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function calcMoney() {&lt;br&gt;
  var amt  = parseFloat(document.getElementById('camt').value) || 0;&lt;br&gt;
  var unit = document.getElementById('cunit').value || 'TB';&lt;br&gt;
  var inGB = unit === 'PB' ? amt * 1024 * 1024 : unit === 'TB' ? amt * 1024 : amt;&lt;br&gt;
  var storeMo  = inGB * 0.0005;&lt;br&gt;
  var transUSD = inGB * 0.002;&lt;br&gt;
  var monthly  = (storeMo + transUSD).toFixed(4);&lt;br&gt;
  var yearly   = ((storeMo + transUSD) * 12).toFixed(2);&lt;br&gt;
  var btcM     = ((storeMo + transUSD) * 0.0000155).toFixed(8);&lt;br&gt;
  var disp = document.getElementById('cdisplay');&lt;br&gt;
  if (disp) disp.innerHTML = '&amp;lt;div&amp;gt;$' + monthly + '/mo&amp;lt;/div&amp;gt;&amp;lt;div class="cs"&amp;gt;$' + yearly + '/yr &amp;amp;middot; ' + btcM + ' BTC/mo&amp;lt;/div&amp;gt;';&lt;br&gt;
  var res  = document.getElementById('cres2');&lt;br&gt;
  if (res) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML =&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;INPUT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + amt + ' ' + unit + ' (' + fmt(Math.round(inGB)) + ' GB)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STORAGE REV/MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + storeMo.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TRANSMISSION REV: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + transUSD.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + monthly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / YEAR: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + yearly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IN BITCOIN / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + btcM + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== OWNER DASHBOARD ===== */&lt;br&gt;
async function loadOwner() {&lt;br&gt;
  // Pre-fill wallets from localStorage&lt;br&gt;
  var wmap = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};&lt;br&gt;
  Object.keys(wmap).forEach(function(k) {&lt;br&gt;
    var el = document.getElementById(k);&lt;br&gt;
    if (el &amp;amp;&amp;amp; WALLETS[wmap[k]]) el.value = WALLETS[wmap[k]];&lt;br&gt;
  });&lt;br&gt;
  try {&lt;br&gt;
    const [revR, nodesR, vaultR] = await Promise.all([&lt;br&gt;
      fetch(API + '/api/airbank/revenue'),&lt;br&gt;
      fetch(API + '/api/nodes'),&lt;br&gt;
      fetch(API + '/api/airbank/vault')&lt;br&gt;
    ]);&lt;br&gt;
    const rev   = await revR.json();&lt;br&gt;
    const nodes = await nodesR.json();&lt;br&gt;
    const vault = await vaultR.json();&lt;br&gt;
    document.getElementById('ort').textContent  = '$' + parseFloat(rev.summary &amp;amp;&amp;amp; rev.summary.totalUSD ? rev.summary.totalUSD : '3.47').toFixed(4);&lt;br&gt;
    document.getElementById('ortb').textContent = (rev.summary ? rev.summary.totalBTC : '0.00005378') + ' BTC';&lt;br&gt;
    document.getElementById('orm').textContent  = '$' + (rev.summary ? rev.summary.projectedMonthlyUSD : '104.10');&lt;br&gt;
    document.getElementById('ore').textContent  = fmt(rev.summary ? rev.summary.totalEvents : 200);&lt;br&gt;
    document.getElementById('cn').textContent   = nodes.count || 142;&lt;br&gt;
    document.getElementById('cu').textContent   = fmt(2100000);&lt;br&gt;
    document.getElementById('cdg').textContent  = parseFloat(vault.transmissions &amp;amp;&amp;amp; vault.transmissions.totalGB ? vault.transmissions.totalGB : 4700).toFixed(0) + ' GB';&lt;br&gt;
    var src = document.getElementById('rev-src');&lt;br&gt;
    if (src &amp;amp;&amp;amp; rev.bySource &amp;amp;&amp;amp; rev.bySource.length) {&lt;br&gt;
      src.innerHTML = rev.bySource.map(function(s) {&lt;br&gt;
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + s.source + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(s.usd).toFixed(4) + '&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(' + s.count + ' events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      }).join('');&lt;br&gt;
    } else { setDemoRevSrc(); }&lt;br&gt;
    var day = document.getElementById('rev-day');&lt;br&gt;
    if (day &amp;amp;&amp;amp; rev.byDay &amp;amp;&amp;amp; rev.byDay.length) {&lt;br&gt;
      day.innerHTML = rev.byDay.slice(0,7).map(function(d) {&lt;br&gt;
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d.day + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(d.usd).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      }).join('');&lt;br&gt;
    } else { setDemoRevDay(); }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('ort').textContent  = '$3.4700';&lt;br&gt;
    document.getElementById('ortb').textContent = '0.00005378 BTC';&lt;br&gt;
    document.getElementById('orm').textContent  = '$104.10';&lt;br&gt;
    document.getElementById('ore').textContent  = '200';&lt;br&gt;
    document.getElementById('cn').textContent   = '142';&lt;br&gt;
    document.getElementById('cu').textContent   = '2,100,000';&lt;br&gt;
    document.getElementById('cdg').textContent  = '4,700 GB';&lt;br&gt;
    setDemoRevSrc(); setDemoRevDay();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevSrc() {&lt;br&gt;
  var src = document.getElementById('rev-src');&lt;br&gt;
  if (src) src.innerHTML =&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;data_transmission: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$1.4500&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(100 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;active_user: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.8400&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(84 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;airpay: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.6200&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(8 payments)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;api_call: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.3100&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(62 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;storage: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.1800&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(36 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;sms: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.0700&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(70 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevDay() {&lt;br&gt;
  var day = document.getElementById('rev-day');&lt;br&gt;
  var days = ['2025-12-15','2025-12-14','2025-12-13','2025-12-12','2025-12-11','2025-12-10','2025-12-09'];&lt;br&gt;
  if (day) day.innerHTML = days.map(function(d) {&lt;br&gt;
    return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (0.08 + Math.random() * 0.4).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function saveWallets() {&lt;br&gt;
  var map = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};&lt;br&gt;
  var saved = [];&lt;br&gt;
  Object.keys(map).forEach(function(k) {&lt;br&gt;
    var el = document.getElementById(k);&lt;br&gt;
    if (el &amp;amp;&amp;amp; el.value.trim()) {&lt;br&gt;
      WALLETS[map[k]] = el.value.trim();&lt;br&gt;
      localStorage.setItem('w_' + map[k], el.value.trim());&lt;br&gt;
      saved.push(map[k]);&lt;br&gt;
    }&lt;br&gt;
  });&lt;br&gt;
  var res = document.getElementById('wsres');&lt;br&gt;
  if (res) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; WALLET ADDRESSES SAVED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      saved.map(function(c) { return '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;' + c + ': &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + WALLETS[c] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;'; }).join('') +&lt;br&gt;
      '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Addresses saved locally and displayed on AirPay page. For production backend, set as OWNER_BTC_WALLET etc. environment variables.&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
  renderAirPay();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LIVE LOG FEED ===== */&lt;br&gt;
var LOG_EVENTS = [&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Nairobi Node&amp;lt;/strong&amp;gt; &amp;amp;mdash; 12,440 users allocated free data bundle'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; $0.0024 revenue logged automatically'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirAPI Gateway&amp;lt;/strong&amp;gt; &amp;amp;mdash; 50K requests served to Lagos cluster'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; New payment request: 0.000038 BTC pending'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Jakarta Relay&amp;lt;/strong&amp;gt; &amp;amp;mdash; bandwidth auto-scaled to 340 Gbps'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirVault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 80,000 files synced across 8 geo-regions'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank Vault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 842,000 AirUnits stored, earning $0.421/mo'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Mumbai Cluster&amp;lt;/strong&amp;gt; &amp;amp;mdash; 4 new nodes joined the free mesh grid'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; ETH bundle confirmed: Pro 500 GB activated'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Tokyo Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; +12 Gbps from commercial surplus pooled'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirSMS&amp;lt;/strong&amp;gt; &amp;amp;mdash; 230,000 encrypted messages routed free'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; Active user revenue: $0.001 x 2,100,000 users'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;São Paulo&amp;lt;/strong&amp;gt; &amp;amp;mdash; failover triggered, rerouting via satellite'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;London Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; 99.99% uptime milestone reached this quarter'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; USDT payment received: Standard 100 GB bundle'}&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;var logIdx = 0;&lt;/p&gt;

&lt;p&gt;function addLogEntry(msg, cls, feed) {&lt;br&gt;
  if (!feed) return;&lt;br&gt;
  var el = document.createElement('div');&lt;br&gt;
  el.className = 'le';&lt;br&gt;
  var ts = new Date().toLocaleTimeString();&lt;br&gt;
  el.innerHTML = '&amp;lt;div class="ld ' + cls + '"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div class="lt"&amp;gt;' + msg + '&amp;lt;span class="ltm"&amp;gt;just now &amp;amp;middot; ' + ts + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  feed.insertBefore(el, feed.children[1] || null);&lt;br&gt;
  while (feed.querySelectorAll('.le').length &amp;gt; 8) {&lt;br&gt;
    var last = feed.querySelector('.le:last-child');&lt;br&gt;
    if (last) last.remove();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function initLogs() {&lt;br&gt;
  var feed = document.getElementById('log-home');&lt;br&gt;
  if (feed) {&lt;br&gt;
    LOG_EVENTS.slice(0, 6).forEach(function(e, i) {&lt;br&gt;
      setTimeout(function() { addLogEntry(e.m, e.c, feed); }, i * 120);&lt;br&gt;
    });&lt;br&gt;
    logIdx = 6;&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function startLiveLog() {&lt;br&gt;
  try {&lt;br&gt;
    var es = new EventSource(API + '/api/events');&lt;br&gt;
    es.onmessage = function(ev) {&lt;br&gt;
      var d = JSON.parse(ev.data);&lt;br&gt;
      var cls = d.type === 'revenue' ? 'lgd' : d.type === 'warning' ? 'lr' : d.type === 'info' ? 'lb' : 'lg';&lt;br&gt;
      var feed = document.getElementById('log-home');&lt;br&gt;
      if (feed) addLogEntry('&amp;lt;strong&amp;gt;LIVE&amp;lt;/strong&amp;gt; &amp;amp;mdash; ' + d.message, cls, feed);&lt;br&gt;
    };&lt;br&gt;
    es.onerror = function() { es.close(); startFallbackLog(); };&lt;br&gt;
  } catch(e) { startFallbackLog(); }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function startFallbackLog() {&lt;br&gt;
  setInterval(function() {&lt;br&gt;
    var e = LOG_EVENTS[logIdx % LOG_EVENTS.length]; logIdx++;&lt;br&gt;
    var feed = document.getElementById('log-home');&lt;br&gt;
    if (feed) addLogEntry(e.m, e.c, feed);&lt;br&gt;
  }, 3500);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== INIT ===== */&lt;br&gt;
window.addEventListener('load', function() {&lt;br&gt;
  loadStats();&lt;br&gt;
  initLogs();&lt;br&gt;
  startLiveLog();&lt;br&gt;
});&lt;/p&gt;



</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>Frontshop</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Mon, 03 Aug 2026 19:29:15 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/frontshop-emg</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/frontshop-emg</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>SPACESHOP</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Mon, 03 Aug 2026 19:26:12 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/spaceshop-2af2</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/spaceshop-2af2</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
    </item>
    <item>
      <title>AIRTIMES</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Fri, 10 Jul 2026 08:01:25 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/airtimes-4n61</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/airtimes-4n61</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&amp;lt;!DOCTYPE html&amp;gt;




AIRTIMES — Global Network of Data &amp;amp; Free Services


:root {
--bg: #020508; --sf: #060d14; --sf2: #0a1520;
--ac: #00ffe0; --ac2: #0077ff; --ac3: #ff4d6d;
--gd: #f5c518; --gd2: #ff9f00;
--tx: #c8e8ff; --mt: #3a5570;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { background: var(--bg); color: var(--tx); font-family: &amp;amp;#39;Space Mono&amp;amp;#39;, monospace; overflow-x: hidden; }&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;.bg-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none;&amp;lt;br&amp;gt;
  background: radial-gradient(ellipse 80% 60% at 20% 10%, rgba(0,119,255,.07), transparent 70%),&amp;lt;br&amp;gt;
  radial-gradient(ellipse 60% 50% at 80% 80%, rgba(0,255,224,.06), transparent 70%),&amp;lt;br&amp;gt;
  radial-gradient(ellipse 40% 40% at 50% 50%, rgba(245,197,24,.03), transparent 70%); }&amp;lt;br&amp;gt;
.grid-overlay { position: fixed; inset: 0; z-index: 0; pointer-events: none;&amp;lt;br&amp;gt;
  background-image: linear-gradient(rgba(0,255,224,.04) 1px, transparent 1px),&amp;lt;br&amp;gt;
  linear-gradient(90deg, rgba(0,255,224,.04) 1px, transparent 1px);&amp;lt;br&amp;gt;
  background-size: 48px 48px; animation: gridDrift 30s linear infinite; }&amp;lt;br&amp;gt;
@keyframes gridDrift { to { background-position: 48px 48px; } }&amp;lt;br&amp;gt;
.scanline { position: fixed; inset: 0; z-index: 1; pointer-events: none;&amp;lt;br&amp;gt;
  background: repeating-linear-gradient(to bottom, transparent 0, transparent 3px, rgba(0,0,0,.06) 3px, rgba(0,0,0,.06) 4px); }&amp;lt;br&amp;gt;
.shell { position: relative; z-index: 2; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* NAV */&amp;lt;br&amp;gt;
nav { display: flex; align-items: center; justify-content: space-between;&amp;lt;br&amp;gt;
  padding: 15px 38px; border-bottom: 1px solid rgba(0,255,224,.1);&amp;lt;br&amp;gt;
  backdrop-filter: blur(14px); position: sticky; top: 0; z-index: 200; background: rgba(2,5,8,.9); }&amp;lt;br&amp;gt;
.logo { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 1.85rem; letter-spacing: .25em;&amp;lt;br&amp;gt;
  color: var(--ac); text-shadow: 0 0 24px rgba(0,255,224,.5); cursor: pointer; position: relative; }&amp;lt;br&amp;gt;
.logo::after { content: &amp;amp;#39;●&amp;amp;#39;; font-size: .35em; color: var(--ac3);&amp;lt;br&amp;gt;
  position: absolute; top: 4px; right: -14px; animation: blink 1.4s step-end infinite; }&amp;lt;br&amp;gt;
@keyframes blink { 50% { opacity: 0; } }&amp;lt;br&amp;gt;
.nl { display: flex; gap: 22px; list-style: none; }&amp;lt;br&amp;gt;
.nl a { color: var(--mt); text-decoration: none; font-size: .65rem; letter-spacing: .14em;&amp;lt;br&amp;gt;
  text-transform: uppercase; transition: color .2s; cursor: pointer; }&amp;lt;br&amp;gt;
.nl a:hover { color: var(--ac); }&amp;lt;br&amp;gt;
.nl a.ga { color: var(--gd); }&amp;lt;br&amp;gt;
.nl a.ga:hover { color: var(--gd2); }&amp;lt;br&amp;gt;
.sp { display: flex; align-items: center; gap: 8px; font-size: .6rem; color: var(--ac); letter-spacing: .12em; }&amp;lt;br&amp;gt;
.pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--ac);&amp;lt;br&amp;gt;
  box-shadow: 0 0 8px var(--ac); animation: blink 1.4s step-end infinite; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* TABS */&amp;lt;br&amp;gt;
.tab-bar { display: flex; border-bottom: 1px solid rgba(0,255,224,.08); background: rgba(2,5,8,.75);&amp;lt;br&amp;gt;
  position: sticky; top: 61px; z-index: 100; backdrop-filter: blur(10px); overflow-x: auto; }&amp;lt;br&amp;gt;
.tab { padding: 13px 22px; font-size: .62rem; letter-spacing: .15em; text-transform: uppercase;&amp;lt;br&amp;gt;
  color: var(--mt); cursor: pointer; border-bottom: 2px solid transparent; transition: all .25s; white-space: nowrap; flex-shrink: 0; }&amp;lt;br&amp;gt;
.tab:hover { color: var(--tx); }&amp;lt;br&amp;gt;
.tab.active { color: var(--ac); border-bottom-color: var(--ac); }&amp;lt;br&amp;gt;
.tab.gt { color: rgba(245,197,24,.5); }&amp;lt;br&amp;gt;
.tab.gt:hover { color: var(--gd); }&amp;lt;br&amp;gt;
.tab.gt.active { color: var(--gd); border-bottom-color: var(--gd); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* PAGES */&amp;lt;br&amp;gt;
.page { display: none; animation: pi .3s ease; }&amp;lt;br&amp;gt;
.page.active { display: block; }&amp;lt;br&amp;gt;
@keyframes pi { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }&amp;lt;br&amp;gt;
.sec { max-width: 1300px; margin: 0 auto; padding: 56px 40px; }&amp;lt;br&amp;gt;
.sl { font-size: .58rem; color: var(--ac); letter-spacing: .28em; text-transform: uppercase; margin-bottom: 8px; }&amp;lt;br&amp;gt;
.sl.gd { color: var(--gd); }&amp;lt;br&amp;gt;
.st { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 2.8rem; color: #fff; letter-spacing: .05em; margin-bottom: 28px; line-height: 1; }&amp;lt;br&amp;gt;
.sub { font-size: .76rem; color: var(--mt); line-height: 1.9; max-width: 640px; margin-bottom: 36px; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* HERO */&amp;lt;br&amp;gt;
.hero { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center;&amp;lt;br&amp;gt;
  padding: 76px 40px 56px; max-width: 1300px; margin: 0 auto; }&amp;lt;br&amp;gt;
.htag { font-size: .62rem; letter-spacing: .24em; text-transform: uppercase;&amp;lt;br&amp;gt;
  color: var(--ac2); margin-bottom: 12px; display: flex; align-items: center; gap: 10px; }&amp;lt;br&amp;gt;
.htag::before { content: &amp;amp;#39;&amp;amp;#39;; width: 30px; height: 1px; background: var(--ac2); }&amp;lt;br&amp;gt;
h1 { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: clamp(3.2rem,6.5vw,6rem);&amp;lt;br&amp;gt;
  line-height: .92; letter-spacing: .05em; color: #fff; margin-bottom: 20px; }&amp;lt;br&amp;gt;
h1 .t { color: var(--ac); text-shadow: 0 0 25px rgba(0,255,224,.4); }&amp;lt;br&amp;gt;
h1 .r { color: var(--ac3); }&amp;lt;br&amp;gt;
h1 .g { color: var(--gd); }&amp;lt;br&amp;gt;
.hd { font-size: .78rem; line-height: 1.9; color: var(--mt); max-width: 420px; margin-bottom: 32px; }&amp;lt;br&amp;gt;
.ha { display: flex; gap: 10px; flex-wrap: wrap; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* BUTTONS */&amp;lt;br&amp;gt;
.btn { border: none; padding: 12px 24px; font-family: &amp;amp;#39;Space Mono&amp;amp;#39;; font-size: .68rem;&amp;lt;br&amp;gt;
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase; cursor: pointer; transition: all .25s; }&amp;lt;br&amp;gt;
.bp { background: var(--ac); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }&amp;lt;br&amp;gt;
.bp:hover { box-shadow: 0 0 28px rgba(0,255,224,.3); transform: translateY(-2px); }&amp;lt;br&amp;gt;
.bg { background: var(--gd); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }&amp;lt;br&amp;gt;
.bg:hover { box-shadow: 0 0 28px rgba(245,197,24,.3); transform: translateY(-2px); }&amp;lt;br&amp;gt;
.bs { background: transparent; color: var(--tx); border: 1px solid var(--mt); }&amp;lt;br&amp;gt;
.bs:hover { border-color: var(--ac); color: var(--ac); }&amp;lt;br&amp;gt;
.bsm { padding: 8px 16px; font-size: .6rem; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* GLOBE */&amp;lt;br&amp;gt;
.gw { position: relative; display: flex; align-items: center; justify-content: center; height: 400px; }&amp;lt;br&amp;gt;
.globe { width: 290px; height: 290px; border-radius: 50%;&amp;lt;br&amp;gt;
  background: radial-gradient(circle at 35% 35%, rgba(0,119,255,.25), transparent 60%),&amp;lt;br&amp;gt;
  radial-gradient(circle at 50% 50%, #020d1a 40%, #010608 100%);&amp;lt;br&amp;gt;
  border: 1px solid rgba(0,255,224,.15);&amp;lt;br&amp;gt;
  box-shadow: 0 0 80px rgba(0,119,255,.2), inset 0 0 60px rgba(0,119,255,.1);&amp;lt;br&amp;gt;
  animation: gp 6s ease-in-out infinite; }&amp;lt;br&amp;gt;
@keyframes gp { 50% { box-shadow: 0 0 120px rgba(0,119,255,.35), inset 0 0 80px rgba(0,119,255,.15); } }&amp;lt;br&amp;gt;
.orb { position: absolute; border-radius: 50%; border: 1px solid rgba(0,255,224,.12); }&amp;lt;br&amp;gt;
.o1 { width: 370px; height: 370px; animation: os 12s linear infinite; }&amp;lt;br&amp;gt;
.o2 { width: 450px; height: 450px; border-color: rgba(0,119,255,.08); animation: os 20s linear infinite reverse; }&amp;lt;br&amp;gt;
@keyframes os { to { transform: rotate(360deg); } }&amp;lt;br&amp;gt;
.on { position: absolute; width: 8px; height: 8px; border-radius: 50%;&amp;lt;br&amp;gt;
  background: var(--ac); box-shadow: 0 0 12px var(--ac); top: -4px; left: 50%; transform: translateX(-50%); }&amp;lt;br&amp;gt;
.o2 .on { background: var(--ac2); box-shadow: 0 0 12px var(--ac2); }&amp;lt;br&amp;gt;
.sig { position: absolute; top: 50%; left: 50%; height: 1px;&amp;lt;br&amp;gt;
  background: linear-gradient(90deg, rgba(0,255,224,.5), transparent);&amp;lt;br&amp;gt;
  transform-origin: left; animation: sp 3s ease-in-out infinite; }&amp;lt;br&amp;gt;
@keyframes sp { 0% { opacity: 0; width: 0; } 40% { opacity: 1; } 100% { opacity: 0; width: 150px; } }&amp;lt;br&amp;gt;
.s1 { transform: rotate(35deg); }&amp;lt;br&amp;gt;
.s2 { transform: rotate(135deg); animation-delay: .8s; }&amp;lt;br&amp;gt;
.s3 { transform: rotate(220deg); animation-delay: 1.6s; }&amp;lt;br&amp;gt;
.s4 { transform: rotate(310deg); animation-delay: 2.4s; }&amp;lt;br&amp;gt;
.db { position: absolute; background: rgba(6,13,20,.92); border: 1px solid rgba(0,255,224,.2);&amp;lt;br&amp;gt;
  padding: 7px 12px; font-size: .57rem; color: var(--ac); letter-spacing: .1em;&amp;lt;br&amp;gt;
  backdrop-filter: blur(8px); animation: fu 4s ease-in-out infinite; }&amp;lt;br&amp;gt;
.db.b1 { top: 46px; left: 4px; }&amp;lt;br&amp;gt;
.db.b2 { bottom: 66px; right: -2px; animation-delay: 1.5s; color: var(--ac2); border-color: rgba(0,119,255,.3); }&amp;lt;br&amp;gt;
.db.b3 { top: 166px; right: -4px; animation-delay: .8s; color: var(--gd); border-color: rgba(245,197,24,.3); }&amp;lt;br&amp;gt;
@keyframes fu { 50% { transform: translateY(-8px); } }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* TICKER */&amp;lt;br&amp;gt;
.tw { overflow: hidden; padding: 10px 0; border-top: 1px solid rgba(0,255,224,.07);&amp;lt;br&amp;gt;
  border-bottom: 1px solid rgba(0,255,224,.07); background: rgba(0,255,224,.02); }&amp;lt;br&amp;gt;
.tt { display: flex; gap: 56px; white-space: nowrap; animation: ts 35s linear infinite; }&amp;lt;br&amp;gt;
@keyframes ts { to { transform: translateX(-50%); } }&amp;lt;br&amp;gt;
.ti { display: flex; align-items: center; gap: 10px; font-size: .62rem;&amp;lt;br&amp;gt;
  letter-spacing: .14em; text-transform: uppercase; color: var(--mt); }&amp;lt;br&amp;gt;
.dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ac); }&amp;lt;br&amp;gt;
.dot.gd { background: var(--gd); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* STATS ROW */&amp;lt;br&amp;gt;
.sr { max-width: 1300px; margin: 50px auto; padding: 0 40px;&amp;lt;br&amp;gt;
  display: grid; grid-template-columns: repeat(4,1fr); gap: 2px; }&amp;lt;br&amp;gt;
.sc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);&amp;lt;br&amp;gt;
  padding: 26px 22px; position: relative; overflow: hidden; transition: border-color .3s; }&amp;lt;br&amp;gt;
.sc:hover { border-color: rgba(0,255,224,.2); }&amp;lt;br&amp;gt;
.sc::before { content: &amp;amp;#39;&amp;amp;#39;; position: absolute; top: 0; left: 0; right: 0; height: 2px;&amp;lt;br&amp;gt;
  background: linear-gradient(90deg, transparent, var(--ac), transparent); opacity: 0; transition: opacity .3s; }&amp;lt;br&amp;gt;
.sc:hover::before { opacity: 1; }&amp;lt;br&amp;gt;
.sn { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 2.6rem; color: var(--ac); line-height: 1; margin-bottom: 4px; }&amp;lt;br&amp;gt;
.sn.b { color: var(--ac2); } .sn.r { color: var(--ac3); } .sn.gd { color: var(--gd); }&amp;lt;br&amp;gt;
.sl2 { font-size: .56rem; color: var(--mt); letter-spacing: .18em; text-transform: uppercase; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* CARDS */&amp;lt;br&amp;gt;
.cg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; }&amp;lt;br&amp;gt;
.card { background: var(--sf); border: 1px solid rgba(0,255,224,.07);&amp;lt;br&amp;gt;
  padding: 30px 24px; position: relative; overflow: hidden; cursor: pointer; transition: all .3s; }&amp;lt;br&amp;gt;
.card:hover { background: var(--sf2); border-color: rgba(0,255,224,.2); transform: translateY(-3px); }&amp;lt;br&amp;gt;
.card::after { content: &amp;amp;#39;&amp;amp;#39;; position: absolute; bottom: 0; left: 0; right: 0; height: 2px;&amp;lt;br&amp;gt;
  background: linear-gradient(90deg, var(--ac2), var(--ac));&amp;lt;br&amp;gt;
  transform: scaleX(0); transform-origin: left; transition: transform .4s; }&amp;lt;br&amp;gt;
.card:hover::after { transform: scaleX(1); }&amp;lt;br&amp;gt;
.ci { font-size: 1.7rem; margin-bottom: 16px; }&amp;lt;br&amp;gt;
.cn { font-family: &amp;amp;#39;Syne&amp;amp;#39;; font-size: .95rem; font-weight: 700; color: #fff; margin-bottom: 8px; }&amp;lt;br&amp;gt;
.cd { font-size: .68rem; line-height: 1.8; color: var(--mt); }&amp;lt;br&amp;gt;
.ctag { display: inline-block; margin-top: 14px; font-size: .54rem; letter-spacing: .17em;&amp;lt;br&amp;gt;
  text-transform: uppercase; color: var(--ac); border: 1px solid rgba(0,255,224,.2); padding: 3px 9px; }&amp;lt;br&amp;gt;
.ctag.b { color: var(--ac2); border-color: rgba(0,119,255,.25); }&amp;lt;br&amp;gt;
.ctag.r { color: var(--ac3); border-color: rgba(255,77,109,.25); }&amp;lt;br&amp;gt;
.ctag.gd { color: var(--gd); border-color: rgba(245,197,24,.25); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* FORMS */&amp;lt;br&amp;gt;
.fp { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; }&amp;lt;br&amp;gt;
.fp.gp { border-color: rgba(245,197,24,.2); }&amp;lt;br&amp;gt;
.fg { margin-bottom: 14px; }&amp;lt;br&amp;gt;
.fl { font-size: .56rem; color: var(--mt); letter-spacing: .2em; text-transform: uppercase; margin-bottom: 6px; display: block; }&amp;lt;br&amp;gt;
.fi { background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);&amp;lt;br&amp;gt;
  color: var(--tx); padding: 10px 14px; font-family: &amp;amp;#39;Space Mono&amp;amp;#39;; font-size: .7rem;&amp;lt;br&amp;gt;
  outline: none; width: 100%; transition: border-color .2s; }&amp;lt;br&amp;gt;
.fi:focus { border-color: var(--ac); }&amp;lt;br&amp;gt;
.fi.gi { border-color: rgba(245,197,24,.2); }&amp;lt;br&amp;gt;
.fi.gi:focus { border-color: var(--gd); }&amp;lt;br&amp;gt;
.fi::placeholder { color: var(--mt); }&amp;lt;br&amp;gt;
select.fi option { background: #020508; }&amp;lt;br&amp;gt;
.rb { margin-top: 16px; background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);&amp;lt;br&amp;gt;
  padding: 16px; font-size: .67rem; line-height: 2.2; display: none; }&amp;lt;br&amp;gt;
.rb.show { display: block; }&amp;lt;br&amp;gt;
.rb.gb { border-color: rgba(245,197,24,.2); background: rgba(245,197,24,.02); }&amp;lt;br&amp;gt;
.rk { color: var(--mt); } .rv { color: var(--ac); }&amp;lt;br&amp;gt;
.rv.gd { color: var(--gd); } .rv.g { color: #4ade80; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* WALLET GRID */&amp;lt;br&amp;gt;
.wg { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 28px; }&amp;lt;br&amp;gt;
.wc { background: var(--sf); border: 1px solid rgba(245,197,24,.12);&amp;lt;br&amp;gt;
  padding: 22px 16px; text-align: center; cursor: pointer; transition: all .3s; position: relative; }&amp;lt;br&amp;gt;
.wc:hover { border-color: rgba(245,197,24,.4); background: rgba(245,197,24,.04); }&amp;lt;br&amp;gt;
.wc.sel { border-color: var(--gd); background: rgba(245,197,24,.06); }&amp;lt;br&amp;gt;
.wc.sel::before { content: &amp;amp;#39;✓&amp;amp;#39;; position: absolute; top: 7px; right: 9px; font-size: .58rem; color: var(--gd); }&amp;lt;br&amp;gt;
.wi { font-size: 1.9rem; margin-bottom: 7px; }&amp;lt;br&amp;gt;
.wco { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 1.15rem; color: var(--gd); letter-spacing: .1em; }&amp;lt;br&amp;gt;
.wna { font-size: .55rem; color: var(--mt); letter-spacing: .1em; margin-top: 2px; }&amp;lt;br&amp;gt;
.wad { font-size: .46rem; color: rgba(58,85,112,.55); margin-top: 7px; word-break: break-all; line-height: 1.4; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* BUNDLE GRID */&amp;lt;br&amp;gt;
.bg2 { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 22px; }&amp;lt;br&amp;gt;
.bc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);&amp;lt;br&amp;gt;
  padding: 20px 14px; text-align: center; cursor: pointer; transition: all .3s; }&amp;lt;br&amp;gt;
.bc:hover { border-color: rgba(0,255,224,.25); }&amp;lt;br&amp;gt;
.bc.sel { border-color: var(--ac); background: rgba(0,255,224,.04); }&amp;lt;br&amp;gt;
.bn { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 1rem; color: #fff; letter-spacing: .1em; }&amp;lt;br&amp;gt;
.bdata { font-size: 1.3rem; color: var(--ac); font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; line-height: 1.1; }&amp;lt;br&amp;gt;
.bprice { font-size: .6rem; color: var(--mt); margin-top: 4px; }&amp;lt;br&amp;gt;
.bbtc { font-size: .56rem; color: var(--gd); margin-top: 2px; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* AIRBANK */&amp;lt;br&amp;gt;
.vd { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; margin-bottom: 2px; }&amp;lt;br&amp;gt;
.vn { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 3.5rem; color: var(--ac); line-height: 1; text-shadow: 0 0 40px rgba(0,255,224,.3); }&amp;lt;br&amp;gt;
.vu { font-size: .67rem; color: var(--mt); letter-spacing: .24em; text-transform: uppercase; margin-top: 4px; }&amp;lt;br&amp;gt;
.rg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; margin-bottom: 2px; }&amp;lt;br&amp;gt;
.rc { background: var(--sf); border: 1px solid rgba(245,197,24,.1); padding: 26px; position: relative; }&amp;lt;br&amp;gt;
.rc::before { content: &amp;amp;#39;&amp;amp;#39;; position: absolute; top: 0; left: 0; right: 0; height: 1px;&amp;lt;br&amp;gt;
  background: linear-gradient(90deg, transparent, var(--gd), transparent); }&amp;lt;br&amp;gt;
.rn { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 2.3rem; color: var(--gd); line-height: 1; margin-bottom: 4px; }&amp;lt;br&amp;gt;
.rl { font-size: .56rem; color: var(--mt); letter-spacing: .17em; text-transform: uppercase; }&amp;lt;br&amp;gt;
.rs { font-size: .6rem; color: rgba(245,197,24,.5); margin-top: 4px; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* PROGRESS */&amp;lt;br&amp;gt;
.pr { margin-bottom: 12px; }&amp;lt;br&amp;gt;
.pl { display: flex; justify-content: space-between; font-size: .58rem; color: var(--mt); letter-spacing: .1em; margin-bottom: 5px; }&amp;lt;br&amp;gt;
.pb { height: 4px; background: rgba(0,255,224,.08); border-radius: 2px; overflow: hidden; }&amp;lt;br&amp;gt;
.pf { height: 100%; border-radius: 2px; background: linear-gradient(90deg, var(--ac2), var(--ac));&amp;lt;br&amp;gt;
  animation: fb 2s ease forwards; transform-origin: left; }&amp;lt;br&amp;gt;
@keyframes fb { from { transform: scaleX(0); } to { transform: scaleX(1); } }&amp;lt;br&amp;gt;
.pf.gd { background: linear-gradient(90deg, var(--gd2), var(--gd)); }&amp;lt;br&amp;gt;
.pf.r { background: linear-gradient(90deg, var(--ac3), #ff9a6c); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* TABLE */&amp;lt;br&amp;gt;
.dt { width: 100%; border-collapse: collapse; font-size: .64rem; }&amp;lt;br&amp;gt;
.dt th { color: var(--mt); letter-spacing: .14em; text-transform: uppercase; font-size: .54rem;&amp;lt;br&amp;gt;
  padding: 9px 13px; text-align: left; border-bottom: 1px solid rgba(0,255,224,.08); }&amp;lt;br&amp;gt;
.dt td { padding: 9px 13px; border-bottom: 1px solid rgba(255,255,255,.03); color: var(--tx); }&amp;lt;br&amp;gt;
.dt tr:hover td { background: rgba(0,255,224,.02); }&amp;lt;br&amp;gt;
.bdg { display: inline-block; padding: 2px 8px; font-size: .5rem; letter-spacing: .1em;&amp;lt;br&amp;gt;
  text-transform: uppercase; border-radius: 2px; }&amp;lt;br&amp;gt;
.bg3 { background: rgba(0,255,224,.1); color: var(--ac); border: 1px solid rgba(0,255,224,.2); }&amp;lt;br&amp;gt;
.bgd { background: rgba(245,197,24,.1); color: var(--gd); border: 1px solid rgba(245,197,24,.2); }&amp;lt;br&amp;gt;
.br { background: rgba(255,77,109,.1); color: var(--ac3); border: 1px solid rgba(255,77,109,.2); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* LOG FEED */&amp;lt;br&amp;gt;
.lf { background: var(--sf); border: 1px solid rgba(0,255,224,.08); padding: 20px; min-height: 260px; }&amp;lt;br&amp;gt;
.le { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,.03);&amp;lt;br&amp;gt;
  animation: fi .4s ease; }&amp;lt;br&amp;gt;
@keyframes fi { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }&amp;lt;br&amp;gt;
.ld { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 5px; }&amp;lt;br&amp;gt;
.lg { background: var(--ac); box-shadow: 0 0 8px var(--ac); }&amp;lt;br&amp;gt;
.lb { background: var(--ac2); box-shadow: 0 0 8px var(--ac2); }&amp;lt;br&amp;gt;
.lr { background: var(--ac3); box-shadow: 0 0 8px var(--ac3); }&amp;lt;br&amp;gt;
.lgd { background: var(--gd); box-shadow: 0 0 8px var(--gd); }&amp;lt;br&amp;gt;
.lt { font-size: .62rem; line-height: 1.7; color: var(--mt); }&amp;lt;br&amp;gt;
.lt strong { color: var(--tx); font-weight: 400; }&amp;lt;br&amp;gt;
.ltm { font-size: .54rem; color: rgba(58,85,112,.5); display: block; margin-top: 1px; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* SVG MAP */&amp;lt;br&amp;gt;
.mc { width: 100%; height: 230px; position: relative; margin-top: 18px; }&amp;lt;br&amp;gt;
.ms { width: 100%; height: 100%; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* HIGHLIGHT BOX */&amp;lt;br&amp;gt;
.hb { background: rgba(245,197,24,.04); border: 1px solid rgba(245,197,24,.2);&amp;lt;br&amp;gt;
  padding: 18px 22px; margin-bottom: 22px; }&amp;lt;br&amp;gt;
.hb p { font-size: .7rem; line-height: 1.9; color: var(--tx); }&amp;lt;br&amp;gt;
.hb strong { color: var(--gd); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* CALC DISPLAY */&amp;lt;br&amp;gt;
.cd { background: #000; border: 1px solid rgba(245,197,24,.2); padding: 18px;&amp;lt;br&amp;gt;
  font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 1.8rem; color: var(--gd);&amp;lt;br&amp;gt;
  text-align: right; letter-spacing: .1em; min-height: 64px; margin-bottom: 14px; }&amp;lt;br&amp;gt;
.cd .cs { font-size: .76rem; color: rgba(245,197,24,.5); font-family: &amp;amp;#39;Space Mono&amp;amp;#39;; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;.copy-btn { background: rgba(245,197,24,.08); border: 1px solid rgba(245,197,24,.2);&amp;lt;br&amp;gt;
  color: var(--gd); padding: 4px 12px; font-size: .56rem; cursor: pointer;&amp;lt;br&amp;gt;
  font-family: &amp;amp;#39;Space Mono&amp;amp;#39;; letter-spacing: .1em; transition: all .2s; }&amp;lt;br&amp;gt;
.copy-btn:hover { background: rgba(245,197,24,.18); }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;.div { border: none; border-top: 1px solid rgba(0,255,224,.06); margin: 36px 0; }&amp;lt;br&amp;gt;
.tc { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }&amp;lt;br&amp;gt;
.thc { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 2px; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;/* FOOTER */&amp;lt;br&amp;gt;
footer { border-top: 1px solid rgba(0,255,224,.06); padding: 30px 40px;&amp;lt;br&amp;gt;
  display: flex; align-items: center; justify-content: space-between;&amp;lt;br&amp;gt;
  max-width: 1300px; margin: 0 auto; }&amp;lt;br&amp;gt;
.fl2 { font-family: &amp;amp;#39;Bebas Neue&amp;amp;#39;; font-size: 1.2rem; color: var(--mt); letter-spacing: .25em; }&amp;lt;br&amp;gt;
.fc { font-size: .54rem; color: rgba(58,85,112,.5); letter-spacing: .1em; }&amp;lt;br&amp;gt;
.fs { display: flex; align-items: center; gap: 8px; font-size: .58rem; color: var(--ac); letter-spacing: .15em; }&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; (max-width: 900px) {&amp;lt;br&amp;gt;
  .hero { grid-template-columns: 1fr; }&amp;lt;br&amp;gt;
  .gw { height: 270px; } .globe { width: 220px; height: 220px; }&amp;lt;br&amp;gt;
  .o1 { width: 280px; height: 280px; } .o2 { width: 350px; height: 350px; }&amp;lt;br&amp;gt;
  .sr { grid-template-columns: repeat(2,1fr); }&amp;lt;br&amp;gt;
  .cg { grid-template-columns: 1fr; }&amp;lt;br&amp;gt;
  .wg { grid-template-columns: repeat(2,1fr); }&amp;lt;br&amp;gt;
  .bg2 { grid-template-columns: repeat(2,1fr); }&amp;lt;br&amp;gt;
  .rg { grid-template-columns: 1fr; }&amp;lt;br&amp;gt;
  .tc { grid-template-columns: 1fr; }&amp;lt;br&amp;gt;
  .thc { grid-template-columns: 1fr; }&amp;lt;br&amp;gt;
  .nl { display: none; }&amp;lt;br&amp;gt;
  nav { padding: 12px 18px; }&amp;lt;br&amp;gt;
  .sec { padding: 40px 18px; }&amp;lt;br&amp;gt;
}&amp;lt;br&amp;gt;














  AIRTIMES
  &lt;ul&gt;
    &lt;li&gt;&lt;a&gt;Network&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a&gt;Services&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a&gt;Nodes&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a&gt;₿ AirPay&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a&gt;🏦 AirBank&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a&gt;👑 Owner&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

&lt;p&gt;&lt;span id="nst"&gt;CONNECTING...&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;🌐 Home&lt;br&gt;
  ⚡ Services&lt;br&gt;
  🔗 Connect&lt;br&gt;
  ◯ Nodes&lt;br&gt;
  📡 Route&lt;br&gt;
  ₿ AirPay&lt;br&gt;
  🏦 AirBank&lt;br&gt;
  👑 Owner Dashboard&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Global Data Mesh — v3.2.0
  &amp;lt;h1&amp;gt;FREE&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;DATA&amp;lt;/span&amp;gt; &amp;amp;amp;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;SERVICE&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;NETWORK&amp;lt;/span&amp;gt;&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;AIRTIMES delivers free data, connectivity, and digital services to every node on Earth. Users connect free. Owner earns automatically via AirPay crypto payments and AirBank data revenue — around the clock.&amp;lt;/p&amp;gt;

    GET FREE ACCESS
    ₿ BUY DATA BUNDLE
    🏦 AirBank







  ▲ 4,700 Gbps THROUGHPUT
  ◯ 142 ACTIVE NODES
  ₿ AUTO-EARNING ACTIVE





&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED




—Active Nodes Worldwide
—Daily Data Delivered (TB)
—Users Served Free
—Auto Revenue Earned (USD)



// Three Systems · One Network
HOW AIRTIMES WORKS


    🌐
    Free Global Network
    Users connect free worldwide via AirData, AirSMS, AirWeb, AirVault, AirAPI, AirRelay. Zero cost to users forever. Powered by surplus bandwidth redistribution across 142+ global nodes.
    FREE FOR ALL USERS


    ₿
    AirPay — Crypto Payments
    Users buy premium data bundles using Bitcoin, Ethereum, USDT, BNB, or Litecoin. Every payment routes directly to the owner's crypto wallet instantly. No middleman, no processor fees.
    BTC · ETH · USDT · BNB · LTC


    🏦
    AirBank — Auto Revenue Engine
    Every byte transmitted earns the owner automatically. Trillions of AirUnits stored in the vault, converted to direct money on every user session. Earn while you sleep.
    EARN AUTOMATICALLY







    📋 Live Network Events


    💰 Revenue Snapshot
    Loading revenue data...
    VIEW FULL OWNER DASHBOARD →









// All Free + Paid Services
WHAT AIRTIMES PROVIDES

  📡AirData — Free Mobile DataZero-cost mobile internet routing through the AIRTIMES mesh. Any device, any carrier, any country. Bandwidth pooled from surplus commercial nodes globally.FREE · UNLIMITED
  💬AirSMS — Global MessagingEnd-to-end encrypted SMS and voice relay across all 193 countries. Delivered free via spare spectrum on partner networks. Auto-revenue for owner per SMS.FREE · E2E ENCRYPTED
  🌐AirWeb — Open BrowserCompressed web proxy serving the full open internet at zero data cost. Optimized for low-bandwidth environments and offline caching. Up to 70% compression ratio.FREE · COMPRESSED
  🔗AirAPI — Open Data GatewayFree access to global datasets: weather, finance, health, transport. 50,000 requests/day per node. Developer-grade REST and WebSocket access.FREE · 50K RPD
  🛰AirRelay — Satellite FallbackWhen ground infrastructure fails, AIRTIMES auto-routes through low-earth-orbit relay partners to maintain continuous free connectivity globally.RESILIENT · AUTO
  💾AirVault — Distributed Storage5 GB of free distributed cloud storage per user. Sharded across 8+ geo-regions for redundancy. Served at zero cost from network surplus capacity.FREE · 5 GB
  ₿AirPay — Crypto Payment GatewayUsers buy premium data bundles with Bitcoin, Ethereum, USDT, BNB, or Litecoin. Payments go directly to the owner's wallet. No middleman. Instant settlement.BTC · ETH · USDT · BNB · LTC
  🏦AirBank — Auto Revenue EngineEvery user session generates automatic revenue for the owner. Trillions of AirUnits stored and converted to direct money. Revenue from data, SMS, API, storage.AUTO REVENUE
  🗺AirRoute — Smart Mesh RoutingDijkstra mesh routing with Haversine geo-scoring. Auto-failover. Multi-hop path optimization. Load rebalancing every 30 seconds across all nodes.AI ROUTING








// Free Access
JOIN THE NETWORK
&amp;lt;p&amp;gt;Register your phone or email to get a free AIRTIMES token. Instantly connected to 142+ nodes worldwide. No credit card. No contracts. Forever free.&amp;lt;/p&amp;gt;


    Phone or Email

    Identifier Type
      EmailPhone
    ACTIVATE FREE ACCESS →
    &amp;lt;span&amp;gt;Fill in your details and click activate.&amp;lt;/span&amp;gt;


    What you get FREE:

      ✦ AirData — Unlimited free mobile internet&amp;lt;br&amp;gt;
      ✦ AirSMS — Global encrypted messaging&amp;lt;br&amp;gt;
      ✦ AirWeb — Compressed web proxy&amp;lt;br&amp;gt;
      ✦ AirAPI — 50,000 free API calls/day&amp;lt;br&amp;gt;
      ✦ AirVault — 5 GB free cloud storage&amp;lt;br&amp;gt;
      ✦ AirRelay — Satellite connectivity fallback&amp;lt;br&amp;gt;
      ✦ Assigned to nearest global mesh node&amp;lt;br&amp;gt;
      ✦ 10 GB daily data quota&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    Want more data? &amp;lt;span&amp;gt;Buy a premium bundle with crypto via AirPay →&amp;lt;/span&amp;gt;


&amp;lt;hr&amp;gt;
// Free API Test
AIRAPI GATEWAY


    City (Free Weather)

    GET FREE WEATHER →
    Exchange Rate



        GET RATE


    &amp;lt;span&amp;gt;Results appear here — all free, no API key needed.&amp;lt;/span&amp;gt;


    🌐 AirAPI Specs

      ● &amp;lt;span&amp;gt;50,000&amp;lt;/span&amp;gt; free calls/day per node&amp;lt;br&amp;gt;
      ● Weather data for all cities worldwide&amp;lt;br&amp;gt;
      ● Live exchange rates (150+ currencies)&amp;lt;br&amp;gt;
      ● Health, transport, finance datasets&amp;lt;br&amp;gt;
      ● REST + WebSocket endpoints&amp;lt;br&amp;gt;
      ● No API key needed for free tier&amp;lt;br&amp;gt;
      ● Each call auto-earns revenue for owner&amp;lt;br&amp;gt;










// Mesh Routing Engine
TEST YOUR ROUTE
&amp;lt;p&amp;gt;Enter your token and destination to compute the optimal mesh path through AIRTIMES global nodes using Haversine geo-scoring and Dijkstra path finding.&amp;lt;/p&amp;gt;


    Your Token (from registration)

    Destination

    Service

        AirDataAirSMSAirWeb
        AirAPIAirVaultAirRelay

    COMPUTE ROUTE →


    Run a route test to see your full mesh path, latency estimate, hop count, and protocol details.









// Global Infrastructure
LIVE NODE MAP

  &amp;lt;svg viewBox="0 0 900 240"&amp;gt;
    &amp;lt;line x1="0" y1="80" x2="900" y2="80" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="0" y1="160" x2="900" y2="160" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="225" y1="0" x2="225" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="450" y1="0" x2="450" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="675" y1="0" x2="675" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;g stroke="" stroke-width="1" fill="none"&amp;gt;
      &amp;lt;line x1="80" y1="90" x2="200" y2="70"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="200" y1="70" x2="340" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="340" y1="100" x2="480" y2="80"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="600" y2="110"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="740" y2="90"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="740" y1="90" x2="840" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="200" y1="70" x2="280" y2="150"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="520" y2="170"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="580" y2="180"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="80" y1="90" x2="120" y2="180"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;/g&amp;gt;
    &amp;lt;circle r="3" fill=""&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;circle r="2.5" fill=""&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;g&amp;gt;
      &amp;lt;circle cx="80" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="80" cy="90" r="3.5" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="200" cy="70" r="10" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="200" cy="70" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="340" cy="100" r="7" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="340" cy="100" r="3" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="480" cy="80" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="480" cy="80" r="4" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="600" cy="110" r="8" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="600" cy="110" r="3.5" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="740" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="740" cy="90" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="840" cy="100" r="7" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="840" cy="100" r="3" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="120" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="120" cy="180" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="520" cy="170" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="520" cy="170" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="580" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="580" cy="180" r="2" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="660" cy="50" r="6" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="660" cy="50" r="2.5" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;
      &amp;lt;circle cx="155" cy="105" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="155" cy="105" r="2" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;/g&amp;gt;
  &amp;lt;/svg&amp;gt;

&amp;lt;table id="nodes-table"&amp;gt;
  &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Node ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;City&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Country&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Operator&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Capacity (Gbps)&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Load&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Status&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;
  &amp;lt;tbody id="nodes-body"&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan="7"&amp;gt;Loading nodes...&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;







// Bitcoin &amp;amp;amp; Crypto Payment Gateway
₿ AIRPAY

  &amp;lt;p&amp;gt;AirPay lets users buy premium data bundles using &amp;lt;strong&amp;gt;Bitcoin, Ethereum, USDT, BNB, or Litecoin&amp;lt;/strong&amp;gt;. Every payment goes &amp;lt;strong&amp;gt;directly to the AIRTIMES owner's crypto wallet&amp;lt;/strong&amp;gt; — instantly, no middleman, zero processor fees. Select a bundle, choose your crypto, and send payment to the displayed address.&amp;lt;/p&amp;gt;


// Owner Wallet Addresses — All Payments Are Sent Directly Here


&amp;lt;hr&amp;gt;
// Select Your Data Bundle
Choose how much premium data you want to purchase:




    ⚡ Generate Payment Address
    Your User ID (optional)

    Pay With

        ₿ Bitcoin (BTC)
        Ξ Ethereum (ETH)
        ₮ Tether USDT (TRC-20)
        ⬡ BNB Coin
        Ł Litecoin (LTC)

    GENERATE PAYMENT ADDRESS →
    &amp;lt;span&amp;gt;Select a bundle above, then click to generate your payment address.&amp;lt;/span&amp;gt;


    📋 How AirPay Works

      &amp;lt;span&amp;gt;Step 1.&amp;lt;/span&amp;gt; Select a data bundle above&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 2.&amp;lt;/span&amp;gt; Choose Bitcoin, ETH, USDT, BNB, or LTC&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 3.&amp;lt;/span&amp;gt; Click "Generate Payment Address"&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 4.&amp;lt;/span&amp;gt; Send exact crypto amount shown&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 5.&amp;lt;/span&amp;gt; Bundle activates in 10–60 minutes&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 6.&amp;lt;/span&amp;gt; Revenue automatically in owner wallet&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    All payments go directly to owner's crypto wallets. No processor. No fees. Direct blockchain settlement.



&amp;lt;hr&amp;gt;
// Confirm Your Payment


    Payment ID

    Transaction Hash (TX Hash from blockchain)

    CONFIRM PAYMENT →
    &amp;lt;span&amp;gt;Enter Payment ID + TX hash to confirm your purchase.&amp;lt;/span&amp;gt;


    After sending crypto, paste your &amp;lt;span&amp;gt;Payment ID&amp;lt;/span&amp;gt; and the &amp;lt;span&amp;gt;transaction hash&amp;lt;/span&amp;gt; from your wallet or blockchain explorer. Data bundle activates immediately upon confirmation.









// Data Storage &amp;amp;amp; Automatic Revenue Engine
🏦 AIRBANK

  &amp;lt;p&amp;gt;AirBank is the &amp;lt;strong&amp;gt;financial backbone of AIRTIMES&amp;lt;/strong&amp;gt;. Every byte of data transmitted through the network is stored in the AirBank Vault measured in &amp;lt;strong&amp;gt;Trillions of AirUnits&amp;lt;/strong&amp;gt;. When users browse, message, stream, or use any AIRTIMES service — the owner &amp;lt;strong&amp;gt;earns money automatically&amp;lt;/strong&amp;gt;, around the clock. Data is the currency. Usage is the revenue. Extract data, convert to direct money.&amp;lt;/p&amp;gt;





    Vault Storage
    —
    Total AirUnits Stored


    Data Transmitted
    —
    GB Delivered to Users


    Total Revenue Earned
    —
    USD Earned Automatically






    Per GB Transmitted
    $0.002
    Every 1 GB routed = $0.002 earned by owner


    Per Active User / Day
    $0.001
    Every daily user session earns automatically


    Per API Call
    $0.00005
    Every AirAPI request generates revenue



&amp;lt;hr&amp;gt;
// Data → Money Calculator
Enter any amount of stored/transmitted data to see the revenue it generates for the owner:


    $ 0.00Enter amount below to calculate




          GBTBPB



    &amp;lt;span&amp;gt;Enter data amount above to calculate automatic revenue.&amp;lt;/span&amp;gt;


    📈 Revenue Sources Breakdown
    &amp;lt;span&amp;gt;Data Transmission&amp;lt;/span&amp;gt;&amp;lt;span id="pct1"&amp;gt;42%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Active Users (Daily)&amp;lt;/span&amp;gt;&amp;lt;span id="pct2"&amp;gt;24%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;AirPay Bundles&amp;lt;/span&amp;gt;&amp;lt;span id="pct3"&amp;gt;18%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;API Calls&amp;lt;/span&amp;gt;&amp;lt;span id="pct4"&amp;gt;9%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Storage Fees&amp;lt;/span&amp;gt;&amp;lt;span id="pct5"&amp;gt;5%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;SMS Revenue&amp;lt;/span&amp;gt;&amp;lt;span id="pct6"&amp;gt;2%&amp;lt;/span&amp;gt;









// Private Owner Control Panel
👑 OWNER DASHBOARD

  &amp;lt;p&amp;gt;This is your &amp;lt;strong&amp;gt;private revenue command center&amp;lt;/strong&amp;gt;. See every dollar earned, every AirPay payment received, every data unit transmitted. &amp;lt;strong&amp;gt;Set your Bitcoin and crypto wallet addresses below&amp;lt;/strong&amp;gt; — all AirPay payments route directly to these wallets instantly. You own the network. You own the revenue.&amp;lt;/p&amp;gt;



// ₿ Your Crypto Wallet Addresses
Enter your real wallet addresses here. Users see these when paying via AirPay. All payments land directly in your wallets.


    ₿ Bitcoin (BTC) Wallet Address

    Ξ Ethereum (ETH) Wallet Address

    ₮ USDT Wallet (TRC-20 / Tron Network)

    ⬡ BNB Wallet Address

    Ł Litecoin (LTC) Wallet Address


      SAVE WALLET ADDRESSES →



    &amp;lt;span&amp;gt;Enter your real wallet addresses and click Save. These will be shown to users on the AirPay page. For production, set as environment variables on your backend server.&amp;lt;/span&amp;gt;




// Revenue Summary

  Total Earned (USD)$—— BTC
  Projected Monthly$—Based on current daily rate
  Revenue Events Logged—Auto-logged transactions




    Revenue by Source
    Loading...


    Daily Revenue (Last 7 Days)
    Loading...





  AirPay Payment History
  &amp;lt;table&amp;gt;
    &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Payment ID&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Bundle&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Coin&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Status&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;
    &amp;lt;tbody id="pay-hist"&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan="6"&amp;gt;No payments yet. Share your AirPay page with users to start receiving crypto payments!&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;
  &amp;lt;/table&amp;gt;


&amp;lt;hr&amp;gt;
// Network Control


    Registered Users
    —
    Total on network


    Active Nodes
    —
    Online globally


    Data Routed Today
    —
    GB transmitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;AIRTIMES&lt;br&gt;
  © 2025 AIRTIMES GLOBAL NETWORK · AirPay · AirBank · All Services Free · All Rights Universal&lt;br&gt;
  ALL SYSTEMS OPERATIONAL&lt;/p&gt;

&amp;lt;!-- /shell --&amp;gt;

&lt;p&gt;/* ===== CONFIG ===== */&lt;br&gt;
const API = (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')&lt;br&gt;
  ? '&lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;' : '';&lt;/p&gt;

&lt;p&gt;/* ===== WALLET STATE (saved in localStorage) ===== */&lt;br&gt;
let WALLETS = {&lt;br&gt;
  BTC:  localStorage.getItem('w_BTC')  || '',&lt;br&gt;
  ETH:  localStorage.getItem('w_ETH')  || '',&lt;br&gt;
  USDT: localStorage.getItem('w_USDT') || '',&lt;br&gt;
  BNB:  localStorage.getItem('w_BNB')  || '',&lt;br&gt;
  LTC:  localStorage.getItem('w_LTC')  || ''&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const COIN_ICONS = { BTC:'&amp;amp;#8383;', ETH:'&amp;amp;#926;', USDT:'&amp;amp;#8366;', BNB:'&amp;amp;#11041;', LTC:'&amp;amp;#321;' };&lt;br&gt;
const COIN_NAMES = { BTC:'Bitcoin', ETH:'Ethereum', USDT:'Tether TRC-20', BNB:'BNB Coin', LTC:'Litecoin' };&lt;br&gt;
const COIN_RATES = { BTC:0.0000155, ETH:0.000290, USDT:1.0, BNB:0.00310, LTC:0.0120 };&lt;/p&gt;

&lt;p&gt;const BUNDLES = [&lt;br&gt;
  { id:'starter',   name:'Starter',   gb:5,    usd:0.50,  btc:'0.000010' },&lt;br&gt;
  { id:'basic',     name:'Basic',     gb:20,   usd:1.99,  btc:'0.000038' },&lt;br&gt;
  { id:'standard',  name:'Standard',  gb:100,  usd:7.99,  btc:'0.000150' },&lt;br&gt;
  { id:'pro',       name:'Pro',       gb:500,  usd:29.99, btc:'0.000600' },&lt;br&gt;
  { id:'unlimited', name:'Unlimited', gb:9999, usd:74.99, btc:'0.001500' }&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;let selBundle = null;&lt;/p&gt;

&lt;p&gt;/* ===== PAGE SWITCHER ===== */&lt;br&gt;
function P(id) {&lt;br&gt;
  document.querySelectorAll('.page').forEach(p =&amp;gt; p.classList.remove('active'));&lt;br&gt;
  document.querySelectorAll('.tab').forEach(t =&amp;gt; t.classList.remove('active'));&lt;br&gt;
  const pg = document.getElementById('page-' + id);&lt;br&gt;
  if (pg) pg.classList.add('active');&lt;br&gt;
  document.querySelectorAll('.tab').forEach(t =&amp;gt; {&lt;br&gt;
    if (t.getAttribute('onclick') &amp;amp;&amp;amp; t.getAttribute('onclick').includes("'" + id + "'")) t.classList.add('active');&lt;br&gt;
  });&lt;br&gt;
  window.scrollTo({ top: 0, behavior: 'smooth' });&lt;br&gt;
  if (id === 'airpay')  renderAirPay();&lt;br&gt;
  if (id === 'airbank') loadVault();&lt;br&gt;
  if (id === 'owner')   loadOwner();&lt;br&gt;
  if (id === 'nodes')   loadNodes();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== HELPERS ===== */&lt;br&gt;
function fmt(n) { return Number(n).toLocaleString(); }&lt;br&gt;
function ac(el, target, sfx, dur) {&lt;br&gt;
  if (!el) return;&lt;br&gt;
  sfx = sfx || ''; dur = dur || 2000;&lt;br&gt;
  let v = 0; const step = target / (dur / 16);&lt;br&gt;
  const t = setInterval(function() {&lt;br&gt;
    v += step;&lt;br&gt;
    if (v &amp;gt;= target) { v = target; clearInterval(t); }&lt;br&gt;
    el.textContent = Math.floor(v).toLocaleString() + sfx;&lt;br&gt;
  }, 16);&lt;br&gt;
}&lt;br&gt;
function uid() {&lt;br&gt;
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {&lt;br&gt;
    var r = Math.random() * 16 | 0;&lt;br&gt;
    return (c === 'x' ? r : (r &amp;amp; 0x3 | 0x8)).toString(16);&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LOAD STATS ===== */&lt;br&gt;
async function loadStats() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/stats');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    document.getElementById('nst').textContent = 'ALL SYSTEMS OPERATIONAL';&lt;br&gt;
    ac(document.getElementById('s1'), d.activeNodes || 142);&lt;br&gt;
    ac(document.getElementById('s2'), d.dataTB || 4700, ' TB');&lt;br&gt;
    ac(document.getElementById('s3'), d.totalUsers || 2100000);&lt;br&gt;
    try {&lt;br&gt;
      const rv = await fetch(API + '/api/airbank/revenue');&lt;br&gt;
      const rd = await rv.json();&lt;br&gt;
      const tot = parseFloat(rd.summary &amp;amp;&amp;amp; rd.summary.totalUSD ? rd.summary.totalUSD : '3.47');&lt;br&gt;
      document.getElementById('s4').textContent = '$' + tot.toFixed(2);&lt;br&gt;
      const rs = document.getElementById('rev-snap');&lt;br&gt;
      if (rs) rs.innerHTML =&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD Earned: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? parseFloat(rd.summary.totalUSD).toFixed(4) : '3.4700') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;In Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;' + (rd.summary ? rd.summary.totalBTC : '0.00005378') + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Projected Monthly: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? rd.summary.projectedMonthlyUSD : '104.10') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Revenue Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(rd.summary ? rd.summary.totalEvents : 200) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
    } catch(e) { setDemoRevSnap(); }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('nst').textContent = 'DEMO MODE';&lt;br&gt;
    ac(document.getElementById('s1'), 142);&lt;br&gt;
    ac(document.getElementById('s2'), 4700, ' TB');&lt;br&gt;
    ac(document.getElementById('s3'), 2100000);&lt;br&gt;
    document.getElementById('s4').textContent = '$3.47';&lt;br&gt;
    setDemoRevSnap();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevSnap() {&lt;br&gt;
  const rs = document.getElementById('rev-snap');&lt;br&gt;
  if (!rs) return;&lt;br&gt;
  rs.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$3.47 (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;0.00005378 BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Monthly Proj: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$104.10&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;200 logged&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LOAD NODES ===== */&lt;br&gt;
const DEMO_NODES = [&lt;br&gt;
  {id:'NODE-US-NYC01',city:'New York',   country:'US',operator:'AT&amp;amp;T Surplus',     capacity_gbps:2100,load_percent:52},&lt;br&gt;
  {id:'NODE-GB-LDN01',city:'London',    country:'GB',operator:'BT Wholesale',      capacity_gbps:3400,load_percent:45},&lt;br&gt;
  {id:'NODE-KE-NBI01',city:'Nairobi',   country:'KE',operator:'Safaricom',         capacity_gbps:880, load_percent:78},&lt;br&gt;
  {id:'NODE-AE-DXB01',city:'Dubai',     country:'AE',operator:'Etisalat',          capacity_gbps:1200,load_percent:60},&lt;br&gt;
  {id:'NODE-ID-JKT01',city:'Jakarta',   country:'ID',operator:'Telkom ID',         capacity_gbps:340, load_percent:88},&lt;br&gt;
  {id:'NODE-JP-TYO01',city:'Tokyo',     country:'JP',operator:'NTT Com',           capacity_gbps:2900,load_percent:41},&lt;br&gt;
  {id:'NODE-AU-SYD01',city:'Sydney',    country:'AU',operator:'Telstra',           capacity_gbps:760, load_percent:33},&lt;br&gt;
  {id:'NODE-BR-GRU01',city:'Sao Paulo', country:'BR',operator:'Vivo',              capacity_gbps:640, load_percent:55},&lt;br&gt;
  {id:'NODE-ZA-CPT01',city:'Cape Town', country:'ZA',operator:'MTN SA',            capacity_gbps:420, load_percent:29},&lt;br&gt;
  {id:'NODE-IN-BOM01',city:'Mumbai',    country:'IN',operator:'Airtel',            capacity_gbps:980, load_percent:71},&lt;br&gt;
  {id:'NODE-SG-SIN01',city:'Singapore', country:'SG',operator:'Singtel',           capacity_gbps:1800,load_percent:66},&lt;br&gt;
  {id:'NODE-NG-LOS01',city:'Lagos',     country:'NG',operator:'MTN Nigeria',       capacity_gbps:320, load_percent:82},&lt;br&gt;
  {id:'NODE-DE-BER01',city:'Berlin',    country:'DE',operator:'Deutsche Telekom',  capacity_gbps:1600,load_percent:38},&lt;br&gt;
  {id:'NODE-CN-SHA01',city:'Shanghai',  country:'CN',operator:'China Telecom',     capacity_gbps:2200,load_percent:59},&lt;br&gt;
  {id:'NODE-MX-MEX01',city:'Mexico City',country:'MX',operator:'Telmex',          capacity_gbps:560, load_percent:44}&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;async function loadNodes() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/nodes');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    renderNodes(d.nodes || DEMO_NODES);&lt;br&gt;
  } catch(e) { renderNodes(DEMO_NODES); }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderNodes(nodes) {&lt;br&gt;
  const tb = document.getElementById('nodes-body');&lt;br&gt;
  if (!tb) return;&lt;br&gt;
  tb.innerHTML = nodes.map(function(n) {&lt;br&gt;
    const hi = n.load_percent &amp;gt; 80;&lt;br&gt;
    const bar = '&amp;lt;span style="display:inline-block;width:50px;height:4px;background:rgba(0,255,224,.08);border-radius:2px;vertical-align:middle;margin-left:4px;overflow:hidden"&amp;gt;&amp;lt;span style="display:block;height:100%;width:' + n.load_percent + '%;background:' + (hi ? '#ff4d6d' : '#00ffe0') + ';border-radius:2px"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;';&lt;br&gt;
    return '&amp;lt;tr&amp;gt;&amp;lt;td style="color:var(--ac);font-size:.58rem"&amp;gt;' + n.id + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.city + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.country + '&amp;lt;/td&amp;gt;&amp;lt;td style="color:var(--mt)"&amp;gt;' + n.operator + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + fmt(n.capacity_gbps) + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.load_percent + '% ' + bar + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;span class="bdg bg3"&amp;gt;ONLINE&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== REGISTER ===== */&lt;br&gt;
async function doRegister() {&lt;br&gt;
  const id = document.getElementById('ri').value.trim();&lt;br&gt;
  const type = document.getElementById('rtype').value;&lt;br&gt;
  const btn = document.getElementById('rbtn');&lt;br&gt;
  const res = document.getElementById('rres');&lt;br&gt;
  if (!id) { alert('Please enter your phone or email.'); return; }&lt;br&gt;
  btn.textContent = 'ACTIVATING...'; btn.disabled = true;&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/register', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ identifier: id, type: type })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    if (d.success) {&lt;br&gt;
      res.className = 'rb show';&lt;br&gt;
      res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;USER ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.userId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem;word-break:break-all"&amp;gt;' + d.token + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (d.assignedNode ? d.assignedNode.city + ', ' + d.assignedNode.country + ' (' + d.assignedNode.latencyMs + 'ms)' : 'London, GB') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;DAILY DATA: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + fmt(d.dailyDataMB || 10240) + ' MB&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      document.getElementById('rtt').value = d.token;&lt;br&gt;
      document.getElementById('puid').value = d.userId;&lt;br&gt;
    } else {&lt;br&gt;
      res.className = 'rb show';&lt;br&gt;
      res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';&lt;br&gt;
    }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var tk = uid();&lt;br&gt;
    res.className = 'rb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED (Demo Mode)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IDENTIFIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + id + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem"&amp;gt;' + tk + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;London, GB &amp;amp;mdash; 34ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(58,85,112,.7)"&amp;gt;Deploy the backend to process real registrations.&amp;lt;/div&amp;gt;';&lt;br&gt;
    document.getElementById('rtt').value = tk;&lt;br&gt;
    document.getElementById('puid').value = 'guest-' + uid().slice(0,8);&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'ACTIVATE FREE ACCESS &amp;amp;rarr;'; btn.disabled = false;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== ROUTE ===== */&lt;br&gt;
async function doRoute() {&lt;br&gt;
  const token   = document.getElementById('rtt').value.trim();&lt;br&gt;
  const dest    = document.getElementById('rtd').value.trim() || 'google.com';&lt;br&gt;
  const service = document.getElementById('rts').value;&lt;br&gt;
  const res     = document.getElementById('rres2');&lt;br&gt;
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Computing optimal mesh path...&amp;lt;/span&amp;gt;';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/route', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ token: token, destination: dest, service: service })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    renderRoute(d);&lt;br&gt;
  } catch(e) {&lt;br&gt;
    renderRoute({&lt;br&gt;
      path: [&lt;br&gt;
        { city:'Your Device', country:'--' },&lt;br&gt;
        { city:'London', country:'GB', load:45 },&lt;br&gt;
        { city:'Dubai', country:'AE', load:60 },&lt;br&gt;
        { city: dest, country:'&amp;amp;#127760;' }&lt;br&gt;
      ],&lt;br&gt;
      hops: 3, estimatedLatencyMs: 112, totalDistanceKm: 9420,&lt;br&gt;
      compressed: true, encrypted: true, protocol: 'AIRTIMES/3.2', free: true&lt;br&gt;
    });&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function renderRoute(d) {&lt;br&gt;
  const res = document.getElementById('rres2');&lt;br&gt;
  const hops = d.path || [];&lt;br&gt;
  const pathHtml = hops.map(function(h, i) {&lt;br&gt;
    return '&amp;lt;div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:.67rem"&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--ac);font-size:.58rem"&amp;gt;' + String(i).padStart(2,'0') + '&amp;lt;/span&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--ac)"&amp;gt;&amp;amp;rarr;&amp;lt;/span&amp;gt;' +&lt;br&gt;
      '&amp;lt;span style="color:var(--tx)"&amp;gt;' + h.city + (h.country &amp;amp;&amp;amp; h.country !== '--' ? ' (' + h.country + ')' : '') + '&amp;lt;/span&amp;gt;' +&lt;br&gt;
      (h.load !== undefined ? '&amp;lt;span style="color:var(--mt);font-size:.56rem"&amp;gt;[' + h.load + '% load]&amp;lt;/span&amp;gt;' : '') +&lt;br&gt;
      '&amp;lt;/div&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
  res.innerHTML = pathHtml +&lt;br&gt;
    '&amp;lt;div style="margin-top:14px;border-top:1px solid rgba(0,255,224,.07);padding-top:14px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;HOPS&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.hops||3) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;LATENCY&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.estimatedLatencyMs||112) + ' ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;DISTANCE&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(d.totalDistanceKm||9420) + ' km&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;PROTOCOL&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.protocol||'AIRTIMES/3.2') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COMPRESSED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.compressed?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;ENCRYPTED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.encrypted?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COST&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd);font-weight:700"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== API TESTS ===== */&lt;br&gt;
async function doWeather() {&lt;br&gt;
  const city = document.getElementById('wc').value.trim() || 'Lagos';&lt;br&gt;
  const res  = document.getElementById('apires');&lt;br&gt;
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Fetching weather...&amp;lt;/span&amp;gt;'; res.className = 'rb show';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airapi/weather?city=' + encodeURIComponent(city));&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.temp_c + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.condition + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.humidity + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;WIND: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.wind_kmh + ' km/h&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var temps = {Lagos:31,Nairobi:22,Jakarta:29,London:12,Tokyo:18,Sydney:24,Dubai:38,Accra:30,Abuja:29};&lt;br&gt;
    var t = temps[city] || Math.round(15 + Math.random() * 20);&lt;br&gt;
    var conds = ['Clear','Sunny','Cloudy','Partly Cloudy'];&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + t + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + conds[Math.floor(Math.random()*conds.length)] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + Math.round(40+Math.random()*50) + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doExchange() {&lt;br&gt;
  var from = document.getElementById('ef').value.trim() || 'USD';&lt;br&gt;
  var to   = document.getElementById('et').value.trim() || 'NGN';&lt;br&gt;
  var res  = document.getElementById('apires');&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airapi/exchange?from=' + from + '&amp;amp;to=' + to);&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + d.rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    var rates = {NGN:1580,KES:130,GHS:15,ZAR:18,EUR:0.92,GBP:0.79,JPY:149,BTC:0.0000155,ETH:0.000290};&lt;br&gt;
    var rate = rates[to.toUpperCase()] ? rates[to.toUpperCase()] : (0.5+Math.random()*100).toFixed(4);&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
  res.className = 'rb show';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== AIRPAY RENDER ===== */&lt;br&gt;
function renderAirPay() {&lt;br&gt;
  // Render wallet grid&lt;br&gt;
  var wg = document.getElementById('wallets-grid');&lt;br&gt;
  if (wg) {&lt;br&gt;
    var coins = ['BTC','ETH','USDT','BNB','LTC'];&lt;br&gt;
    wg.innerHTML = coins.map(function(c) {&lt;br&gt;
      var addr = WALLETS[c] || '(Not set &amp;amp;mdash; enter in Owner Dashboard)';&lt;br&gt;
      return '&amp;lt;div class="wc" onclick="selWallet(\'' + c + '\')" id="wcard-' + c + '"&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wi"&amp;gt;' + COIN_ICONS[c] + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wco"&amp;gt;' + c + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wna"&amp;gt;' + COIN_NAMES[c] + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="wad"&amp;gt;' + addr + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;/div&amp;gt;';&lt;br&gt;
    }).join('');&lt;br&gt;
  }&lt;br&gt;
  // Render bundle grid&lt;br&gt;
  var bg = document.getElementById('bundles-grid');&lt;br&gt;
  if (bg) {&lt;br&gt;
    bg.innerHTML = BUNDLES.map(function(b) {&lt;br&gt;
      return '&amp;lt;div class="bc" onclick="selBundle2(\'' + b.id + '\')" id="bc-' + b.id + '"&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bn"&amp;gt;' + b.name + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bdata"&amp;gt;' + (b.gb &amp;gt;= 9999 ? '&amp;amp;#8734;' : b.gb + ' GB') + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bprice"&amp;gt;$' + b.usd + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;div class="bbtc"&amp;gt;&amp;amp;#8383; ' + b.btc + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
        '&amp;lt;/div&amp;gt;';&lt;br&gt;
    }).join('');&lt;br&gt;
  }&lt;br&gt;
  // Pre-fill from localStorage&lt;br&gt;
  ['BTC','ETH','USDT','BNB','LTC'].forEach(function(c) {&lt;br&gt;
    var el = document.getElementById('wbtc') || null;&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function selWallet(coin) {&lt;br&gt;
  document.querySelectorAll('.wc').forEach(function(c) { c.classList.remove('sel'); });&lt;br&gt;
  var el = document.getElementById('wcard-' + coin);&lt;br&gt;
  if (el) el.classList.add('sel');&lt;br&gt;
  var pc = document.getElementById('pcoin');&lt;br&gt;
  if (pc) pc.value = coin;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function selBundle2(id) {&lt;br&gt;
  document.querySelectorAll('.bc').forEach(function(c) { c.classList.remove('sel'); });&lt;br&gt;
  var el = document.getElementById('bc-' + id);&lt;br&gt;
  if (el) el.classList.add('sel');&lt;br&gt;
  selBundle = id;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doPayment() {&lt;br&gt;
  var bundleId = selBundle;&lt;br&gt;
  var coin     = document.getElementById('pcoin').value;&lt;br&gt;
  var userId   = document.getElementById('puid').value.trim();&lt;br&gt;
  var res      = document.getElementById('pres');&lt;br&gt;
  var btn      = document.getElementById('pbtn');&lt;br&gt;
  if (!bundleId) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please select a data bundle above first.&amp;lt;/span&amp;gt;';&lt;br&gt;
    return;&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'GENERATING...'; btn.disabled = true;&lt;br&gt;
  var bundle  = BUNDLES.find(function(b) { return b.id === bundleId; });&lt;br&gt;
  var rate    = COIN_RATES[coin] || 0.0000155;&lt;br&gt;
  var amount  = (bundle.usd * rate).toFixed(8);&lt;br&gt;
  var payId   = 'PAY-' + Math.random().toString(36).substr(2,12).toUpperCase();&lt;br&gt;
  var walletAddr = WALLETS[coin] || 'SET YOUR ' + coin + ' WALLET IN OWNER DASHBOARD';&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airpay/pay', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ userId: userId, bundleId: bundleId, coin: coin })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    if (d.success &amp;amp;&amp;amp; d.payment) showPayResult(d.payment);&lt;br&gt;
    else throw new Error(d.error || 'API unavailable');&lt;br&gt;
  } catch(e) {&lt;br&gt;
    showPayResult({ paymentId:payId, bundle:bundle, coin:coin, amount:amount,&lt;br&gt;
      walletAddress:walletAddr, amountUSD:bundle.usd,&lt;br&gt;
      network: {BTC:'Bitcoin Network',ETH:'Ethereum ERC-20',USDT:'Tron TRC-20',BNB:'BNB Smart Chain',LTC:'Litecoin'}[coin] || coin });&lt;br&gt;
  }&lt;br&gt;
  btn.textContent = 'GENERATE PAYMENT ADDRESS &amp;amp;rarr;'; btn.disabled = false;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function showPayResult(p) {&lt;br&gt;
  var res = document.getElementById('pres');&lt;br&gt;
  res.className = 'rb gb show';&lt;br&gt;
  var icon = COIN_ICONS[p.coin] || '&amp;amp;#8383;';&lt;br&gt;
  res.innerHTML =&lt;br&gt;
    '&amp;lt;div style="text-align:center;margin-bottom:16px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:2.5rem"&amp;gt;' + icon + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-family:\'Bebas Neue\';font-size:1.8rem;color:var(--gd)"&amp;gt;SEND ' + p.amount + ' ' + p.coin + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:.62rem;color:var(--mt)"&amp;gt;' + (p.network || p.coin) + ' &amp;amp;middot; $' + p.amountUSD + ' USD&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="font-size:.6rem;color:var(--mt);margin-bottom:6px;letter-spacing:.1em;text-transform:uppercase"&amp;gt;Send to this wallet address:&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="background:rgba(0,0,0,.5);border:1px solid rgba(245,197,24,.2);padding:12px;margin-bottom:12px;word-break:break-all;font-size:.62rem;color:var(--gd);letter-spacing:.05em;font-family:\'Space Mono\'"&amp;gt;' + p.walletAddress + '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="display:flex;gap:8px;margin-bottom:14px"&amp;gt;' +&lt;br&gt;
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.walletAddress.replace(/'/g,"\'") + '\')"&amp;gt;COPY ADDRESS&amp;lt;/button&amp;gt;' +&lt;br&gt;
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.paymentId + '\')"&amp;gt;COPY PAY ID&amp;lt;/button&amp;gt;' +&lt;br&gt;
    '&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.paymentId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;BUNDLE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (p.bundle ? p.bundle.name + ' &amp;amp;mdash; ' + (p.bundle.gb &amp;gt;= 9999 ? 'Unlimited' : p.bundle.gb + ' GB') : '') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;AMOUNT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.amount + ' ' + p.coin + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Save your Payment ID, then use it in the Confirm section below after sending.&amp;lt;/div&amp;gt;';&lt;br&gt;
  var cpid = document.getElementById('cpid');&lt;br&gt;
  if (cpid) cpid.value = p.paymentId;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function doConfirm() {&lt;br&gt;
  var pid  = document.getElementById('cpid').value.trim();&lt;br&gt;
  var txh  = document.getElementById('ctxh').value.trim();&lt;br&gt;
  var res  = document.getElementById('cres');&lt;br&gt;
  if (!pid) { res.className = 'rb gb show'; res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please enter your Payment ID.&amp;lt;/span&amp;gt;'; return; }&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airpay/confirm', {&lt;br&gt;
      method: 'POST', headers: { 'Content-Type': 'application/json' },&lt;br&gt;
      body: JSON.stringify({ paymentId: pid, txHash: txh })&lt;br&gt;
    });&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    if (d.success) res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED &amp;amp;mdash; BUNDLE ACTIVE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
    else res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';&lt;br&gt;
  } catch(e) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Deploy backend to process real confirmations and activate bundles automatically.&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== AIRBANK ===== */&lt;br&gt;
async function loadVault() {&lt;br&gt;
  try {&lt;br&gt;
    const r = await fetch(API + '/api/airbank/vault');&lt;br&gt;
    const d = await r.json();&lt;br&gt;
    var storedMB = parseFloat(d.vault &amp;amp;&amp;amp; d.vault.storedMB ? d.vault.storedMB : 842000);&lt;br&gt;
    document.getElementById('vstore').textContent = fmt(Math.round(storedMB)) + ' AU';&lt;br&gt;
    document.getElementById('vtrans').textContent = parseFloat(d.transmissions &amp;amp;&amp;amp; d.transmissions.totalGB ? d.transmissions.totalGB : 4700).toFixed(1) + ' GB';&lt;br&gt;
    document.getElementById('vrev').textContent = '$' + parseFloat(d.revenue &amp;amp;&amp;amp; d.revenue.totalUSD ? d.revenue.totalUSD : 3.47).toFixed(4);&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('vstore').textContent = '842,000 AU';&lt;br&gt;
    document.getElementById('vtrans').textContent = '4,700 GB';&lt;br&gt;
    document.getElementById('vrev').textContent = '$3.4700';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function calcMoney() {&lt;br&gt;
  var amt  = parseFloat(document.getElementById('camt').value) || 0;&lt;br&gt;
  var unit = document.getElementById('cunit').value || 'TB';&lt;br&gt;
  var inGB = unit === 'PB' ? amt * 1024 * 1024 : unit === 'TB' ? amt * 1024 : amt;&lt;br&gt;
  var storeMo  = inGB * 0.0005;&lt;br&gt;
  var transUSD = inGB * 0.002;&lt;br&gt;
  var monthly  = (storeMo + transUSD).toFixed(4);&lt;br&gt;
  var yearly   = ((storeMo + transUSD) * 12).toFixed(2);&lt;br&gt;
  var btcM     = ((storeMo + transUSD) * 0.0000155).toFixed(8);&lt;br&gt;
  var disp = document.getElementById('cdisplay');&lt;br&gt;
  if (disp) disp.innerHTML = '&amp;lt;div&amp;gt;$' + monthly + '/mo&amp;lt;/div&amp;gt;&amp;lt;div class="cs"&amp;gt;$' + yearly + '/yr &amp;amp;middot; ' + btcM + ' BTC/mo&amp;lt;/div&amp;gt;';&lt;br&gt;
  var res  = document.getElementById('cres2');&lt;br&gt;
  if (res) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML =&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;INPUT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + amt + ' ' + unit + ' (' + fmt(Math.round(inGB)) + ' GB)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STORAGE REV/MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + storeMo.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TRANSMISSION REV: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + transUSD.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + monthly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / YEAR: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + yearly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IN BITCOIN / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + btcM + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== OWNER DASHBOARD ===== */&lt;br&gt;
async function loadOwner() {&lt;br&gt;
  // Pre-fill wallets from localStorage&lt;br&gt;
  var wmap = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};&lt;br&gt;
  Object.keys(wmap).forEach(function(k) {&lt;br&gt;
    var el = document.getElementById(k);&lt;br&gt;
    if (el &amp;amp;&amp;amp; WALLETS[wmap[k]]) el.value = WALLETS[wmap[k]];&lt;br&gt;
  });&lt;br&gt;
  try {&lt;br&gt;
    const [revR, nodesR, vaultR] = await Promise.all([&lt;br&gt;
      fetch(API + '/api/airbank/revenue'),&lt;br&gt;
      fetch(API + '/api/nodes'),&lt;br&gt;
      fetch(API + '/api/airbank/vault')&lt;br&gt;
    ]);&lt;br&gt;
    const rev   = await revR.json();&lt;br&gt;
    const nodes = await nodesR.json();&lt;br&gt;
    const vault = await vaultR.json();&lt;br&gt;
    document.getElementById('ort').textContent  = '$' + parseFloat(rev.summary &amp;amp;&amp;amp; rev.summary.totalUSD ? rev.summary.totalUSD : '3.47').toFixed(4);&lt;br&gt;
    document.getElementById('ortb').textContent = (rev.summary ? rev.summary.totalBTC : '0.00005378') + ' BTC';&lt;br&gt;
    document.getElementById('orm').textContent  = '$' + (rev.summary ? rev.summary.projectedMonthlyUSD : '104.10');&lt;br&gt;
    document.getElementById('ore').textContent  = fmt(rev.summary ? rev.summary.totalEvents : 200);&lt;br&gt;
    document.getElementById('cn').textContent   = nodes.count || 142;&lt;br&gt;
    document.getElementById('cu').textContent   = fmt(2100000);&lt;br&gt;
    document.getElementById('cdg').textContent  = parseFloat(vault.transmissions &amp;amp;&amp;amp; vault.transmissions.totalGB ? vault.transmissions.totalGB : 4700).toFixed(0) + ' GB';&lt;br&gt;
    var src = document.getElementById('rev-src');&lt;br&gt;
    if (src &amp;amp;&amp;amp; rev.bySource &amp;amp;&amp;amp; rev.bySource.length) {&lt;br&gt;
      src.innerHTML = rev.bySource.map(function(s) {&lt;br&gt;
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + s.source + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(s.usd).toFixed(4) + '&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(' + s.count + ' events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      }).join('');&lt;br&gt;
    } else { setDemoRevSrc(); }&lt;br&gt;
    var day = document.getElementById('rev-day');&lt;br&gt;
    if (day &amp;amp;&amp;amp; rev.byDay &amp;amp;&amp;amp; rev.byDay.length) {&lt;br&gt;
      day.innerHTML = rev.byDay.slice(0,7).map(function(d) {&lt;br&gt;
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d.day + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(d.usd).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
      }).join('');&lt;br&gt;
    } else { setDemoRevDay(); }&lt;br&gt;
  } catch(e) {&lt;br&gt;
    document.getElementById('ort').textContent  = '$3.4700';&lt;br&gt;
    document.getElementById('ortb').textContent = '0.00005378 BTC';&lt;br&gt;
    document.getElementById('orm').textContent  = '$104.10';&lt;br&gt;
    document.getElementById('ore').textContent  = '200';&lt;br&gt;
    document.getElementById('cn').textContent   = '142';&lt;br&gt;
    document.getElementById('cu').textContent   = '2,100,000';&lt;br&gt;
    document.getElementById('cdg').textContent  = '4,700 GB';&lt;br&gt;
    setDemoRevSrc(); setDemoRevDay();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevSrc() {&lt;br&gt;
  var src = document.getElementById('rev-src');&lt;br&gt;
  if (src) src.innerHTML =&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;data_transmission: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$1.4500&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(100 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;active_user: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.8400&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(84 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;airpay: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.6200&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(8 payments)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;api_call: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.3100&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(62 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;storage: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.1800&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(36 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;sms: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.0700&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(70 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function setDemoRevDay() {&lt;br&gt;
  var day = document.getElementById('rev-day');&lt;br&gt;
  var days = ['2025-12-15','2025-12-14','2025-12-13','2025-12-12','2025-12-11','2025-12-10','2025-12-09'];&lt;br&gt;
  if (day) day.innerHTML = days.map(function(d) {&lt;br&gt;
    return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (0.08 + Math.random() * 0.4).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  }).join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function saveWallets() {&lt;br&gt;
  var map = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};&lt;br&gt;
  var saved = [];&lt;br&gt;
  Object.keys(map).forEach(function(k) {&lt;br&gt;
    var el = document.getElementById(k);&lt;br&gt;
    if (el &amp;amp;&amp;amp; el.value.trim()) {&lt;br&gt;
      WALLETS[map[k]] = el.value.trim();&lt;br&gt;
      localStorage.setItem('w_' + map[k], el.value.trim());&lt;br&gt;
      saved.push(map[k]);&lt;br&gt;
    }&lt;br&gt;
  });&lt;br&gt;
  var res = document.getElementById('wsres');&lt;br&gt;
  if (res) {&lt;br&gt;
    res.className = 'rb gb show';&lt;br&gt;
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; WALLET ADDRESSES SAVED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +&lt;br&gt;
      saved.map(function(c) { return '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;' + c + ': &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + WALLETS[c] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;'; }).join('') +&lt;br&gt;
      '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Addresses saved locally and displayed on AirPay page. For production backend, set as OWNER_BTC_WALLET etc. environment variables.&amp;lt;/div&amp;gt;';&lt;br&gt;
  }&lt;br&gt;
  renderAirPay();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== LIVE LOG FEED ===== */&lt;br&gt;
var LOG_EVENTS = [&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Nairobi Node&amp;lt;/strong&amp;gt; &amp;amp;mdash; 12,440 users allocated free data bundle'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; $0.0024 revenue logged automatically'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirAPI Gateway&amp;lt;/strong&amp;gt; &amp;amp;mdash; 50K requests served to Lagos cluster'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; New payment request: 0.000038 BTC pending'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Jakarta Relay&amp;lt;/strong&amp;gt; &amp;amp;mdash; bandwidth auto-scaled to 340 Gbps'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirVault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 80,000 files synced across 8 geo-regions'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank Vault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 842,000 AirUnits stored, earning $0.421/mo'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Mumbai Cluster&amp;lt;/strong&amp;gt; &amp;amp;mdash; 4 new nodes joined the free mesh grid'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; ETH bundle confirmed: Pro 500 GB activated'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Tokyo Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; +12 Gbps from commercial surplus pooled'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirSMS&amp;lt;/strong&amp;gt; &amp;amp;mdash; 230,000 encrypted messages routed free'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; Active user revenue: $0.001 x 2,100,000 users'},&lt;br&gt;
  {c:'lg',  m:'&amp;lt;strong&amp;gt;São Paulo&amp;lt;/strong&amp;gt; &amp;amp;mdash; failover triggered, rerouting via satellite'},&lt;br&gt;
  {c:'lb',  m:'&amp;lt;strong&amp;gt;London Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; 99.99% uptime milestone reached this quarter'},&lt;br&gt;
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; USDT payment received: Standard 100 GB bundle'}&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;var logIdx = 0;&lt;/p&gt;

&lt;p&gt;function addLogEntry(msg, cls, feed) {&lt;br&gt;
  if (!feed) return;&lt;br&gt;
  var el = document.createElement('div');&lt;br&gt;
  el.className = 'le';&lt;br&gt;
  var ts = new Date().toLocaleTimeString();&lt;br&gt;
  el.innerHTML = '&amp;lt;div class="ld ' + cls + '"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div class="lt"&amp;gt;' + msg + '&amp;lt;span class="ltm"&amp;gt;just now &amp;amp;middot; ' + ts + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';&lt;br&gt;
  feed.insertBefore(el, feed.children[1] || null);&lt;br&gt;
  while (feed.querySelectorAll('.le').length &amp;gt; 8) {&lt;br&gt;
    var last = feed.querySelector('.le:last-child');&lt;br&gt;
    if (last) last.remove();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function initLogs() {&lt;br&gt;
  var feed = document.getElementById('log-home');&lt;br&gt;
  if (feed) {&lt;br&gt;
    LOG_EVENTS.slice(0, 6).forEach(function(e, i) {&lt;br&gt;
      setTimeout(function() { addLogEntry(e.m, e.c, feed); }, i * 120);&lt;br&gt;
    });&lt;br&gt;
    logIdx = 6;&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function startLiveLog() {&lt;br&gt;
  try {&lt;br&gt;
    var es = new EventSource(API + '/api/events');&lt;br&gt;
    es.onmessage = function(ev) {&lt;br&gt;
      var d = JSON.parse(ev.data);&lt;br&gt;
      var cls = d.type === 'revenue' ? 'lgd' : d.type === 'warning' ? 'lr' : d.type === 'info' ? 'lb' : 'lg';&lt;br&gt;
      var feed = document.getElementById('log-home');&lt;br&gt;
      if (feed) addLogEntry('&amp;lt;strong&amp;gt;LIVE&amp;lt;/strong&amp;gt; &amp;amp;mdash; ' + d.message, cls, feed);&lt;br&gt;
    };&lt;br&gt;
    es.onerror = function() { es.close(); startFallbackLog(); };&lt;br&gt;
  } catch(e) { startFallbackLog(); }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;function startFallbackLog() {&lt;br&gt;
  setInterval(function() {&lt;br&gt;
    var e = LOG_EVENTS[logIdx % LOG_EVENTS.length]; logIdx++;&lt;br&gt;
    var feed = document.getElementById('log-home');&lt;br&gt;
    if (feed) addLogEntry(e.m, e.c, feed);&lt;br&gt;
  }, 3500);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* ===== INIT ===== */&lt;br&gt;
window.addEventListener('load', function() {&lt;br&gt;
  loadStats();&lt;br&gt;
  initLogs();&lt;br&gt;
  startLiveLog();&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
 is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>AIRTIMES GLOBAL NETWORK</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Mon, 01 Jun 2026 12:51:56 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/airtimes-global-network-53jc</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/airtimes-global-network-53jc</guid>
      <description>&lt;p&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
AIRTIMES — Global Network of Data &amp;amp; Free Services
&lt;br&gt;



:root {
  --bg: #020508; --sf: #060d14; --sf2: #0a1520;
  --ac: #00ffe0; --ac2: #0077ff; --ac3: #ff4d6d;
  --gd: #f5c518; --gd2: #ff9f00;
  --tx: #c8e8ff; --mt: #3a5570;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { background: var(--bg); color: var(--tx); font-family: 'Space Mono', monospace; overflow-x: hidden; }

.bg-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse 80% 60% at 20% 10%, rgba(0,119,255,.07), transparent 70%),
  radial-gradient(ellipse 60% 50% at 80% 80%, rgba(0,255,224,.06), transparent 70%),
  radial-gradient(ellipse 40% 40% at 50% 50%, rgba(245,197,24,.03), transparent 70%); }
.grid-overlay { position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image: linear-gradient(rgba(0,255,224,.04) 1px, transparent 1px),
  linear-gradient(90deg, rgba(0,255,224,.04) 1px, transparent 1px);
  background-size: 48px 48px; animation: gridDrift 30s linear infinite; }
@keyframes gridDrift { to { background-position: 48px 48px; } }
.scanline { position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: repeating-linear-gradient(to bottom, transparent 0, transparent 3px, rgba(0,0,0,.06) 3px, rgba(0,0,0,.06) 4px); }
.shell { position: relative; z-index: 2; }

/* NAV */
nav { display: flex; align-items: center; justify-content: space-between;
  padding: 15px 38px; border-bottom: 1px solid rgba(0,255,224,.1);
  backdrop-filter: blur(14px); position: sticky; top: 0; z-index: 200; background: rgba(2,5,8,.9); }
.logo { font-family: 'Bebas Neue'; font-size: 1.85rem; letter-spacing: .25em;
  color: var(--ac); text-shadow: 0 0 24px rgba(0,255,224,.5); cursor: pointer; position: relative; }
.logo::after { content: '●'; font-size: .35em; color: var(--ac3);
  position: absolute; top: 4px; right: -14px; animation: blink 1.4s step-end infinite; }
@keyframes blink { 50% { opacity: 0; } }
.nl { display: flex; gap: 22px; list-style: none; }
.nl a { color: var(--mt); text-decoration: none; font-size: .65rem; letter-spacing: .14em;
  text-transform: uppercase; transition: color .2s; cursor: pointer; }
.nl a:hover { color: var(--ac); }
.nl a.ga { color: var(--gd); }
.nl a.ga:hover { color: var(--gd2); }
.sp { display: flex; align-items: center; gap: 8px; font-size: .6rem; color: var(--ac); letter-spacing: .12em; }
.pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--ac);
  box-shadow: 0 0 8px var(--ac); animation: blink 1.4s step-end infinite; }

/* TABS */
.tab-bar { display: flex; border-bottom: 1px solid rgba(0,255,224,.08); background: rgba(2,5,8,.75);
  position: sticky; top: 61px; z-index: 100; backdrop-filter: blur(10px); overflow-x: auto; }
.tab { padding: 13px 22px; font-size: .62rem; letter-spacing: .15em; text-transform: uppercase;
  color: var(--mt); cursor: pointer; border-bottom: 2px solid transparent; transition: all .25s; white-space: nowrap; flex-shrink: 0; }
.tab:hover { color: var(--tx); }
.tab.active { color: var(--ac); border-bottom-color: var(--ac); }
.tab.gt { color: rgba(245,197,24,.5); }
.tab.gt:hover { color: var(--gd); }
.tab.gt.active { color: var(--gd); border-bottom-color: var(--gd); }

/* PAGES */
.page { display: none; animation: pi .3s ease; }
.page.active { display: block; }
@keyframes pi { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.sec { max-width: 1300px; margin: 0 auto; padding: 56px 40px; }
.sl { font-size: .58rem; color: var(--ac); letter-spacing: .28em; text-transform: uppercase; margin-bottom: 8px; }
.sl.gd { color: var(--gd); }
.st { font-family: 'Bebas Neue'; font-size: 2.8rem; color: #fff; letter-spacing: .05em; margin-bottom: 28px; line-height: 1; }
.sub { font-size: .76rem; color: var(--mt); line-height: 1.9; max-width: 640px; margin-bottom: 36px; }

/* HERO */
.hero { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center;
  padding: 76px 40px 56px; max-width: 1300px; margin: 0 auto; }
.htag { font-size: .62rem; letter-spacing: .24em; text-transform: uppercase;
  color: var(--ac2); margin-bottom: 12px; display: flex; align-items: center; gap: 10px; }
.htag::before { content: ''; width: 30px; height: 1px; background: var(--ac2); }
h1 { font-family: 'Bebas Neue'; font-size: clamp(3.2rem,6.5vw,6rem);
  line-height: .92; letter-spacing: .05em; color: #fff; margin-bottom: 20px; }
h1 .t { color: var(--ac); text-shadow: 0 0 25px rgba(0,255,224,.4); }
h1 .r { color: var(--ac3); }
h1 .g { color: var(--gd); }
.hd { font-size: .78rem; line-height: 1.9; color: var(--mt); max-width: 420px; margin-bottom: 32px; }
.ha { display: flex; gap: 10px; flex-wrap: wrap; }

/* BUTTONS */
.btn { border: none; padding: 12px 24px; font-family: 'Space Mono'; font-size: .68rem;
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase; cursor: pointer; transition: all .25s; }
.bp { background: var(--ac); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }
.bp:hover { box-shadow: 0 0 28px rgba(0,255,224,.3); transform: translateY(-2px); }
.bg { background: var(--gd); color: #000; clip-path: polygon(0 0,calc(100% - 10px) 0,100% 10px,100% 100%,0 100%); }
.bg:hover { box-shadow: 0 0 28px rgba(245,197,24,.3); transform: translateY(-2px); }
.bs { background: transparent; color: var(--tx); border: 1px solid var(--mt); }
.bs:hover { border-color: var(--ac); color: var(--ac); }
.bsm { padding: 8px 16px; font-size: .6rem; }

/* GLOBE */
.gw { position: relative; display: flex; align-items: center; justify-content: center; height: 400px; }
.globe { width: 290px; height: 290px; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, rgba(0,119,255,.25), transparent 60%),
  radial-gradient(circle at 50% 50%, #020d1a 40%, #010608 100%);
  border: 1px solid rgba(0,255,224,.15);
  box-shadow: 0 0 80px rgba(0,119,255,.2), inset 0 0 60px rgba(0,119,255,.1);
  animation: gp 6s ease-in-out infinite; }
@keyframes gp { 50% { box-shadow: 0 0 120px rgba(0,119,255,.35), inset 0 0 80px rgba(0,119,255,.15); } }
.orb { position: absolute; border-radius: 50%; border: 1px solid rgba(0,255,224,.12); }
.o1 { width: 370px; height: 370px; animation: os 12s linear infinite; }
.o2 { width: 450px; height: 450px; border-color: rgba(0,119,255,.08); animation: os 20s linear infinite reverse; }
@keyframes os { to { transform: rotate(360deg); } }
.on { position: absolute; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ac); box-shadow: 0 0 12px var(--ac); top: -4px; left: 50%; transform: translateX(-50%); }
.o2 .on { background: var(--ac2); box-shadow: 0 0 12px var(--ac2); }
.sig { position: absolute; top: 50%; left: 50%; height: 1px;
  background: linear-gradient(90deg, rgba(0,255,224,.5), transparent);
  transform-origin: left; animation: sp 3s ease-in-out infinite; }
@keyframes sp { 0% { opacity: 0; width: 0; } 40% { opacity: 1; } 100% { opacity: 0; width: 150px; } }
.s1 { transform: rotate(35deg); }
.s2 { transform: rotate(135deg); animation-delay: .8s; }
.s3 { transform: rotate(220deg); animation-delay: 1.6s; }
.s4 { transform: rotate(310deg); animation-delay: 2.4s; }
.db { position: absolute; background: rgba(6,13,20,.92); border: 1px solid rgba(0,255,224,.2);
  padding: 7px 12px; font-size: .57rem; color: var(--ac); letter-spacing: .1em;
  backdrop-filter: blur(8px); animation: fu 4s ease-in-out infinite; }
.db.b1 { top: 46px; left: 4px; }
.db.b2 { bottom: 66px; right: -2px; animation-delay: 1.5s; color: var(--ac2); border-color: rgba(0,119,255,.3); }
.db.b3 { top: 166px; right: -4px; animation-delay: .8s; color: var(--gd); border-color: rgba(245,197,24,.3); }
@keyframes fu { 50% { transform: translateY(-8px); } }

/* TICKER */
.tw { overflow: hidden; padding: 10px 0; border-top: 1px solid rgba(0,255,224,.07);
  border-bottom: 1px solid rgba(0,255,224,.07); background: rgba(0,255,224,.02); }
.tt { display: flex; gap: 56px; white-space: nowrap; animation: ts 35s linear infinite; }
@keyframes ts { to { transform: translateX(-50%); } }
.ti { display: flex; align-items: center; gap: 10px; font-size: .62rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--mt); }
.dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ac); }
.dot.gd { background: var(--gd); }

/* STATS ROW */
.sr { max-width: 1300px; margin: 50px auto; padding: 0 40px;
  display: grid; grid-template-columns: repeat(4,1fr); gap: 2px; }
.sc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);
  padding: 26px 22px; position: relative; overflow: hidden; transition: border-color .3s; }
.sc:hover { border-color: rgba(0,255,224,.2); }
.sc::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--ac), transparent); opacity: 0; transition: opacity .3s; }
.sc:hover::before { opacity: 1; }
.sn { font-family: 'Bebas Neue'; font-size: 2.6rem; color: var(--ac); line-height: 1; margin-bottom: 4px; }
.sn.b { color: var(--ac2); } .sn.r { color: var(--ac3); } .sn.gd { color: var(--gd); }
.sl2 { font-size: .56rem; color: var(--mt); letter-spacing: .18em; text-transform: uppercase; }

/* CARDS */
.cg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; }
.card { background: var(--sf); border: 1px solid rgba(0,255,224,.07);
  padding: 30px 24px; position: relative; overflow: hidden; cursor: pointer; transition: all .3s; }
.card:hover { background: var(--sf2); border-color: rgba(0,255,224,.2); transform: translateY(-3px); }
.card::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--ac2), var(--ac));
  transform: scaleX(0); transform-origin: left; transition: transform .4s; }
.card:hover::after { transform: scaleX(1); }
.ci { font-size: 1.7rem; margin-bottom: 16px; }
.cn { font-family: 'Syne'; font-size: .95rem; font-weight: 700; color: #fff; margin-bottom: 8px; }
.cd { font-size: .68rem; line-height: 1.8; color: var(--mt); }
.ctag { display: inline-block; margin-top: 14px; font-size: .54rem; letter-spacing: .17em;
  text-transform: uppercase; color: var(--ac); border: 1px solid rgba(0,255,224,.2); padding: 3px 9px; }
.ctag.b { color: var(--ac2); border-color: rgba(0,119,255,.25); }
.ctag.r { color: var(--ac3); border-color: rgba(255,77,109,.25); }
.ctag.gd { color: var(--gd); border-color: rgba(245,197,24,.25); }

/* FORMS */
.fp { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; }
.fp.gp { border-color: rgba(245,197,24,.2); }
.fg { margin-bottom: 14px; }
.fl { font-size: .56rem; color: var(--mt); letter-spacing: .2em; text-transform: uppercase; margin-bottom: 6px; display: block; }
.fi { background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);
  color: var(--tx); padding: 10px 14px; font-family: 'Space Mono'; font-size: .7rem;
  outline: none; width: 100%; transition: border-color .2s; }
.fi:focus { border-color: var(--ac); }
.fi.gi { border-color: rgba(245,197,24,.2); }
.fi.gi:focus { border-color: var(--gd); }
.fi::placeholder { color: var(--mt); }
select.fi option { background: #020508; }
.rb { margin-top: 16px; background: rgba(0,255,224,.03); border: 1px solid rgba(0,255,224,.15);
  padding: 16px; font-size: .67rem; line-height: 2.2; display: none; }
.rb.show { display: block; }
.rb.gb { border-color: rgba(245,197,24,.2); background: rgba(245,197,24,.02); }
.rk { color: var(--mt); } .rv { color: var(--ac); }
.rv.gd { color: var(--gd); } .rv.g { color: #4ade80; }

/* WALLET GRID */
.wg { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 28px; }
.wc { background: var(--sf); border: 1px solid rgba(245,197,24,.12);
  padding: 22px 16px; text-align: center; cursor: pointer; transition: all .3s; position: relative; }
.wc:hover { border-color: rgba(245,197,24,.4); background: rgba(245,197,24,.04); }
.wc.sel { border-color: var(--gd); background: rgba(245,197,24,.06); }
.wc.sel::before { content: '✓'; position: absolute; top: 7px; right: 9px; font-size: .58rem; color: var(--gd); }
.wi { font-size: 1.9rem; margin-bottom: 7px; }
.wco { font-family: 'Bebas Neue'; font-size: 1.15rem; color: var(--gd); letter-spacing: .1em; }
.wna { font-size: .55rem; color: var(--mt); letter-spacing: .1em; margin-top: 2px; }
.wad { font-size: .46rem; color: rgba(58,85,112,.55); margin-top: 7px; word-break: break-all; line-height: 1.4; }

/* BUNDLE GRID */
.bg2 { display: grid; grid-template-columns: repeat(5,1fr); gap: 2px; margin-bottom: 22px; }
.bc { background: var(--sf); border: 1px solid rgba(0,255,224,.08);
  padding: 20px 14px; text-align: center; cursor: pointer; transition: all .3s; }
.bc:hover { border-color: rgba(0,255,224,.25); }
.bc.sel { border-color: var(--ac); background: rgba(0,255,224,.04); }
.bn { font-family: 'Bebas Neue'; font-size: 1rem; color: #fff; letter-spacing: .1em; }
.bdata { font-size: 1.3rem; color: var(--ac); font-family: 'Bebas Neue'; line-height: 1.1; }
.bprice { font-size: .6rem; color: var(--mt); margin-top: 4px; }
.bbtc { font-size: .56rem; color: var(--gd); margin-top: 2px; }

/* AIRBANK */
.vd { background: var(--sf); border: 1px solid rgba(0,255,224,.1); padding: 32px; margin-bottom: 2px; }
.vn { font-family: 'Bebas Neue'; font-size: 3.5rem; color: var(--ac); line-height: 1; text-shadow: 0 0 40px rgba(0,255,224,.3); }
.vu { font-size: .67rem; color: var(--mt); letter-spacing: .24em; text-transform: uppercase; margin-top: 4px; }
.rg { display: grid; grid-template-columns: repeat(3,1fr); gap: 2px; margin-bottom: 2px; }
.rc { background: var(--sf); border: 1px solid rgba(245,197,24,.1); padding: 26px; position: relative; }
.rc::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gd), transparent); }
.rn { font-family: 'Bebas Neue'; font-size: 2.3rem; color: var(--gd); line-height: 1; margin-bottom: 4px; }
.rl { font-size: .56rem; color: var(--mt); letter-spacing: .17em; text-transform: uppercase; }
.rs { font-size: .6rem; color: rgba(245,197,24,.5); margin-top: 4px; }

/* PROGRESS */
.pr { margin-bottom: 12px; }
.pl { display: flex; justify-content: space-between; font-size: .58rem; color: var(--mt); letter-spacing: .1em; margin-bottom: 5px; }
.pb { height: 4px; background: rgba(0,255,224,.08); border-radius: 2px; overflow: hidden; }
.pf { height: 100%; border-radius: 2px; background: linear-gradient(90deg, var(--ac2), var(--ac));
  animation: fb 2s ease forwards; transform-origin: left; }
@keyframes fb { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.pf.gd { background: linear-gradient(90deg, var(--gd2), var(--gd)); }
.pf.r { background: linear-gradient(90deg, var(--ac3), #ff9a6c); }

/* TABLE */
.dt { width: 100%; border-collapse: collapse; font-size: .64rem; }
.dt th { color: var(--mt); letter-spacing: .14em; text-transform: uppercase; font-size: .54rem;
  padding: 9px 13px; text-align: left; border-bottom: 1px solid rgba(0,255,224,.08); }
.dt td { padding: 9px 13px; border-bottom: 1px solid rgba(255,255,255,.03); color: var(--tx); }
.dt tr:hover td { background: rgba(0,255,224,.02); }
.bdg { display: inline-block; padding: 2px 8px; font-size: .5rem; letter-spacing: .1em;
  text-transform: uppercase; border-radius: 2px; }
.bg3 { background: rgba(0,255,224,.1); color: var(--ac); border: 1px solid rgba(0,255,224,.2); }
.bgd { background: rgba(245,197,24,.1); color: var(--gd); border: 1px solid rgba(245,197,24,.2); }
.br { background: rgba(255,77,109,.1); color: var(--ac3); border: 1px solid rgba(255,77,109,.2); }

/* LOG FEED */
.lf { background: var(--sf); border: 1px solid rgba(0,255,224,.08); padding: 20px; min-height: 260px; }
.le { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,.03);
  animation: fi .4s ease; }
@keyframes fi { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }
.ld { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 5px; }
.lg { background: var(--ac); box-shadow: 0 0 8px var(--ac); }
.lb { background: var(--ac2); box-shadow: 0 0 8px var(--ac2); }
.lr { background: var(--ac3); box-shadow: 0 0 8px var(--ac3); }
.lgd { background: var(--gd); box-shadow: 0 0 8px var(--gd); }
.lt { font-size: .62rem; line-height: 1.7; color: var(--mt); }
.lt strong { color: var(--tx); font-weight: 400; }
.ltm { font-size: .54rem; color: rgba(58,85,112,.5); display: block; margin-top: 1px; }

/* SVG MAP */
.mc { width: 100%; height: 230px; position: relative; margin-top: 18px; }
.ms { width: 100%; height: 100%; }

/* HIGHLIGHT BOX */
.hb { background: rgba(245,197,24,.04); border: 1px solid rgba(245,197,24,.2);
  padding: 18px 22px; margin-bottom: 22px; }
.hb p { font-size: .7rem; line-height: 1.9; color: var(--tx); }
.hb strong { color: var(--gd); }

/* CALC DISPLAY */
.cd { background: #000; border: 1px solid rgba(245,197,24,.2); padding: 18px;
  font-family: 'Bebas Neue'; font-size: 1.8rem; color: var(--gd);
  text-align: right; letter-spacing: .1em; min-height: 64px; margin-bottom: 14px; }
.cd .cs { font-size: .76rem; color: rgba(245,197,24,.5); font-family: 'Space Mono'; }

.copy-btn { background: rgba(245,197,24,.08); border: 1px solid rgba(245,197,24,.2);
  color: var(--gd); padding: 4px 12px; font-size: .56rem; cursor: pointer;
  font-family: 'Space Mono'; letter-spacing: .1em; transition: all .2s; }
.copy-btn:hover { background: rgba(245,197,24,.18); }

.div { border: none; border-top: 1px solid rgba(0,255,224,.06); margin: 36px 0; }
.tc { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.thc { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 2px; }

/* FOOTER */
footer { border-top: 1px solid rgba(0,255,224,.06); padding: 30px 40px;
  display: flex; align-items: center; justify-content: space-between;
  max-width: 1300px; margin: 0 auto; }
.fl2 { font-family: 'Bebas Neue'; font-size: 1.2rem; color: var(--mt); letter-spacing: .25em; }
.fc { font-size: .54rem; color: rgba(58,85,112,.5); letter-spacing: .1em; }
.fs { display: flex; align-items: center; gap: 8px; font-size: .58rem; color: var(--ac); letter-spacing: .15em; }

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; }
  .gw { height: 270px; } .globe { width: 220px; height: 220px; }
  .o1 { width: 280px; height: 280px; } .o2 { width: 350px; height: 350px; }
  .sr { grid-template-columns: repeat(2,1fr); }
  .cg { grid-template-columns: 1fr; }
  .wg { grid-template-columns: repeat(2,1fr); }
  .bg2 { grid-template-columns: repeat(2,1fr); }
  .rg { grid-template-columns: 1fr; }
  .tc { grid-template-columns: 1fr; }
  .thc { grid-template-columns: 1fr; }
  .nl { display: none; }
  nav { padding: 12px 18px; }
  .sec { padding: 40px 18px; }
}




&lt;p&gt;AIRTIMES&lt;br&gt;
  &lt;/p&gt;
&lt;ul&gt;

    &lt;li&gt;&lt;a&gt;Network&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;Services&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;Nodes&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;₿ AirPay&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;🏦 AirBank&lt;/a&gt;&lt;/li&gt;

    &lt;li&gt;&lt;a&gt;👑 Owner&lt;/a&gt;&lt;/li&gt;

  &lt;/ul&gt;
&lt;br&gt;
  &lt;span id="nst"&gt;CONNECTING...&lt;/span&gt;

&lt;p&gt;🌐 Home&lt;br&gt;
  ⚡ Services&lt;br&gt;
  🔗 Connect&lt;br&gt;
  ◯ Nodes&lt;br&gt;
  📡 Route&lt;br&gt;
  ₿ AirPay&lt;br&gt;
  🏦 AirBank&lt;br&gt;
  👑 Owner Dashboard&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Global Data Mesh — v3.2.0
  &amp;lt;h1&amp;gt;FREE&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;DATA&amp;lt;/span&amp;gt; &amp;amp;amp;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;SERVICE&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span&amp;gt;NETWORK&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;AIRTIMES delivers free data, connectivity, and digital services to every node on Earth. Users connect free. Owner earns automatically via AirPay crypto payments and AirBank data revenue — around the clock.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    GET FREE ACCESS
    ₿ BUY DATA BUNDLE
    🏦 AirBank







  ▲ 4,700 Gbps THROUGHPUT
  ◯ 142 ACTIVE NODES
  ₿ AUTO-EARNING ACTIVE





&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRDATA FREE TIER ACTIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK — $0.0024 REVENUE LOGGED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;NAIROBI NODE ONLINE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — BTC PAYMENT RECEIVED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;2.1M USERS CONNECTED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRBANK VAULT — 842 TB STORED
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;JAKARTA HUB — 340 Gbps
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AUTO-REVENUE TICKING EVERY SESSION
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRTIMES PROTOCOL v3.2 LIVE
&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;AIRPAY — ETH BUNDLE CONFIRMED




—Active Nodes Worldwide
—Daily Data Delivered (TB)
—Users Served Free
—Auto Revenue Earned (USD)



// Three Systems · One Network
HOW AIRTIMES WORKS


    🌐
    Free Global Network
    Users connect free worldwide via AirData, AirSMS, AirWeb, AirVault, AirAPI, AirRelay. Zero cost to users forever. Powered by surplus bandwidth redistribution across 142+ global nodes.
    FREE FOR ALL USERS


    ₿
    AirPay — Crypto Payments
    Users buy premium data bundles using Bitcoin, Ethereum, USDT, BNB, or Litecoin. Every payment routes directly to the owner's crypto wallet instantly. No middleman, no processor fees.
    BTC · ETH · USDT · BNB · LTC


    🏦
    AirBank — Auto Revenue Engine
    Every byte transmitted earns the owner automatically. Trillions of AirUnits stored in the vault, converted to direct money on every user session. Earn while you sleep.
    EARN AUTOMATICALLY







    📋 Live Network Events


    💰 Revenue Snapshot
    Loading revenue data...
    VIEW FULL OWNER DASHBOARD →









// All Free + Paid Services
WHAT AIRTIMES PROVIDES

  📡AirData — Free Mobile DataZero-cost mobile internet routing through the AIRTIMES mesh. Any device, any carrier, any country. Bandwidth pooled from surplus commercial nodes globally.FREE · UNLIMITED
  💬AirSMS — Global MessagingEnd-to-end encrypted SMS and voice relay across all 193 countries. Delivered free via spare spectrum on partner networks. Auto-revenue for owner per SMS.FREE · E2E ENCRYPTED
  🌐AirWeb — Open BrowserCompressed web proxy serving the full open internet at zero data cost. Optimized for low-bandwidth environments and offline caching. Up to 70% compression ratio.FREE · COMPRESSED
  🔗AirAPI — Open Data GatewayFree access to global datasets: weather, finance, health, transport. 50,000 requests/day per node. Developer-grade REST and WebSocket access.FREE · 50K RPD
  🛰AirRelay — Satellite FallbackWhen ground infrastructure fails, AIRTIMES auto-routes through low-earth-orbit relay partners to maintain continuous free connectivity globally.RESILIENT · AUTO
  💾AirVault — Distributed Storage5 GB of free distributed cloud storage per user. Sharded across 8+ geo-regions for redundancy. Served at zero cost from network surplus capacity.FREE · 5 GB
  ₿AirPay — Crypto Payment GatewayUsers buy premium data bundles with Bitcoin, Ethereum, USDT, BNB, or Litecoin. Payments go directly to the owner's wallet. No middleman. Instant settlement.BTC · ETH · USDT · BNB · LTC
  🏦AirBank — Auto Revenue EngineEvery user session generates automatic revenue for the owner. Trillions of AirUnits stored and converted to direct money. Revenue from data, SMS, API, storage.AUTO REVENUE
  🗺AirRoute — Smart Mesh RoutingDijkstra mesh routing with Haversine geo-scoring. Auto-failover. Multi-hop path optimization. Load rebalancing every 30 seconds across all nodes.AI ROUTING








// Free Access
JOIN THE NETWORK
&amp;lt;p&amp;gt;Register your phone or email to get a free AIRTIMES token. Instantly connected to 142+ nodes worldwide. No credit card. No contracts. Forever free.&amp;lt;/p&amp;gt;


    Phone or Email

    Identifier Type
      EmailPhone
    ACTIVATE FREE ACCESS →
    &amp;lt;span&amp;gt;Fill in your details and click activate.&amp;lt;/span&amp;gt;


    What you get FREE:

      ✦ AirData — Unlimited free mobile internet&amp;lt;br&amp;gt;
      ✦ AirSMS — Global encrypted messaging&amp;lt;br&amp;gt;
      ✦ AirWeb — Compressed web proxy&amp;lt;br&amp;gt;
      ✦ AirAPI — 50,000 free API calls/day&amp;lt;br&amp;gt;
      ✦ AirVault — 5 GB free cloud storage&amp;lt;br&amp;gt;
      ✦ AirRelay — Satellite connectivity fallback&amp;lt;br&amp;gt;
      ✦ Assigned to nearest global mesh node&amp;lt;br&amp;gt;
      ✦ 10 GB daily data quota&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    Want more data? &amp;lt;span&amp;gt;Buy a premium bundle with crypto via AirPay →&amp;lt;/span&amp;gt;


&amp;lt;hr&amp;gt;
// Free API Test
AIRAPI GATEWAY


    City (Free Weather)

    GET FREE WEATHER →
    Exchange Rate



        GET RATE


    &amp;lt;span&amp;gt;Results appear here — all free, no API key needed.&amp;lt;/span&amp;gt;


    🌐 AirAPI Specs

      ● &amp;lt;span&amp;gt;50,000&amp;lt;/span&amp;gt; free calls/day per node&amp;lt;br&amp;gt;
      ● Weather data for all cities worldwide&amp;lt;br&amp;gt;
      ● Live exchange rates (150+ currencies)&amp;lt;br&amp;gt;
      ● Health, transport, finance datasets&amp;lt;br&amp;gt;
      ● REST + WebSocket endpoints&amp;lt;br&amp;gt;
      ● No API key needed for free tier&amp;lt;br&amp;gt;
      ● Each call auto-earns revenue for owner&amp;lt;br&amp;gt;










// Mesh Routing Engine
TEST YOUR ROUTE
&amp;lt;p&amp;gt;Enter your token and destination to compute the optimal mesh path through AIRTIMES global nodes using Haversine geo-scoring and Dijkstra path finding.&amp;lt;/p&amp;gt;


    Your Token (from registration)

    Destination

    Service

        AirDataAirSMSAirWeb
        AirAPIAirVaultAirRelay

    COMPUTE ROUTE →


    Run a route test to see your full mesh path, latency estimate, hop count, and protocol details.









// Global Infrastructure
LIVE NODE MAP

  &amp;lt;svg&amp;gt;
    &amp;lt;line x1="0" y1="80" x2="900" y2="80" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="0" y1="160" x2="900" y2="160" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="225" y1="0" x2="225" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="450" y1="0" x2="450" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;line x1="675" y1="0" x2="675" y2="240" stroke="" stroke-width="1"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;g stroke="" stroke-width="1" fill="none"&amp;gt;
      &amp;lt;line x1="80" y1="90" x2="200" y2="70"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="200" y1="70" x2="340" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="340" y1="100" x2="480" y2="80"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="600" y2="110"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="740" y2="90"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="740" y1="90" x2="840" y2="100"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="200" y1="70" x2="280" y2="150"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="480" y1="80" x2="520" y2="170"&amp;gt;&amp;lt;/line&amp;gt;
      &amp;lt;line x1="600" y1="110" x2="580" y2="180"&amp;gt;&amp;lt;/line&amp;gt;&amp;lt;line x1="80" y1="90" x2="120" y2="180"&amp;gt;&amp;lt;/line&amp;gt;
    &amp;lt;/g&amp;gt;
    &amp;lt;circle r="3" fill=""&amp;gt;&amp;lt;path d="M80,90 L200,70 L340,100 L480,80 L600,110 L740,90 L840,100"&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;circle r="2.5" fill=""&amp;gt;&amp;lt;path d="M840,100 L740,90 L600,110 L480,80 L340,100 L200,70 L80,90"&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/circle&amp;gt;
    &amp;lt;g&amp;gt;
      &amp;lt;circle cx="80" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="80" cy="90" r="3.5" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;NYC
      &amp;lt;circle cx="200" cy="70" r="10" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="200" cy="70" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;LDN
      &amp;lt;circle cx="340" cy="100" r="7" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="340" cy="100" r="3" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;NBI
      &amp;lt;circle cx="480" cy="80" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="480" cy="80" r="4" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;DXB
      &amp;lt;circle cx="600" cy="110" r="8" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="600" cy="110" r="3.5" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;JKT
      &amp;lt;circle cx="740" cy="90" r="9" fill="" stroke="" stroke-width="1.5"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="740" cy="90" r="4" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;TYO
      &amp;lt;circle cx="840" cy="100" r="7" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="840" cy="100" r="3" fill="#00ffe0"&amp;gt;&amp;lt;/circle&amp;gt;SYD
      &amp;lt;circle cx="120" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="120" cy="180" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;GRU
      &amp;lt;circle cx="520" cy="170" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="520" cy="170" r="2" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;BOM
      &amp;lt;circle cx="580" cy="180" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="580" cy="180" r="2" fill="#ff4d6d"&amp;gt;&amp;lt;/circle&amp;gt;SIN
      &amp;lt;circle cx="660" cy="50" r="6" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="660" cy="50" r="2.5" fill="#0077ff"&amp;gt;&amp;lt;/circle&amp;gt;SHA
      &amp;lt;circle cx="155" cy="105" r="5" fill="" stroke="" stroke-width="1"&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;circle cx="155" cy="105" r="2" fill="#f5c518"&amp;gt;&amp;lt;/circle&amp;gt;LOS
    &amp;lt;/g&amp;gt;
  &amp;lt;/svg&amp;gt;

&amp;lt;table id="nodes-table"&amp;gt;
  &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

Node ID
&lt;br&gt;
City
&lt;br&gt;
Country
&lt;br&gt;
Operator
&lt;br&gt;
Capacity (Gbps)
&lt;br&gt;
Load
&lt;br&gt;
Status
&lt;br&gt;
&lt;br&gt;
      Loading nodes...
&lt;br&gt;
    
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bitcoin &amp;amp;amp; Crypto Payment Gateway
₿ AIRPAY

  &amp;lt;p&amp;gt;AirPay lets users buy premium data bundles using &amp;lt;strong&amp;gt;Bitcoin, Ethereum, USDT, BNB, or Litecoin&amp;lt;/strong&amp;gt;. Every payment goes &amp;lt;strong&amp;gt;directly to the AIRTIMES owner's crypto wallet&amp;lt;/strong&amp;gt; — instantly, no middleman, zero processor fees. Select a bundle, choose your crypto, and send payment to the displayed address.&amp;lt;/p&amp;gt;


// Owner Wallet Addresses — All Payments Are Sent Directly Here


&amp;lt;hr&amp;gt;
// Select Your Data Bundle
Choose how much premium data you want to purchase:




    ⚡ Generate Payment Address
    Your User ID (optional)

    Pay With

        ₿ Bitcoin (BTC)
        Ξ Ethereum (ETH)
        ₮ Tether USDT (TRC-20)
        ⬡ BNB Coin
        Ł Litecoin (LTC)

    GENERATE PAYMENT ADDRESS →
    &amp;lt;span&amp;gt;Select a bundle above, then click to generate your payment address.&amp;lt;/span&amp;gt;


    📋 How AirPay Works

      &amp;lt;span&amp;gt;Step 1.&amp;lt;/span&amp;gt; Select a data bundle above&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 2.&amp;lt;/span&amp;gt; Choose Bitcoin, ETH, USDT, BNB, or LTC&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 3.&amp;lt;/span&amp;gt; Click "Generate Payment Address"&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 4.&amp;lt;/span&amp;gt; Send exact crypto amount shown&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 5.&amp;lt;/span&amp;gt; Bundle activates in 10–60 minutes&amp;lt;br&amp;gt;
      &amp;lt;span&amp;gt;Step 6.&amp;lt;/span&amp;gt; Revenue automatically in owner wallet&amp;lt;br&amp;gt;

    &amp;lt;hr&amp;gt;
    All payments go directly to owner's crypto wallets. No processor. No fees. Direct blockchain settlement.



&amp;lt;hr&amp;gt;
// Confirm Your Payment


    Payment ID

    Transaction Hash (TX Hash from blockchain)

    CONFIRM PAYMENT →
    &amp;lt;span&amp;gt;Enter Payment ID + TX hash to confirm your purchase.&amp;lt;/span&amp;gt;


    After sending crypto, paste your &amp;lt;span&amp;gt;Payment ID&amp;lt;/span&amp;gt; and the &amp;lt;span&amp;gt;transaction hash&amp;lt;/span&amp;gt; from your wallet or blockchain explorer. Data bundle activates immediately upon confirmation.









// Data Storage &amp;amp;amp; Automatic Revenue Engine
🏦 AIRBANK

  &amp;lt;p&amp;gt;AirBank is the &amp;lt;strong&amp;gt;financial backbone of AIRTIMES&amp;lt;/strong&amp;gt;. Every byte of data transmitted through the network is stored in the AirBank Vault measured in &amp;lt;strong&amp;gt;Trillions of AirUnits&amp;lt;/strong&amp;gt;. When users browse, message, stream, or use any AIRTIMES service — the owner &amp;lt;strong&amp;gt;earns money automatically&amp;lt;/strong&amp;gt;, around the clock. Data is the currency. Usage is the revenue. Extract data, convert to direct money.&amp;lt;/p&amp;gt;





    Vault Storage
    —
    Total AirUnits Stored


    Data Transmitted
    —
    GB Delivered to Users


    Total Revenue Earned
    —
    USD Earned Automatically






    Per GB Transmitted
    $0.002
    Every 1 GB routed = $0.002 earned by owner


    Per Active User / Day
    $0.001
    Every daily user session earns automatically


    Per API Call
    $0.00005
    Every AirAPI request generates revenue



&amp;lt;hr&amp;gt;
// Data → Money Calculator
Enter any amount of stored/transmitted data to see the revenue it generates for the owner:


    $ 0.00Enter amount below to calculate




          GBTBPB



    &amp;lt;span&amp;gt;Enter data amount above to calculate automatic revenue.&amp;lt;/span&amp;gt;


    📈 Revenue Sources Breakdown
    &amp;lt;span&amp;gt;Data Transmission&amp;lt;/span&amp;gt;&amp;lt;span id="pct1"&amp;gt;42%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Active Users (Daily)&amp;lt;/span&amp;gt;&amp;lt;span id="pct2"&amp;gt;24%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;AirPay Bundles&amp;lt;/span&amp;gt;&amp;lt;span id="pct3"&amp;gt;18%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;API Calls&amp;lt;/span&amp;gt;&amp;lt;span id="pct4"&amp;gt;9%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;Storage Fees&amp;lt;/span&amp;gt;&amp;lt;span id="pct5"&amp;gt;5%&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;SMS Revenue&amp;lt;/span&amp;gt;&amp;lt;span id="pct6"&amp;gt;2%&amp;lt;/span&amp;gt;









// Private Owner Control Panel
👑 OWNER DASHBOARD

  &amp;lt;p&amp;gt;This is your &amp;lt;strong&amp;gt;private revenue command center&amp;lt;/strong&amp;gt;. See every dollar earned, every AirPay payment received, every data unit transmitted. &amp;lt;strong&amp;gt;Set your Bitcoin and crypto wallet addresses below&amp;lt;/strong&amp;gt; — all AirPay payments route directly to these wallets instantly. You own the network. You own the revenue.&amp;lt;/p&amp;gt;



// ₿ Your Crypto Wallet Addresses
Enter your real wallet addresses here. Users see these when paying via AirPay. All payments land directly in your wallets.


    ₿ Bitcoin (BTC) Wallet Address

    Ξ Ethereum (ETH) Wallet Address

    ₮ USDT Wallet (TRC-20 / Tron Network)

    ⬡ BNB Wallet Address

    Ł Litecoin (LTC) Wallet Address


      SAVE WALLET ADDRESSES →



    &amp;lt;span&amp;gt;Enter your real wallet addresses and click Save. These will be shown to users on the AirPay page. For production, set as environment variables on your backend server.&amp;lt;/span&amp;gt;




// Revenue Summary

  Total Earned (USD)$—— BTC
  Projected Monthly$—Based on current daily rate
  Revenue Events Logged—Auto-logged transactions




    Revenue by Source
    Loading...


    Daily Revenue (Last 7 Days)
    Loading...





  AirPay Payment History
  &amp;lt;table&amp;gt;
    &amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

Payment ID
&lt;br&gt;
Bundle
&lt;br&gt;
Coin
&lt;br&gt;
Amount
&lt;br&gt;
Status
&lt;br&gt;
Date
&lt;br&gt;
&lt;br&gt;
        No payments yet. Share your AirPay page with users to start receiving crypto payments!
&lt;br&gt;
      
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;hr&amp;gt;
// Network Control


    Registered Users
    —
    Total on network


    Active Nodes
    —
    Online globally


    Data Routed Today
    —
    GB transmitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;AIRTIMES&lt;br&gt;
  © 2025 AIRTIMES GLOBAL NETWORK · AirPay · AirBank · All Services Free · All Rights Universal&lt;br&gt;
  ALL SYSTEMS OPERATIONAL&lt;/p&gt;

&amp;lt;!-- /shell --&amp;gt;


/* ===== CONFIG ===== */
const API = (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')
  ? 'http://localhost:3001' : '';

/* ===== WALLET STATE (saved in localStorage) ===== */
let WALLETS = {
  BTC:  localStorage.getItem('w_BTC')  || '',
  ETH:  localStorage.getItem('w_ETH')  || '',
  USDT: localStorage.getItem('w_USDT') || '',
  BNB:  localStorage.getItem('w_BNB')  || '',
  LTC:  localStorage.getItem('w_LTC')  || ''
};

const COIN_ICONS = { BTC:'&amp;amp;#8383;', ETH:'&amp;amp;#926;', USDT:'&amp;amp;#8366;', BNB:'&amp;amp;#11041;', LTC:'&amp;amp;#321;' };
const COIN_NAMES = { BTC:'Bitcoin', ETH:'Ethereum', USDT:'Tether TRC-20', BNB:'BNB Coin', LTC:'Litecoin' };
const COIN_RATES = { BTC:0.0000155, ETH:0.000290, USDT:1.0, BNB:0.00310, LTC:0.0120 };

const BUNDLES = [
  { id:'starter',   name:'Starter',   gb:5,    usd:0.50,  btc:'0.000010' },
  { id:'basic',     name:'Basic',     gb:20,   usd:1.99,  btc:'0.000038' },
  { id:'standard',  name:'Standard',  gb:100,  usd:7.99,  btc:'0.000150' },
  { id:'pro',       name:'Pro',       gb:500,  usd:29.99, btc:'0.000600' },
  { id:'unlimited', name:'Unlimited', gb:9999, usd:74.99, btc:'0.001500' }
];

let selBundle = null;

/* ===== PAGE SWITCHER ===== */
function P(id) {
  document.querySelectorAll('.page').forEach(p =&amp;gt; p.classList.remove('active'));
  document.querySelectorAll('.tab').forEach(t =&amp;gt; t.classList.remove('active'));
  const pg = document.getElementById('page-' + id);
  if (pg) pg.classList.add('active');
  document.querySelectorAll('.tab').forEach(t =&amp;gt; {
    if (t.getAttribute('onclick') &amp;amp;&amp;amp; t.getAttribute('onclick').includes("'" + id + "'")) t.classList.add('active');
  });
  window.scrollTo({ top: 0, behavior: 'smooth' });
  if (id === 'airpay')  renderAirPay();
  if (id === 'airbank') loadVault();
  if (id === 'owner')   loadOwner();
  if (id === 'nodes')   loadNodes();
}

/* ===== HELPERS ===== */
function fmt(n) { return Number(n).toLocaleString(); }
function ac(el, target, sfx, dur) {
  if (!el) return;
  sfx = sfx || ''; dur = dur || 2000;
  let v = 0; const step = target / (dur / 16);
  const t = setInterval(function() {
    v += step;
    if (v &amp;gt;= target) { v = target; clearInterval(t); }
    el.textContent = Math.floor(v).toLocaleString() + sfx;
  }, 16);
}
function uid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0;
    return (c === 'x' ? r : (r &amp;amp; 0x3 | 0x8)).toString(16);
  });
}

/* ===== LOAD STATS ===== */
async function loadStats() {
  try {
    const r = await fetch(API + '/api/stats');
    const d = await r.json();
    document.getElementById('nst').textContent = 'ALL SYSTEMS OPERATIONAL';
    ac(document.getElementById('s1'), d.activeNodes || 142);
    ac(document.getElementById('s2'), d.dataTB || 4700, ' TB');
    ac(document.getElementById('s3'), d.totalUsers || 2100000);
    try {
      const rv = await fetch(API + '/api/airbank/revenue');
      const rd = await rv.json();
      const tot = parseFloat(rd.summary &amp;amp;&amp;amp; rd.summary.totalUSD ? rd.summary.totalUSD : '3.47');
      document.getElementById('s4').textContent = '$' + tot.toFixed(2);
      const rs = document.getElementById('rev-snap');
      if (rs) rs.innerHTML =
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD Earned: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? parseFloat(rd.summary.totalUSD).toFixed(4) : '3.4700') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;In Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;' + (rd.summary ? rd.summary.totalBTC : '0.00005378') + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Projected Monthly: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (rd.summary ? rd.summary.projectedMonthlyUSD : '104.10') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Revenue Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(rd.summary ? rd.summary.totalEvents : 200) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
    } catch(e) { setDemoRevSnap(); }
  } catch(e) {
    document.getElementById('nst').textContent = 'DEMO MODE';
    ac(document.getElementById('s1'), 142);
    ac(document.getElementById('s2'), 4700, ' TB');
    ac(document.getElementById('s3'), 2100000);
    document.getElementById('s4').textContent = '$3.47';
    setDemoRevSnap();
  }
}

function setDemoRevSnap() {
  const rs = document.getElementById('rev-snap');
  if (!rs) return;
  rs.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Total USD: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$3.47 (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Bitcoin: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;0.00005378 BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Monthly Proj: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$104.10&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;Events: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;200 logged&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
}

/* ===== LOAD NODES ===== */
const DEMO_NODES = [
  {id:'NODE-US-NYC01',city:'New York',   country:'US',operator:'AT&amp;amp;T Surplus',     capacity_gbps:2100,load_percent:52},
  {id:'NODE-GB-LDN01',city:'London',    country:'GB',operator:'BT Wholesale',      capacity_gbps:3400,load_percent:45},
  {id:'NODE-KE-NBI01',city:'Nairobi',   country:'KE',operator:'Safaricom',         capacity_gbps:880, load_percent:78},
  {id:'NODE-AE-DXB01',city:'Dubai',     country:'AE',operator:'Etisalat',          capacity_gbps:1200,load_percent:60},
  {id:'NODE-ID-JKT01',city:'Jakarta',   country:'ID',operator:'Telkom ID',         capacity_gbps:340, load_percent:88},
  {id:'NODE-JP-TYO01',city:'Tokyo',     country:'JP',operator:'NTT Com',           capacity_gbps:2900,load_percent:41},
  {id:'NODE-AU-SYD01',city:'Sydney',    country:'AU',operator:'Telstra',           capacity_gbps:760, load_percent:33},
  {id:'NODE-BR-GRU01',city:'Sao Paulo', country:'BR',operator:'Vivo',              capacity_gbps:640, load_percent:55},
  {id:'NODE-ZA-CPT01',city:'Cape Town', country:'ZA',operator:'MTN SA',            capacity_gbps:420, load_percent:29},
  {id:'NODE-IN-BOM01',city:'Mumbai',    country:'IN',operator:'Airtel',            capacity_gbps:980, load_percent:71},
  {id:'NODE-SG-SIN01',city:'Singapore', country:'SG',operator:'Singtel',           capacity_gbps:1800,load_percent:66},
  {id:'NODE-NG-LOS01',city:'Lagos',     country:'NG',operator:'MTN Nigeria',       capacity_gbps:320, load_percent:82},
  {id:'NODE-DE-BER01',city:'Berlin',    country:'DE',operator:'Deutsche Telekom',  capacity_gbps:1600,load_percent:38},
  {id:'NODE-CN-SHA01',city:'Shanghai',  country:'CN',operator:'China Telecom',     capacity_gbps:2200,load_percent:59},
  {id:'NODE-MX-MEX01',city:'Mexico City',country:'MX',operator:'Telmex',          capacity_gbps:560, load_percent:44}
];

async function loadNodes() {
  try {
    const r = await fetch(API + '/api/nodes');
    const d = await r.json();
    renderNodes(d.nodes || DEMO_NODES);
  } catch(e) { renderNodes(DEMO_NODES); }
}

function renderNodes(nodes) {
  const tb = document.getElementById('nodes-body');
  if (!tb) return;
  tb.innerHTML = nodes.map(function(n) {
    const hi = n.load_percent &amp;gt; 80;
    const bar = '&amp;lt;span style="display:inline-block;width:50px;height:4px;background:rgba(0,255,224,.08);border-radius:2px;vertical-align:middle;margin-left:4px;overflow:hidden"&amp;gt;&amp;lt;span style="display:block;height:100%;width:' + n.load_percent + '%;background:' + (hi ? '#ff4d6d' : '#00ffe0') + ';border-radius:2px"&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;';
    return '&amp;lt;tr&amp;gt;&amp;lt;td style="color:var(--ac);font-size:.58rem"&amp;gt;' + n.id + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.city + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.country + '&amp;lt;/td&amp;gt;&amp;lt;td style="color:var(--mt)"&amp;gt;' + n.operator + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + fmt(n.capacity_gbps) + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;' + n.load_percent + '% ' + bar + '&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;span class="bdg bg3"&amp;gt;ONLINE&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;';
  }).join('');
}

/* ===== REGISTER ===== */
async function doRegister() {
  const id = document.getElementById('ri').value.trim();
  const type = document.getElementById('rtype').value;
  const btn = document.getElementById('rbtn');
  const res = document.getElementById('rres');
  if (!id) { alert('Please enter your phone or email.'); return; }
  btn.textContent = 'ACTIVATING...'; btn.disabled = true;
  try {
    const r = await fetch(API + '/api/register', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ identifier: id, type: type })
    });
    const d = await r.json();
    if (d.success) {
      res.className = 'rb show';
      res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;USER ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.userId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem;word-break:break-all"&amp;gt;' + d.token + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (d.assignedNode ? d.assignedNode.city + ', ' + d.assignedNode.country + ' (' + d.assignedNode.latencyMs + 'ms)' : 'London, GB') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;DAILY DATA: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + fmt(d.dailyDataMB || 10240) + ' MB&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
        '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
      document.getElementById('rtt').value = d.token;
      document.getElementById('puid').value = d.userId;
    } else {
      res.className = 'rb show';
      res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';
    }
  } catch(e) {
    var tk = uid();
    res.className = 'rb show';
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span class="rv g"&amp;gt;&amp;amp;#10003; FREE ACCESS ACTIVATED (Demo Mode)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IDENTIFIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + id + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TOKEN: &amp;lt;/span&amp;gt;&amp;lt;span class="rv" style="font-size:.58rem"&amp;gt;' + tk + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;NODE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;London, GB &amp;amp;mdash; 34ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TIER: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE &amp;amp;mdash; FOREVER&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(58,85,112,.7)"&amp;gt;Deploy the backend to process real registrations.&amp;lt;/div&amp;gt;';
    document.getElementById('rtt').value = tk;
    document.getElementById('puid').value = 'guest-' + uid().slice(0,8);
  }
  btn.textContent = 'ACTIVATE FREE ACCESS &amp;amp;rarr;'; btn.disabled = false;
}

/* ===== ROUTE ===== */
async function doRoute() {
  const token   = document.getElementById('rtt').value.trim();
  const dest    = document.getElementById('rtd').value.trim() || 'google.com';
  const service = document.getElementById('rts').value;
  const res     = document.getElementById('rres2');
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Computing optimal mesh path...&amp;lt;/span&amp;gt;';
  try {
    const r = await fetch(API + '/api/route', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token: token, destination: dest, service: service })
    });
    const d = await r.json();
    renderRoute(d);
  } catch(e) {
    renderRoute({
      path: [
        { city:'Your Device', country:'--' },
        { city:'London', country:'GB', load:45 },
        { city:'Dubai', country:'AE', load:60 },
        { city: dest, country:'&amp;amp;#127760;' }
      ],
      hops: 3, estimatedLatencyMs: 112, totalDistanceKm: 9420,
      compressed: true, encrypted: true, protocol: 'AIRTIMES/3.2', free: true
    });
  }
}

function renderRoute(d) {
  const res = document.getElementById('rres2');
  const hops = d.path || [];
  const pathHtml = hops.map(function(h, i) {
    return '&amp;lt;div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:.67rem"&amp;gt;' +
      '&amp;lt;span style="color:var(--ac);font-size:.58rem"&amp;gt;' + String(i).padStart(2,'0') + '&amp;lt;/span&amp;gt;' +
      '&amp;lt;span style="color:var(--ac)"&amp;gt;&amp;amp;rarr;&amp;lt;/span&amp;gt;' +
      '&amp;lt;span style="color:var(--tx)"&amp;gt;' + h.city + (h.country &amp;amp;&amp;amp; h.country !== '--' ? ' (' + h.country + ')' : '') + '&amp;lt;/span&amp;gt;' +
      (h.load !== undefined ? '&amp;lt;span style="color:var(--mt);font-size:.56rem"&amp;gt;[' + h.load + '% load]&amp;lt;/span&amp;gt;' : '') +
      '&amp;lt;/div&amp;gt;';
  }).join('');
  res.innerHTML = pathHtml +
    '&amp;lt;div style="margin-top:14px;border-top:1px solid rgba(0,255,224,.07);padding-top:14px"&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;HOPS&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.hops||3) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;LATENCY&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.estimatedLatencyMs||112) + ' ms&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;DISTANCE&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + fmt(d.totalDistanceKm||9420) + ' km&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;PROTOCOL&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.protocol||'AIRTIMES/3.2') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COMPRESSED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.compressed?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem;border-bottom:1px solid rgba(255,255,255,.03)"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;ENCRYPTED&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--ac)"&amp;gt;' + (d.encrypted?'YES':'NO') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;justify-content:space-between;padding:6px 0;font-size:.66rem"&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;COST&amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd);font-weight:700"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;/div&amp;gt;';
}

/* ===== API TESTS ===== */
async function doWeather() {
  const city = document.getElementById('wc').value.trim() || 'Lagos';
  const res  = document.getElementById('apires');
  res.innerHTML = '&amp;lt;span style="color:var(--mt)"&amp;gt;Fetching weather...&amp;lt;/span&amp;gt;'; res.className = 'rb show';
  try {
    const r = await fetch(API + '/api/airapi/weather?city=' + encodeURIComponent(city));
    const d = await r.json();
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.temp_c + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.condition + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.humidity + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;WIND: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + d.data.wind_kmh + ' km/h&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  } catch(e) {
    var temps = {Lagos:31,Nairobi:22,Jakarta:29,London:12,Tokyo:18,Sydney:24,Dubai:38,Accra:30,Abuja:29};
    var t = temps[city] || Math.round(15 + Math.random() * 20);
    var conds = ['Clear','Sunny','Cloudy','Partly Cloudy'];
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + city + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TEMP: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + t + '&amp;amp;deg;C&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;CONDITION: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + conds[Math.floor(Math.random()*conds.length)] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;HUMIDITY: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + Math.round(40+Math.random()*50) + '%&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  }
}

async function doExchange() {
  var from = document.getElementById('ef').value.trim() || 'USD';
  var to   = document.getElementById('et').value.trim() || 'NGN';
  var res  = document.getElementById('apires');
  try {
    const r = await fetch(API + '/api/airapi/exchange?from=' + from + '&amp;amp;to=' + to);
    const d = await r.json();
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + d.rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  } catch(e) {
    var rates = {NGN:1580,KES:130,GHS:15,ZAR:18,EUR:0.92,GBP:0.79,JPY:149,BTC:0.0000155,ETH:0.000290};
    var rate = rates[to.toUpperCase()] ? rates[to.toUpperCase()] : (0.5+Math.random()*100).toFixed(4);
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;RATE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;1 ' + from + ' = ' + rate + ' ' + to + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COST: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;FREE (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  }
  res.className = 'rb show';
}

/* ===== AIRPAY RENDER ===== */
function renderAirPay() {
  // Render wallet grid
  var wg = document.getElementById('wallets-grid');
  if (wg) {
    var coins = ['BTC','ETH','USDT','BNB','LTC'];
    wg.innerHTML = coins.map(function(c) {
      var addr = WALLETS[c] || '(Not set &amp;amp;mdash; enter in Owner Dashboard)';
      return '&amp;lt;div class="wc" onclick="selWallet(\'' + c + '\')" id="wcard-' + c + '"&amp;gt;' +
        '&amp;lt;div class="wi"&amp;gt;' + COIN_ICONS[c] + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="wco"&amp;gt;' + c + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="wna"&amp;gt;' + COIN_NAMES[c] + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="wad"&amp;gt;' + addr + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;/div&amp;gt;';
    }).join('');
  }
  // Render bundle grid
  var bg = document.getElementById('bundles-grid');
  if (bg) {
    bg.innerHTML = BUNDLES.map(function(b) {
      return '&amp;lt;div class="bc" onclick="selBundle2(\'' + b.id + '\')" id="bc-' + b.id + '"&amp;gt;' +
        '&amp;lt;div class="bn"&amp;gt;' + b.name + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="bdata"&amp;gt;' + (b.gb &amp;gt;= 9999 ? '&amp;amp;#8734;' : b.gb + ' GB') + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="bprice"&amp;gt;$' + b.usd + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;div class="bbtc"&amp;gt;&amp;amp;#8383; ' + b.btc + '&amp;lt;/div&amp;gt;' +
        '&amp;lt;/div&amp;gt;';
    }).join('');
  }
  // Pre-fill from localStorage
  ['BTC','ETH','USDT','BNB','LTC'].forEach(function(c) {
    var el = document.getElementById('wbtc') || null;
  });
}

function selWallet(coin) {
  document.querySelectorAll('.wc').forEach(function(c) { c.classList.remove('sel'); });
  var el = document.getElementById('wcard-' + coin);
  if (el) el.classList.add('sel');
  var pc = document.getElementById('pcoin');
  if (pc) pc.value = coin;
}

function selBundle2(id) {
  document.querySelectorAll('.bc').forEach(function(c) { c.classList.remove('sel'); });
  var el = document.getElementById('bc-' + id);
  if (el) el.classList.add('sel');
  selBundle = id;
}

async function doPayment() {
  var bundleId = selBundle;
  var coin     = document.getElementById('pcoin').value;
  var userId   = document.getElementById('puid').value.trim();
  var res      = document.getElementById('pres');
  var btn      = document.getElementById('pbtn');
  if (!bundleId) {
    res.className = 'rb gb show';
    res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please select a data bundle above first.&amp;lt;/span&amp;gt;';
    return;
  }
  btn.textContent = 'GENERATING...'; btn.disabled = true;
  var bundle  = BUNDLES.find(function(b) { return b.id === bundleId; });
  var rate    = COIN_RATES[coin] || 0.0000155;
  var amount  = (bundle.usd * rate).toFixed(8);
  var payId   = 'PAY-' + Math.random().toString(36).substr(2,12).toUpperCase();
  var walletAddr = WALLETS[coin] || 'SET YOUR ' + coin + ' WALLET IN OWNER DASHBOARD';
  try {
    const r = await fetch(API + '/api/airpay/pay', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId: userId, bundleId: bundleId, coin: coin })
    });
    const d = await r.json();
    if (d.success &amp;amp;&amp;amp; d.payment) showPayResult(d.payment);
    else throw new Error(d.error || 'API unavailable');
  } catch(e) {
    showPayResult({ paymentId:payId, bundle:bundle, coin:coin, amount:amount,
      walletAddress:walletAddr, amountUSD:bundle.usd,
      network: {BTC:'Bitcoin Network',ETH:'Ethereum ERC-20',USDT:'Tron TRC-20',BNB:'BNB Smart Chain',LTC:'Litecoin'}[coin] || coin });
  }
  btn.textContent = 'GENERATE PAYMENT ADDRESS &amp;amp;rarr;'; btn.disabled = false;
}

function showPayResult(p) {
  var res = document.getElementById('pres');
  res.className = 'rb gb show';
  var icon = COIN_ICONS[p.coin] || '&amp;amp;#8383;';
  res.innerHTML =
    '&amp;lt;div style="text-align:center;margin-bottom:16px"&amp;gt;' +
    '&amp;lt;div style="font-size:2.5rem"&amp;gt;' + icon + '&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="font-family:\'Bebas Neue\';font-size:1.8rem;color:var(--gd)"&amp;gt;SEND ' + p.amount + ' ' + p.coin + '&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="font-size:.62rem;color:var(--mt)"&amp;gt;' + (p.network || p.coin) + ' &amp;amp;middot; $' + p.amountUSD + ' USD&amp;lt;/div&amp;gt;' +
    '&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="font-size:.6rem;color:var(--mt);margin-bottom:6px;letter-spacing:.1em;text-transform:uppercase"&amp;gt;Send to this wallet address:&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="background:rgba(0,0,0,.5);border:1px solid rgba(245,197,24,.2);padding:12px;margin-bottom:12px;word-break:break-all;font-size:.62rem;color:var(--gd);letter-spacing:.05em;font-family:\'Space Mono\'"&amp;gt;' + p.walletAddress + '&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="display:flex;gap:8px;margin-bottom:14px"&amp;gt;' +
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.walletAddress.replace(/'/g,"\\'") + '\')"&amp;gt;COPY ADDRESS&amp;lt;/button&amp;gt;' +
    '&amp;lt;button class="copy-btn" onclick="navigator.clipboard.writeText(\'' + p.paymentId + '\')"&amp;gt;COPY PAY ID&amp;lt;/button&amp;gt;' +
    '&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.paymentId + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;BUNDLE: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + (p.bundle ? p.bundle.name + ' &amp;amp;mdash; ' + (p.bundle.gb &amp;gt;= 9999 ? 'Unlimited' : p.bundle.gb + ' GB') : '') + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;AMOUNT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + p.amount + ' ' + p.coin + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Save your Payment ID, then use it in the Confirm section below after sending.&amp;lt;/div&amp;gt;';
  var cpid = document.getElementById('cpid');
  if (cpid) cpid.value = p.paymentId;
}

async function doConfirm() {
  var pid  = document.getElementById('cpid').value.trim();
  var txh  = document.getElementById('ctxh').value.trim();
  var res  = document.getElementById('cres');
  if (!pid) { res.className = 'rb gb show'; res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Please enter your Payment ID.&amp;lt;/span&amp;gt;'; return; }
  try {
    const r = await fetch(API + '/api/airpay/confirm', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ paymentId: pid, txHash: txh })
    });
    const d = await r.json();
    res.className = 'rb gb show';
    if (d.success) res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED &amp;amp;mdash; BUNDLE ACTIVE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
    else res.innerHTML = '&amp;lt;span style="color:var(--ac3)"&amp;gt;Error: ' + d.error + '&amp;lt;/span&amp;gt;';
  } catch(e) {
    res.className = 'rb gb show';
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;PAYMENT ID: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + pid + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; CONFIRMED (Demo)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div style="margin-top:8px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Deploy backend to process real confirmations and activate bundles automatically.&amp;lt;/div&amp;gt;';
  }
}

/* ===== AIRBANK ===== */
async function loadVault() {
  try {
    const r = await fetch(API + '/api/airbank/vault');
    const d = await r.json();
    var storedMB = parseFloat(d.vault &amp;amp;&amp;amp; d.vault.storedMB ? d.vault.storedMB : 842000);
    document.getElementById('vstore').textContent = fmt(Math.round(storedMB)) + ' AU';
    document.getElementById('vtrans').textContent = parseFloat(d.transmissions &amp;amp;&amp;amp; d.transmissions.totalGB ? d.transmissions.totalGB : 4700).toFixed(1) + ' GB';
    document.getElementById('vrev').textContent = '$' + parseFloat(d.revenue &amp;amp;&amp;amp; d.revenue.totalUSD ? d.revenue.totalUSD : 3.47).toFixed(4);
  } catch(e) {
    document.getElementById('vstore').textContent = '842,000 AU';
    document.getElementById('vtrans').textContent = '4,700 GB';
    document.getElementById('vrev').textContent = '$3.4700';
  }
}

function calcMoney() {
  var amt  = parseFloat(document.getElementById('camt').value) || 0;
  var unit = document.getElementById('cunit').value || 'TB';
  var inGB = unit === 'PB' ? amt * 1024 * 1024 : unit === 'TB' ? amt * 1024 : amt;
  var storeMo  = inGB * 0.0005;
  var transUSD = inGB * 0.002;
  var monthly  = (storeMo + transUSD).toFixed(4);
  var yearly   = ((storeMo + transUSD) * 12).toFixed(2);
  var btcM     = ((storeMo + transUSD) * 0.0000155).toFixed(8);
  var disp = document.getElementById('cdisplay');
  if (disp) disp.innerHTML = '&amp;lt;div&amp;gt;$' + monthly + '/mo&amp;lt;/div&amp;gt;&amp;lt;div class="cs"&amp;gt;$' + yearly + '/yr &amp;amp;middot; ' + btcM + ' BTC/mo&amp;lt;/div&amp;gt;';
  var res  = document.getElementById('cres2');
  if (res) {
    res.className = 'rb gb show';
    res.innerHTML =
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;INPUT: &amp;lt;/span&amp;gt;&amp;lt;span class="rv"&amp;gt;' + amt + ' ' + unit + ' (' + fmt(Math.round(inGB)) + ' GB)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STORAGE REV/MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + storeMo.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;TRANSMISSION REV: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + transUSD.toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + monthly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;COMBINED / YEAR: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;$' + yearly + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;IN BITCOIN / MONTH: &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + btcM + ' BTC&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  }
}

/* ===== OWNER DASHBOARD ===== */
async function loadOwner() {
  // Pre-fill wallets from localStorage
  var wmap = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};
  Object.keys(wmap).forEach(function(k) {
    var el = document.getElementById(k);
    if (el &amp;amp;&amp;amp; WALLETS[wmap[k]]) el.value = WALLETS[wmap[k]];
  });
  try {
    const [revR, nodesR, vaultR] = await Promise.all([
      fetch(API + '/api/airbank/revenue'),
      fetch(API + '/api/nodes'),
      fetch(API + '/api/airbank/vault')
    ]);
    const rev   = await revR.json();
    const nodes = await nodesR.json();
    const vault = await vaultR.json();
    document.getElementById('ort').textContent  = '$' + parseFloat(rev.summary &amp;amp;&amp;amp; rev.summary.totalUSD ? rev.summary.totalUSD : '3.47').toFixed(4);
    document.getElementById('ortb').textContent = (rev.summary ? rev.summary.totalBTC : '0.00005378') + ' BTC';
    document.getElementById('orm').textContent  = '$' + (rev.summary ? rev.summary.projectedMonthlyUSD : '104.10');
    document.getElementById('ore').textContent  = fmt(rev.summary ? rev.summary.totalEvents : 200);
    document.getElementById('cn').textContent   = nodes.count || 142;
    document.getElementById('cu').textContent   = fmt(2100000);
    document.getElementById('cdg').textContent  = parseFloat(vault.transmissions &amp;amp;&amp;amp; vault.transmissions.totalGB ? vault.transmissions.totalGB : 4700).toFixed(0) + ' GB';
    var src = document.getElementById('rev-src');
    if (src &amp;amp;&amp;amp; rev.bySource &amp;amp;&amp;amp; rev.bySource.length) {
      src.innerHTML = rev.bySource.map(function(s) {
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + s.source + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(s.usd).toFixed(4) + '&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(' + s.count + ' events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
      }).join('');
    } else { setDemoRevSrc(); }
    var day = document.getElementById('rev-day');
    if (day &amp;amp;&amp;amp; rev.byDay &amp;amp;&amp;amp; rev.byDay.length) {
      day.innerHTML = rev.byDay.slice(0,7).map(function(d) {
        return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d.day + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + parseFloat(d.usd).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
      }).join('');
    } else { setDemoRevDay(); }
  } catch(e) {
    document.getElementById('ort').textContent  = '$3.4700';
    document.getElementById('ortb').textContent = '0.00005378 BTC';
    document.getElementById('orm').textContent  = '$104.10';
    document.getElementById('ore').textContent  = '200';
    document.getElementById('cn').textContent   = '142';
    document.getElementById('cu').textContent   = '2,100,000';
    document.getElementById('cdg').textContent  = '4,700 GB';
    setDemoRevSrc(); setDemoRevDay();
  }
}

function setDemoRevSrc() {
  var src = document.getElementById('rev-src');
  if (src) src.innerHTML =
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;data_transmission: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$1.4500&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(100 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;active_user: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.8400&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(84 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;airpay: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.6200&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(8 payments)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;api_call: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.3100&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(62 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;storage: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.1800&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(36 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
    '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;sms: &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$0.0700&amp;lt;/span&amp;gt; &amp;lt;span style="color:var(--mt)"&amp;gt;(70 events)&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
}

function setDemoRevDay() {
  var day = document.getElementById('rev-day');
  var days = ['2025-12-15','2025-12-14','2025-12-13','2025-12-12','2025-12-11','2025-12-10','2025-12-09'];
  if (day) day.innerHTML = days.map(function(d) {
    return '&amp;lt;div&amp;gt;&amp;lt;span style="color:var(--mt)"&amp;gt;' + d + ': &amp;lt;/span&amp;gt;&amp;lt;span style="color:var(--gd)"&amp;gt;$' + (0.08 + Math.random() * 0.4).toFixed(4) + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  }).join('');
}

function saveWallets() {
  var map = {wbtc:'BTC', weth:'ETH', wusdt:'USDT', wbnb:'BNB', wltc:'LTC'};
  var saved = [];
  Object.keys(map).forEach(function(k) {
    var el = document.getElementById(k);
    if (el &amp;amp;&amp;amp; el.value.trim()) {
      WALLETS[map[k]] = el.value.trim();
      localStorage.setItem('w_' + map[k], el.value.trim());
      saved.push(map[k]);
    }
  });
  var res = document.getElementById('wsres');
  if (res) {
    res.className = 'rb gb show';
    res.innerHTML = '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;STATUS: &amp;lt;/span&amp;gt;&amp;lt;span style="color:#4ade80"&amp;gt;&amp;amp;#10003; WALLET ADDRESSES SAVED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;' +
      saved.map(function(c) { return '&amp;lt;div&amp;gt;&amp;lt;span class="rk"&amp;gt;' + c + ': &amp;lt;/span&amp;gt;&amp;lt;span class="rv gd"&amp;gt;' + WALLETS[c] + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;'; }).join('') +
      '&amp;lt;div style="margin-top:10px;font-size:.6rem;color:rgba(245,197,24,.5)"&amp;gt;Addresses saved locally and displayed on AirPay page. For production backend, set as OWNER_BTC_WALLET etc. environment variables.&amp;lt;/div&amp;gt;';
  }
  renderAirPay();
}

/* ===== LIVE LOG FEED ===== */
var LOG_EVENTS = [
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Nairobi Node&amp;lt;/strong&amp;gt; &amp;amp;mdash; 12,440 users allocated free data bundle'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; $0.0024 revenue logged automatically'},
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirAPI Gateway&amp;lt;/strong&amp;gt; &amp;amp;mdash; 50K requests served to Lagos cluster'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; New payment request: 0.000038 BTC pending'},
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Jakarta Relay&amp;lt;/strong&amp;gt; &amp;amp;mdash; bandwidth auto-scaled to 340 Gbps'},
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirVault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 80,000 files synced across 8 geo-regions'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank Vault&amp;lt;/strong&amp;gt; &amp;amp;mdash; 842,000 AirUnits stored, earning $0.421/mo'},
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Mumbai Cluster&amp;lt;/strong&amp;gt; &amp;amp;mdash; 4 new nodes joined the free mesh grid'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; ETH bundle confirmed: Pro 500 GB activated'},
  {c:'lg',  m:'&amp;lt;strong&amp;gt;Tokyo Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; +12 Gbps from commercial surplus pooled'},
  {c:'lb',  m:'&amp;lt;strong&amp;gt;AirSMS&amp;lt;/strong&amp;gt; &amp;amp;mdash; 230,000 encrypted messages routed free'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirBank&amp;lt;/strong&amp;gt; &amp;amp;mdash; Active user revenue: $0.001 x 2,100,000 users'},
  {c:'lg',  m:'&amp;lt;strong&amp;gt;São Paulo&amp;lt;/strong&amp;gt; &amp;amp;mdash; failover triggered, rerouting via satellite'},
  {c:'lb',  m:'&amp;lt;strong&amp;gt;London Hub&amp;lt;/strong&amp;gt; &amp;amp;mdash; 99.99% uptime milestone reached this quarter'},
  {c:'lgd', m:'&amp;lt;strong&amp;gt;AirPay&amp;lt;/strong&amp;gt; &amp;amp;mdash; USDT payment received: Standard 100 GB bundle'}
];

var logIdx = 0;

function addLogEntry(msg, cls, feed) {
  if (!feed) return;
  var el = document.createElement('div');
  el.className = 'le';
  var ts = new Date().toLocaleTimeString();
  el.innerHTML = '&amp;lt;div class="ld ' + cls + '"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div class="lt"&amp;gt;' + msg + '&amp;lt;span class="ltm"&amp;gt;just now &amp;amp;middot; ' + ts + '&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;';
  feed.insertBefore(el, feed.children[1] || null);
  while (feed.querySelectorAll('.le').length &amp;gt; 8) {
    var last = feed.querySelector('.le:last-child');
    if (last) last.remove();
  }
}

function initLogs() {
  var feed = document.getElementById('log-home');
  if (feed) {
    LOG_EVENTS.slice(0, 6).forEach(function(e, i) {
      setTimeout(function() { addLogEntry(e.m, e.c, feed); }, i * 120);
    });
    logIdx = 6;
  }
}

function startLiveLog() {
  try {
    var es = new EventSource(API + '/api/events');
    es.onmessage = function(ev) {
      var d = JSON.parse(ev.data);
      var cls = d.type === 'revenue' ? 'lgd' : d.type === 'warning' ? 'lr' : d.type === 'info' ? 'lb' : 'lg';
      var feed = document.getElementById('log-home');
      if (feed) addLogEntry('&amp;lt;strong&amp;gt;LIVE&amp;lt;/strong&amp;gt; &amp;amp;mdash; ' + d.message, cls, feed);
    };
    es.onerror = function() { es.close(); startFallbackLog(); };
  } catch(e) { startFallbackLog(); }
}

function startFallbackLog() {
  setInterval(function() {
    var e = LOG_EVENTS[logIdx % LOG_EVENTS.length]; logIdx++;
    var feed = document.getElementById('log-home');
    if (feed) addLogEntry(e.m, e.c, feed);
  }, 3500);
}

/* ===== INIT ===== */
window.addEventListener('load', function() {
  loadStats();
  initLogs();
  startLiveLog();
});




</description>
    </item>
    <item>
      <title>MODAC GLOBAL AIRTIMES</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Mon, 01 Jun 2026 12:46:44 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/modac-global-airtimes-3p0n</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/modac-global-airtimes-3p0n</guid>
      <description>&lt;h1&gt;
  
  
  !/bin/bash
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ============================================================
&lt;/h1&gt;

&lt;h1&gt;
  
  
  MODAC GLOBAL SYSTEM - COMPLETE INTEGRATED DEPLOYMENT
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Includes: Transaction Blocking, Agent Verification, Wallet System
&lt;/h1&gt;

&lt;h1&gt;
  
  
  One script deploys everything
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ============================================================
&lt;/h1&gt;

&lt;p&gt;set -e&lt;/p&gt;

&lt;p&gt;echo "╔══════════════════════════════════════════════════════════════════════╗"&lt;br&gt;
echo "║                                                                      ║"&lt;br&gt;
echo "║              MODAC GLOBAL SYSTEM - FULL DEPLOYMENT                   ║"&lt;br&gt;
echo "║       Complete Security Framework with Transaction Blocking          ║"&lt;br&gt;
echo "║                                                                      ║"&lt;br&gt;
echo "╚══════════════════════════════════════════════════════════════════════╝"&lt;br&gt;
echo ""&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 1: DIRECTORY STRUCTURE ====================
&lt;/h1&gt;

&lt;p&gt;echo "Phase 1/7: Creating Complete Directory Structure..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;p&gt;mkdir -p backend/{security_beacon,payment_system,shared,config,api,middleware}&lt;br&gt;
mkdir -p frontend/{src,public,dist}&lt;br&gt;
mkdir -p config/{agent,wallet,autopilot,security}&lt;br&gt;
mkdir -p data/{registry,intercepted,blocked,approved}&lt;br&gt;
mkdir -p logs/{api,security,payments,transactions}&lt;br&gt;
mkdir -p scripts/{setup,maintenance,backup}&lt;br&gt;
mkdir -p monitoring/{prometheus,grafana}&lt;br&gt;
mkdir -p nginx&lt;br&gt;
mkdir -p database&lt;/p&gt;

&lt;p&gt;echo "✓ Directory structure created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 2: CORE SECURITY FILES ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 2/7: Creating Core Security System..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create main security module
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; backend/security_beacon/transaction_blocker.py &amp;lt;&amp;lt; 'EOFPYTHON'&lt;br&gt;
"""&lt;br&gt;
MODAC Security Beacon - Transaction Blocker&lt;br&gt;
Blocks ALL transactions until agents are verified&lt;br&gt;
"""&lt;br&gt;
from enum import Enum&lt;br&gt;
from datetime import datetime&lt;br&gt;
from typing import Dict, Tuple, Optional&lt;br&gt;
import json&lt;br&gt;
import uuid&lt;/p&gt;

&lt;p&gt;class AgentStatus(Enum):&lt;br&gt;
    UNREGISTERED = "unregistered"&lt;br&gt;
    PENDING = "pending"&lt;br&gt;
    VERIFIED = "verified"&lt;br&gt;
    BLOCKED = "blocked"&lt;/p&gt;

&lt;p&gt;class TransactionBlocker:&lt;br&gt;
    """Blocks all unverified transactions"""&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def __init__(self):
    self.intercepted = []
    self.blocked = []
    self.approved = []

def check_transaction(self, buyer_id: str, seller_id: str) -&amp;gt; Tuple[bool, str]:
    """Check if transaction should be allowed"""
    # Load registry
    try:
        with open('data/registry/agents.json', 'r') as f:
            registry = json.load(f)
    except:
        registry = {}

    # Check buyer
    buyer = registry.get(buyer_id, {})
    if not buyer or buyer.get('status') != 'verified':
        return False, f"Buyer agent not verified"

    # Check seller
    seller = registry.get(seller_id, {})
    if not seller or seller.get('status') != 'verified':
        return False, f"Seller agent not verified"

    return True, "Both agents verified"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;blocker = TransactionBlocker()&lt;br&gt;
EOFPYTHON&lt;/p&gt;

&lt;p&gt;echo "✓ Transaction blocker created"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create agent registry
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; backend/security_beacon/agent_registry.py &amp;lt;&amp;lt; 'EOFPYTHON'&lt;br&gt;
"""&lt;br&gt;
MODAC Agent Registry&lt;br&gt;
Central registry of all agents&lt;br&gt;
"""&lt;br&gt;
import json&lt;br&gt;
import os&lt;br&gt;
from datetime import datetime&lt;/p&gt;

&lt;p&gt;class AgentRegistry:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self):&lt;br&gt;
        self.registry_file = 'data/registry/agents.json'&lt;br&gt;
        os.makedirs(os.path.dirname(self.registry_file), exist_ok=True)&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def load_registry(self):
    try:
        with open(self.registry_file, 'r') as f:
            return json.load(f)
    except:
        return {}

def save_registry(self, registry):
    with open(self.registry_file, 'w') as f:
        json.dump(registry, f, indent=2, default=str)

def register_agent(self, agent_data):
    registry = self.load_registry()
    agent_id = agent_data['agent_id']
    agent_data['status'] = 'pending'
    agent_data['registered_at'] = datetime.now().isoformat()
    registry[agent_id] = agent_data
    self.save_registry(registry)
    return agent_id

def verify_agent(self, agent_id):
    registry = self.load_registry()
    if agent_id in registry:
        registry[agent_id]['status'] = 'verified'
        registry[agent_id]['verified_at'] = datetime.now().isoformat()
        self.save_registry(registry)
        return True
    return False
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;registry = AgentRegistry()&lt;br&gt;
EOFPYTHON&lt;/p&gt;

&lt;p&gt;echo "✓ Agent registry created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 3: API WITH BLOCKING ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 3/7: Creating API with Transaction Blocking..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create main API
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; backend/main.py &amp;lt;&amp;lt; 'EOFPYTHON'&lt;br&gt;
"""&lt;br&gt;
MODAC API with Transaction Blocking&lt;br&gt;
Every transaction goes through verification&lt;br&gt;
"""&lt;br&gt;
from fastapi import FastAPI, HTTPException&lt;br&gt;
from fastapi.middleware.cors import CORSMiddleware&lt;br&gt;
from pydantic import BaseModel&lt;br&gt;
from typing import Optional&lt;br&gt;
import sys&lt;br&gt;
import os&lt;/p&gt;

&lt;h1&gt;
  
  
  Add security beacon to path
&lt;/h1&gt;

&lt;p&gt;sys.path.append(os.path.join(os.path.dirname(&lt;strong&gt;file&lt;/strong&gt;), 'security_beacon'))&lt;/p&gt;

&lt;p&gt;from transaction_blocker import blocker&lt;br&gt;
from agent_registry import registry&lt;/p&gt;

&lt;p&gt;app = FastAPI(title="MODAC Global System", version="1.0.0")&lt;/p&gt;

&lt;p&gt;app.add_middleware(&lt;br&gt;
    CORSMiddleware,&lt;br&gt;
    allow_origins=["&lt;em&gt;"],&lt;br&gt;
    allow_credentials=True,&lt;br&gt;
    allow_methods=["&lt;/em&gt;"],&lt;br&gt;
    allow_headers=["*"],&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;class AgentRegistration(BaseModel):&lt;br&gt;
    agent_name: str&lt;br&gt;
    owner_entity: str&lt;br&gt;
    agent_type: str&lt;br&gt;
    public_key: Optional[str] = None&lt;/p&gt;

&lt;p&gt;class TransactionRequest(BaseModel):&lt;br&gt;
    buyer_agent_id: str&lt;br&gt;
    seller_agent_id: str&lt;br&gt;
    amount: float&lt;br&gt;
    currency: str = "USD"&lt;/p&gt;

&lt;p&gt;@app.get("/")&lt;br&gt;
def root():&lt;br&gt;
    return {&lt;br&gt;
        "system": "MODAC Global System",&lt;br&gt;
        "version": "1.0.0",&lt;br&gt;
        "status": "operational",&lt;br&gt;
        "security": "transaction_blocking_enabled"&lt;br&gt;
    }&lt;/p&gt;

&lt;p&gt;@app.get("/health")&lt;br&gt;
def health():&lt;br&gt;
    return {"status": "healthy", "blocking_active": True}&lt;/p&gt;

&lt;p&gt;@app.post("/api/v1/agents/register")&lt;br&gt;
def register_agent(agent: AgentRegistration):&lt;br&gt;
    """Register new agent - starts in PENDING status"""&lt;br&gt;
    import uuid&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent_data = agent.dict()
agent_data['agent_id'] = str(uuid.uuid4())

agent_id = registry.register_agent(agent_data)

return {
    "success": True,
    "agent_id": agent_id,
    "status": "pending",
    "message": "Agent registered. Complete verification to transact.",
    "can_transact": False
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@app.post("/api/v1/agents/{agent_id}/verify")&lt;br&gt;
def verify_agent(agent_id: str):&lt;br&gt;
    """Verify agent - enables transactions"""&lt;br&gt;
    success = registry.verify_agent(agent_id)&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if success:
    return {
        "success": True,
        "agent_id": agent_id,
        "status": "verified",
        "message": "Agent verified. Can now transact.",
        "can_transact": True
    }
else:
    raise HTTPException(status_code=404, detail="Agent not found")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@app.post("/api/v1/transactions/execute")&lt;br&gt;
def execute_transaction(txn: TransactionRequest):&lt;br&gt;
    """&lt;br&gt;
    Execute transaction - BLOCKS if agents not verified&lt;br&gt;
    This is the core security enforcement&lt;br&gt;
    """&lt;br&gt;
    # CHECK: Are both agents verified?&lt;br&gt;
    allowed, reason = blocker.check_transaction(&lt;br&gt;
        txn.buyer_agent_id,&lt;br&gt;
        txn.seller_agent_id&lt;br&gt;
    )&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if not allowed:
    # BLOCK THE TRANSACTION
    return {
        "success": False,
        "status": "blocked",
        "reason": reason,
        "message": "Transaction blocked by MODAC security",
        "action_required": "Verify both agents before transacting"
    }

# APPROVED - Transaction can proceed
import uuid
transaction_id = str(uuid.uuid4())

return {
    "success": True,
    "status": "approved",
    "transaction_id": transaction_id,
    "amount": txn.amount,
    "currency": txn.currency,
    "message": "Transaction approved and processing"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@app.get("/api/v1/system/status")&lt;br&gt;
def system_status():&lt;br&gt;
    """Get system security status"""&lt;br&gt;
    agents = registry.load_registry()&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verified_count = sum(1 for a in agents.values() if a.get('status') == 'verified')
pending_count = sum(1 for a in agents.values() if a.get('status') == 'pending')

return {
    "system": "MODAC Global System",
    "security_mode": "strict",
    "transaction_blocking": "enabled",
    "agents": {
        "total": len(agents),
        "verified": verified_count,
        "pending": pending_count
    },
    "transactions": {
        "intercepted": len(blocker.intercepted),
        "blocked": len(blocker.blocked),
        "approved": len(blocker.approved)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;if &lt;strong&gt;name&lt;/strong&gt; == "&lt;strong&gt;main&lt;/strong&gt;":&lt;br&gt;
    import uvicorn&lt;br&gt;
    uvicorn.run(app, host="0.0.0.0", port=8000)&lt;br&gt;
EOFPYTHON&lt;/p&gt;

&lt;p&gt;echo "✓ API with blocking created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 4: REQUIREMENTS ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 4/7: Creating Requirements Files..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;p&gt;cat &amp;gt; backend/requirements.txt &amp;lt;&amp;lt; 'EOF'&lt;br&gt;
fastapi==0.104.1&lt;br&gt;
uvicorn[standard]==0.24.0&lt;br&gt;
pydantic==2.5.0&lt;br&gt;
python-multipart==0.0.6&lt;br&gt;
python-dotenv==1.0.0&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;echo "✓ Requirements created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 5: DOCKER CONFIGURATION ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 5/7: Creating Docker Configuration..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create Dockerfile
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; backend/Dockerfile &amp;lt;&amp;lt; 'EOF'&lt;br&gt;
FROM python:3.11-slim&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;/p&gt;

&lt;p&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y curl &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/p&gt;

&lt;p&gt;COPY requirements.txt .&lt;br&gt;
RUN pip install --no-cache-dir -r requirements.txt&lt;/p&gt;

&lt;p&gt;COPY . .&lt;/p&gt;

&lt;p&gt;RUN useradd -m -u 1000 modac &amp;amp;&amp;amp; chown -R modac:modac /app&lt;br&gt;
USER modac&lt;/p&gt;

&lt;p&gt;EXPOSE 8000&lt;/p&gt;

&lt;p&gt;CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]&lt;br&gt;
EOF&lt;/p&gt;

&lt;h1&gt;
  
  
  Create docker-compose
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; docker-compose.yml &amp;lt;&amp;lt; 'EOF'&lt;br&gt;
version: '3.8'&lt;/p&gt;

&lt;p&gt;services:&lt;br&gt;
  modac-api:&lt;br&gt;
    build:&lt;br&gt;
      context: ./backend&lt;br&gt;
      dockerfile: Dockerfile&lt;br&gt;
    container_name: modac-api&lt;br&gt;
    ports:&lt;br&gt;
      - "8000:8000"&lt;br&gt;
    volumes:&lt;br&gt;
      - ./data:/app/data&lt;br&gt;
      - ./logs:/app/logs&lt;br&gt;
      - ./config:/app/config&lt;br&gt;
    environment:&lt;br&gt;
      - MODAC_ENV=production&lt;br&gt;
      - BLOCKING_ENABLED=true&lt;br&gt;
    restart: unless-stopped&lt;br&gt;
    healthcheck:&lt;br&gt;
      test: ["CMD", "curl", "-f", "&lt;a href="http://localhost:8000/health%22" rel="noopener noreferrer"&gt;http://localhost:8000/health"&lt;/a&gt;]&lt;br&gt;
      interval: 30s&lt;br&gt;
      timeout: 10s&lt;br&gt;
      retries: 3&lt;/p&gt;

&lt;p&gt;modac-dashboard:&lt;br&gt;
    image: nginx:alpine&lt;br&gt;
    container_name: modac-dashboard&lt;br&gt;
    ports:&lt;br&gt;
      - "80:80"&lt;br&gt;
    volumes:&lt;br&gt;
      - ./frontend/dist:/usr/share/nginx/html:ro&lt;br&gt;
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro&lt;br&gt;
    depends_on:&lt;br&gt;
      - modac-api&lt;br&gt;
    restart: unless-stopped&lt;/p&gt;

&lt;p&gt;volumes:&lt;br&gt;
  data:&lt;br&gt;
  logs:&lt;br&gt;
  config:&lt;/p&gt;

&lt;p&gt;networks:&lt;br&gt;
  default:&lt;br&gt;
    name: modac-network&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;echo "✓ Docker configuration created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 6: FRONTEND DASHBOARD ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 6/7: Creating Security Dashboard..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;p&gt;cat &amp;gt; frontend/dist/index.html &amp;lt;&amp;lt; 'EOF'&lt;br&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;
    &lt;br&gt;
    &lt;br&gt;
    MODAC Security Dashboard
&lt;br&gt;
    &amp;lt;br&amp;gt;
        * { margin: 0; padding: 0; box-sizing: border-box; }&amp;lt;br&amp;gt;
        body {&amp;lt;br&amp;gt;
            font-family: &amp;amp;#39;Segoe UI&amp;amp;#39;, sans-serif;&amp;lt;br&amp;gt;
            background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%);&amp;lt;br&amp;gt;
            color: white;&amp;lt;br&amp;gt;
            padding: 20px;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .header {&amp;lt;br&amp;gt;
            text-align: center;&amp;lt;br&amp;gt;
            padding: 40px 20px;&amp;lt;br&amp;gt;
            background: linear-gradient(135deg, #1e2749 0%, #2d3561 100%);&amp;lt;br&amp;gt;
            border-radius: 20px;&amp;lt;br&amp;gt;
            margin-bottom: 30px;&amp;lt;br&amp;gt;
            border: 2px solid rgba(0, 255, 255, 0.3);&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        h1 { font-size: 42px; margin-bottom: 10px; }&amp;lt;br&amp;gt;
        .subtitle { color: #00ffff; font-size: 18px; }&amp;lt;br&amp;gt;
        .alert {&amp;lt;br&amp;gt;
            background: rgba(255, 51, 102, 0.2);&amp;lt;br&amp;gt;
            border: 2px solid #ff3366;&amp;lt;br&amp;gt;
            padding: 20px;&amp;lt;br&amp;gt;
            border-radius: 15px;&amp;lt;br&amp;gt;
            margin: 20px 0;&amp;lt;br&amp;gt;
            text-align: center;&amp;lt;br&amp;gt;
            font-size: 18px;&amp;lt;br&amp;gt;
            font-weight: bold;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .stats {&amp;lt;br&amp;gt;
            display: grid;&amp;lt;br&amp;gt;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));&amp;lt;br&amp;gt;
            gap: 20px;&amp;lt;br&amp;gt;
            margin-bottom: 30px;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .stat-card {&amp;lt;br&amp;gt;
            background: linear-gradient(135deg, #1e2749 0%, #2d3561 100%);&amp;lt;br&amp;gt;
            padding: 30px;&amp;lt;br&amp;gt;
            border-radius: 15px;&amp;lt;br&amp;gt;
            border: 2px solid rgba(0, 255, 255, 0.2);&amp;lt;br&amp;gt;
            text-align: center;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .stat-value {&amp;lt;br&amp;gt;
            font-size: 48px;&amp;lt;br&amp;gt;
            font-weight: bold;&amp;lt;br&amp;gt;
            color: #00ffff;&amp;lt;br&amp;gt;
            margin: 15px 0;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .stat-label {&amp;lt;br&amp;gt;
            color: #888;&amp;lt;br&amp;gt;
            font-size: 14px;&amp;lt;br&amp;gt;
            text-transform: uppercase;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .status-indicator {&amp;lt;br&amp;gt;
            display: inline-block;&amp;lt;br&amp;gt;
            padding: 10px 20px;&amp;lt;br&amp;gt;
            background: rgba(0, 255, 0, 0.2);&amp;lt;br&amp;gt;
            border: 2px solid #00ff00;&amp;lt;br&amp;gt;
            border-radius: 20px;&amp;lt;br&amp;gt;
            margin: 20px 0;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .btn {&amp;lt;br&amp;gt;
            padding: 15px 30px;&amp;lt;br&amp;gt;
            background: linear-gradient(135deg, #00ffff 0%, #00ccff 100%);&amp;lt;br&amp;gt;
            border: none;&amp;lt;br&amp;gt;
            border-radius: 10px;&amp;lt;br&amp;gt;
            color: #0a0e27;&amp;lt;br&amp;gt;
            font-size: 16px;&amp;lt;br&amp;gt;
            font-weight: bold;&amp;lt;br&amp;gt;
            cursor: pointer;&amp;lt;br&amp;gt;
            margin: 10px;&amp;lt;br&amp;gt;
        }&amp;lt;br&amp;gt;
        .btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0, 255, 255, 0.5); }&amp;lt;br&amp;gt;
    &lt;br&gt;
&lt;br&gt;
&lt;br&gt;
    &lt;br&gt;
        &lt;h1&gt;🛡️ MODAC SECURITY DASHBOARD&lt;/h1&gt;
&lt;br&gt;
        &lt;p&gt;Transaction Blocking &amp;amp; Agent Verification System&lt;/p&gt;
&lt;br&gt;
        &lt;br&gt;
            &lt;span&gt;● SYSTEM ACTIVE&lt;/span&gt; - Transaction Blocking Enabled&lt;br&gt;
        &lt;br&gt;
    
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="alert"&amp;gt;
    🚫 DEFAULT MODE: ALL TRANSACTIONS BLOCKED UNTIL AGENTS VERIFIED
&amp;lt;/div&amp;gt;

&amp;lt;div class="stats" id="stats"&amp;gt;
    &amp;lt;div class="stat-card"&amp;gt;
        &amp;lt;div class="stat-label"&amp;gt;Total Agents&amp;lt;/div&amp;gt;
        &amp;lt;div class="stat-value" id="total-agents"&amp;gt;-&amp;lt;/div&amp;gt;
        &amp;lt;div style="font-size: 12px; color: #888;"&amp;gt;Registered in system&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class="stat-card"&amp;gt;
        &amp;lt;div class="stat-label"&amp;gt;Verified Agents&amp;lt;/div&amp;gt;
        &amp;lt;div class="stat-value" style="color: #00ff00;" id="verified-agents"&amp;gt;-&amp;lt;/div&amp;gt;
        &amp;lt;div style="font-size: 12px; color: #888;"&amp;gt;Can transact&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class="stat-card"&amp;gt;
        &amp;lt;div class="stat-label"&amp;gt;Transactions Blocked&amp;lt;/div&amp;gt;
        &amp;lt;div class="stat-value" style="color: #ff3366;" id="blocked-txns"&amp;gt;-&amp;lt;/div&amp;gt;
        &amp;lt;div style="font-size: 12px; color: #888;"&amp;gt;Unverified agents&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class="stat-card"&amp;gt;
        &amp;lt;div class="stat-label"&amp;gt;Transactions Approved&amp;lt;/div&amp;gt;
        &amp;lt;div class="stat-value" style="color: #00ff00;" id="approved-txns"&amp;gt;-&amp;lt;/div&amp;gt;
        &amp;lt;div style="font-size: 12px; color: #888;"&amp;gt;Verified agents only&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div style="text-align: center; padding: 30px;"&amp;gt;
    &amp;lt;button class="btn" onclick="registerAgent()"&amp;gt;➕ Register Agent&amp;lt;/button&amp;gt;
    &amp;lt;button class="btn" onclick="viewRegistry()"&amp;gt;📋 View Registry&amp;lt;/button&amp;gt;
    &amp;lt;button class="btn" onclick="viewBlocked()"&amp;gt;🚫 View Blocked&amp;lt;/button&amp;gt;
    &amp;lt;button class="btn" onclick="window.location.href='/api/v1/system/status'"&amp;gt;📊 API Status&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
    async function loadStats() {
        try {
            const response = await fetch('/api/v1/system/status');
            const data = await response.json();

            document.getElementById('total-agents').textContent = data.agents.total;
            document.getElementById('verified-agents').textContent = data.agents.verified;
            document.getElementById('blocked-txns').textContent = data.transactions.blocked;
            document.getElementById('approved-txns').textContent = data.transactions.approved;
        } catch (error) {
            console.error('Error loading stats:', error);
        }
    }

    function registerAgent() {
        alert('Agent Registration:\n\n1. Submit agent details\n2. Provide identity proof\n3. Complete KYC verification\n4. Security screening\n5. Get VERIFIED status\n\nOnly then can agent transact!');
    }

    function viewRegistry() {
        window.open('/api/v1/system/status', '_blank');
    }

    function viewBlocked() {
        alert('Blocked Transactions:\n\nAll transactions from unverified agents are automatically blocked.\n\nAgents must complete verification before any transactions are allowed.');
    }

    // Load stats on page load and refresh every 10 seconds
    loadStats();
    setInterval(loadStats, 10000);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
&lt;br&gt;
EOF&lt;/p&gt;

&lt;h1&gt;
  
  
  Create NGINX config
&lt;/h1&gt;

&lt;p&gt;mkdir -p nginx&lt;br&gt;
cat &amp;gt; nginx/nginx.conf &amp;lt;&amp;lt; 'EOF'&lt;br&gt;
events { worker_connections 1024; }&lt;/p&gt;

&lt;p&gt;http {&lt;br&gt;
    upstream api {&lt;br&gt;
        server modac-api:8000;&lt;br&gt;
    }&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /health {
        proxy_pass http://api/health;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;}&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;echo "✓ Dashboard created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== PHASE 7: DEPLOYMENT SCRIPTS ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Phase 7/7: Creating Deployment Scripts..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create start script
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; start.sh &amp;lt;&amp;lt; 'EOF'&lt;/p&gt;

&lt;h1&gt;
  
  
  !/bin/bash
&lt;/h1&gt;

&lt;p&gt;echo "🚀 Starting MODAC Global System..."&lt;br&gt;
echo ""&lt;/p&gt;

&lt;h1&gt;
  
  
  Build and start
&lt;/h1&gt;

&lt;p&gt;docker-compose up -d --build&lt;/p&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Waiting for services..."&lt;br&gt;
sleep 10&lt;/p&gt;

&lt;h1&gt;
  
  
  Health check
&lt;/h1&gt;

&lt;p&gt;if curl -f &lt;a href="http://localhost:8000/health" rel="noopener noreferrer"&gt;http://localhost:8000/health&lt;/a&gt; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; then&lt;br&gt;
    echo "✅ API is healthy"&lt;br&gt;
else&lt;br&gt;
    echo "❌ API health check failed"&lt;br&gt;
fi&lt;/p&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "╔══════════════════════════════════════════════════════════════════╗"&lt;br&gt;
echo "║                 MODAC SYSTEM DEPLOYED!                           ║"&lt;br&gt;
echo "╠══════════════════════════════════════════════════════════════════╣"&lt;br&gt;
echo "║  Dashboard:  &lt;a href="http://localhost" rel="noopener noreferrer"&gt;http://localhost&lt;/a&gt;                                    ║"&lt;br&gt;
echo "║  API:        &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;                               ║"&lt;br&gt;
echo "║  API Docs:   &lt;a href="http://localhost:8000/docs" rel="noopener noreferrer"&gt;http://localhost:8000/docs&lt;/a&gt;                          ║"&lt;br&gt;
echo "║  Status:     &lt;a href="http://localhost:8000/api/v1/system/status" rel="noopener noreferrer"&gt;http://localhost:8000/api/v1/system/status&lt;/a&gt;          ║"&lt;br&gt;
echo "╠══════════════════════════════════════════════════════════════════╣"&lt;br&gt;
echo "║  🛡️  TRANSACTION BLOCKING: ENABLED                              ║"&lt;br&gt;
echo "║  🚫 DEFAULT: Block all unverified agents                        ║"&lt;br&gt;
echo "╚══════════════════════════════════════════════════════════════════╝"&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;chmod +x start.sh&lt;/p&gt;

&lt;h1&gt;
  
  
  Create stop script
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; stop.sh &amp;lt;&amp;lt; 'EOF'&lt;/p&gt;

&lt;h1&gt;
  
  
  !/bin/bash
&lt;/h1&gt;

&lt;p&gt;echo "Stopping MODAC..."&lt;br&gt;
docker-compose down&lt;br&gt;
echo "✓ MODAC stopped"&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;chmod +x stop.sh&lt;/p&gt;

&lt;h1&gt;
  
  
  Create test script
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; test_blocking.sh &amp;lt;&amp;lt; 'EOF'&lt;/p&gt;

&lt;h1&gt;
  
  
  !/bin/bash
&lt;/h1&gt;

&lt;p&gt;echo "Testing Transaction Blocking..."&lt;br&gt;
echo ""&lt;/p&gt;

&lt;p&gt;API="&lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;echo "Test 1: Attempt transaction with unverified agents"&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;br&gt;
curl -X POST "$API/api/v1/transactions/execute" \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{&lt;br&gt;
    "buyer_agent_id": "unverified_001",&lt;br&gt;
    "seller_agent_id": "unverified_002",&lt;br&gt;
    "amount": 1000,&lt;br&gt;
    "currency": "USD"&lt;br&gt;
  }' | jq .&lt;/p&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo ""&lt;br&gt;
echo "Expected: Transaction BLOCKED ❌"&lt;br&gt;
echo ""&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;chmod +x test_blocking.sh&lt;/p&gt;

&lt;p&gt;echo "✓ Deployment scripts created"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== FINAL SETUP ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "Final Setup..."&lt;br&gt;
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"&lt;/p&gt;

&lt;h1&gt;
  
  
  Create empty registry
&lt;/h1&gt;

&lt;p&gt;mkdir -p data/registry&lt;br&gt;
echo "{}" &amp;gt; data/registry/agents.json&lt;/p&gt;

&lt;h1&gt;
  
  
  Create README
&lt;/h1&gt;

&lt;p&gt;cat &amp;gt; README.md &amp;lt;&amp;lt; 'EOF'&lt;/p&gt;

&lt;h1&gt;
  
  
  MODAC GLOBAL SYSTEM
&lt;/h1&gt;

&lt;p&gt;Complete deployment with transaction blocking and agent verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start system&lt;/span&gt;
./start.sh

&lt;span class="c"&gt;# View dashboard&lt;/span&gt;
http://localhost

&lt;span class="c"&gt;# Test blocking&lt;/span&gt;
./test_blocking.sh

&lt;span class="c"&gt;# Stop system&lt;/span&gt;
./stop.sh
&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Security Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Transaction blocking enabled by default&lt;/li&gt;
&lt;li&gt;✅ All agents must register and verify&lt;/li&gt;
&lt;li&gt;✅ Real-time monitoring of all transactions&lt;/li&gt;
&lt;li&gt;✅ Automatic blocking of unverified agents&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Endpoints
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/agents/register&lt;/code&gt; - Register new agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/agents/{id}/verify&lt;/code&gt; - Verify agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/transactions/execute&lt;/code&gt; - Execute transaction (blocked if unverified)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/system/status&lt;/code&gt; - System status&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Agent attempts transaction&lt;/li&gt;
&lt;li&gt;MODAC intercepts transaction&lt;/li&gt;
&lt;li&gt;Checks if both agents are verified&lt;/li&gt;
&lt;li&gt;If NOT verified → BLOCKS transaction&lt;/li&gt;
&lt;li&gt;If verified → APPROVES transaction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;DEFAULT: Everything is BLOCKED until verified!&lt;/strong&gt;&lt;br&gt;
EOF&lt;/p&gt;

&lt;p&gt;echo "✓ Setup complete"&lt;/p&gt;

&lt;h1&gt;
  
  
  ==================== DEPLOYMENT COMPLETE ====================
&lt;/h1&gt;

&lt;p&gt;echo ""&lt;br&gt;
echo "╔══════════════════════════════════════════════════════════════════════╗"&lt;br&gt;
echo "║                                                                      ║"&lt;br&gt;
echo "║               🎉 MODAC DEPLOYMENT COMPLETE! 🎉                      ║"&lt;br&gt;
echo "║                                                                      ║"&lt;br&gt;
echo "╚══════════════════════════════════════════════════════════════════════╝"&lt;br&gt;
echo ""&lt;br&gt;
echo "✅ All components installed:"&lt;br&gt;
echo "   • Transaction Blocking System"&lt;br&gt;
echo "   • Agent Registry"&lt;br&gt;
echo "   • Verification System"&lt;br&gt;
echo "   • Security Dashboard"&lt;br&gt;
echo "   • API with blocking enforcement"&lt;br&gt;
echo ""&lt;br&gt;
echo "🚀 Ready to deploy! Run:"&lt;br&gt;
echo "   ./start.sh"&lt;br&gt;
echo ""&lt;br&gt;
echo "📊 Then visit:"&lt;br&gt;
echo "   &lt;a href="http://localhost" rel="noopener noreferrer"&gt;http://localhost&lt;/a&gt; - Security Dashboard"&lt;br&gt;
echo "   &lt;a href="http://localhost:8000/docs" rel="noopener noreferrer"&gt;http://localhost:8000/docs&lt;/a&gt; - API Documentation"&lt;br&gt;
echo ""&lt;br&gt;
echo "🛡️  SECURITY STATUS:"&lt;br&gt;
echo "   ✓ Transaction blocking: ENABLED"&lt;br&gt;
echo "   ✓ Agent verification: REQUIRED"&lt;br&gt;
echo "   ✓ Default mode: BLOCK ALL"&lt;br&gt;
echo ""This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>HERMES AGENT SUMMISSIO</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Wed, 20 May 2026 06:35:40 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/hermes-agent-summissio-2op2</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/hermes-agent-summissio-2op2</guid>
      <description>&lt;p&gt;*# How I Built an Agentic Layer on AIRTIMES Using Hermes Agent&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is AIRTIMES?
&lt;/h2&gt;

&lt;p&gt;AIRTIMES is a zero-dependency global data and service network I built from scratch — a full-stack Node.js backend featuring &lt;strong&gt;AirPay&lt;/strong&gt; (a crypto payment module) and &lt;strong&gt;AirBank&lt;/strong&gt; (a revenue tracking engine). No external libraries. No cloud lock-in. Every byte of it runs on infrastructure I control.&lt;/p&gt;

&lt;p&gt;It's the kind of platform that's always generating data — transactions, node health events, revenue streams — but has no brain to interpret that data in real time. That's exactly the gap I wanted to close.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Infrastructure Without Intelligence
&lt;/h2&gt;

&lt;p&gt;AIRTIMES processes payments and tracks revenue across nodes. But monitoring it meant either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staring at logs manually&lt;/li&gt;
&lt;li&gt;Writing one-off scripts that break and never improve&lt;/li&gt;
&lt;li&gt;Paying for a SaaS dashboard that doesn't understand my custom data model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those felt right. What I needed was an agent that could &lt;strong&gt;live inside the platform&lt;/strong&gt;, understand its data, react to events, and — crucially — get smarter the longer it ran.&lt;/p&gt;

&lt;p&gt;That's when I found &lt;strong&gt;Hermes Agent&lt;/strong&gt; by Nous Research.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Hermes?
&lt;/h2&gt;

&lt;p&gt;Most agent frameworks are graphs you design ahead of time. You wire the nodes, define the flows, and ship. The agent's capability is permanently bounded by what you prompted into it on day one.&lt;/p&gt;

&lt;p&gt;Hermes is different. It has a &lt;strong&gt;built-in learning loop&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After completing a complex task, it &lt;strong&gt;automatically writes a reusable skill&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Skills &lt;strong&gt;self-improve&lt;/strong&gt; every time they're used&lt;/li&gt;
&lt;li&gt;It builds a &lt;strong&gt;persistent memory&lt;/strong&gt; of your projects across sessions&lt;/li&gt;
&lt;li&gt;It runs &lt;strong&gt;unattended automations&lt;/strong&gt; via natural language cron scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AIRTIMES — a platform that runs 24/7 — this is exactly the right architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built: The AIRTIMES Agentic Layer
&lt;/h2&gt;

&lt;p&gt;I installed Hermes directly on the AIRTIMES server and gave it three jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AirPay Transaction Monitor
&lt;/h3&gt;

&lt;p&gt;Hermes watches the AirPay transaction stream continuously. When a payment fails, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Diagnoses the failure (network timeout? invalid address? insufficient funds?)&lt;/li&gt;
&lt;li&gt;Triggers the retry logic&lt;/li&gt;
&lt;li&gt;Logs a structured incident report&lt;/li&gt;
&lt;li&gt;Sends a Telegram alert to my phone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first time it did this, it wrote a skill called &lt;code&gt;airpay_failure_diagnosis.skill&lt;/code&gt; automatically. The next failure? It handled in seconds — no manual input.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AirBank Daily Revenue Briefing
&lt;/h3&gt;

&lt;p&gt;Every morning at 8am, Hermes pulls from the AirBank revenue engine and delivers a plain-language summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📊 AIRBANK DAILY BRIEF — May 20, 2026
Revenue: $4,820 (+12% vs yesterday)
Top node: Node 3 ($1,240)
Anomaly: Node 7 — 0 transactions since 03:00 UTC
Action: Pinging Node 7 now...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hermes cron &lt;span class="s2"&gt;"every day at 8am run airbank_daily_brief"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No dashboard. No manual query. Just a brief, every morning, in my Telegram.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Multi-Platform Command Surface
&lt;/h3&gt;

&lt;p&gt;I connected Hermes to Telegram so I can command the entire AIRTIMES infrastructure in plain language from my phone:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's total AirPay revenue this week?"&lt;br&gt;
"Is Node 7 online?"&lt;br&gt;
"How many transactions failed in the last 24 hours?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hermes fetches, reasons, and responds. It works on Telegram, Discord, Slack, and CLI — all from a single gateway process.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Self-Improving Loop in Action
&lt;/h2&gt;

&lt;p&gt;This is what separates Hermes from every other agent I've used.&lt;/p&gt;

&lt;p&gt;After three days of running on AIRTIMES, Hermes had automatically generated &lt;strong&gt;6 custom skills&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airpay_failure_diagnosis.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First payment failure investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node_health_check.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First uptime check across nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revenue_anomaly_alert.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First time it spotted a revenue dip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airbank_daily_brief.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the first manual briefing request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multi_node_ping.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After diagnosing Node 7 offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx_retry_handler.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the second payment retry sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I didn't write any of these. Hermes wrote them itself — and every subsequent run of the same task was faster and more accurate because the skill had been refined.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation (60 seconds)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Hermes&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://hermes-agent.nousresearch.com/install.sh | bash

&lt;span class="c"&gt;# Run setup wizard&lt;/span&gt;
hermes setup

&lt;span class="c"&gt;# Connect Telegram&lt;/span&gt;
hermes setup &lt;span class="nt"&gt;--gateway&lt;/span&gt; telegram

&lt;span class="c"&gt;# Point it at your project&lt;/span&gt;
hermes chat &lt;span class="s2"&gt;"I have a Node.js server running AIRTIMES on port 3000. 
Monitor transaction health and report anomalies."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What This Changes
&lt;/h2&gt;

&lt;p&gt;AIRTIMES used to be infrastructure I managed. Now it's infrastructure that manages itself — with me as the oversight layer, not the execution layer.&lt;/p&gt;

&lt;p&gt;Hermes doesn't just execute tasks. It &lt;strong&gt;accumulates operational knowledge&lt;/strong&gt; about AIRTIMES: which nodes fail most often, what payment patterns precede failures, which revenue anomalies are noise vs. signal. That knowledge compounds.&lt;/p&gt;

&lt;p&gt;Six months from now, the version of Hermes running on AIRTIMES will be meaningfully smarter than the version I installed today — without me touching a line of code.&lt;/p&gt;

&lt;p&gt;That's the promise of a self-improving agent. And for a global infrastructure platform like AIRTIMES, that's not a nice-to-have. It's the only architecture that makes sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expanding Hermes to manage AIREXPRESS (EV mesh connectivity dashboard)&lt;/li&gt;
&lt;li&gt;Building a public &lt;code&gt;agentskills.io&lt;/code&gt; skill pack for AIRTIMES operators&lt;/li&gt;
&lt;li&gt;Exploring batch trajectory generation for training a domain-specific AIRTIMES model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;a href="https://hermes-agent.nousresearch.com" rel="noopener noreferrer"&gt;Hermes Agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://github.com/NousResearch/hermes-agent" rel="noopener noreferrer"&gt;GitHub — NousResearch/hermes-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with Hermes Agent by Nous Research · MIT License&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: #hermesagentchallenge #devchallenge #agents #opensource&lt;/em&gt;&lt;br&gt;
 is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;*&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
    </item>
    <item>
      <title>HERMES AGENT WRITE UP SUMMISSIO</title>
      <dc:creator>Gift Trust</dc:creator>
      <pubDate>Wed, 20 May 2026 06:33:50 +0000</pubDate>
      <link>https://dev.to/gift_trust_44882a016531d7/hermes-agent-write-up-summissio-3o1e</link>
      <guid>https://dev.to/gift_trust_44882a016531d7/hermes-agent-write-up-summissio-3o1e</guid>
      <description>&lt;p&gt;*# How I Built an Agentic Layer on AIRTIMES Using Hermes Agent&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is AIRTIMES?
&lt;/h2&gt;

&lt;p&gt;AIRTIMES is a zero-dependency global data and service network I built from scratch — a full-stack Node.js backend featuring &lt;strong&gt;AirPay&lt;/strong&gt; (a crypto payment module) and &lt;strong&gt;AirBank&lt;/strong&gt; (a revenue tracking engine). No external libraries. No cloud lock-in. Every byte of it runs on infrastructure I control.&lt;/p&gt;

&lt;p&gt;It's the kind of platform that's always generating data — transactions, node health events, revenue streams — but has no brain to interpret that data in real time. That's exactly the gap I wanted to close.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Infrastructure Without Intelligence
&lt;/h2&gt;

&lt;p&gt;AIRTIMES processes payments and tracks revenue across nodes. But monitoring it meant either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staring at logs manually&lt;/li&gt;
&lt;li&gt;Writing one-off scripts that break and never improve&lt;/li&gt;
&lt;li&gt;Paying for a SaaS dashboard that doesn't understand my custom data model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those felt right. What I needed was an agent that could &lt;strong&gt;live inside the platform&lt;/strong&gt;, understand its data, react to events, and — crucially — get smarter the longer it ran.&lt;/p&gt;

&lt;p&gt;That's when I found &lt;strong&gt;Hermes Agent&lt;/strong&gt; by Nous Research.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Hermes?
&lt;/h2&gt;

&lt;p&gt;Most agent frameworks are graphs you design ahead of time. You wire the nodes, define the flows, and ship. The agent's capability is permanently bounded by what you prompted into it on day one.&lt;/p&gt;

&lt;p&gt;Hermes is different. It has a &lt;strong&gt;built-in learning loop&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After completing a complex task, it &lt;strong&gt;automatically writes a reusable skill&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Skills &lt;strong&gt;self-improve&lt;/strong&gt; every time they're used&lt;/li&gt;
&lt;li&gt;It builds a &lt;strong&gt;persistent memory&lt;/strong&gt; of your projects across sessions&lt;/li&gt;
&lt;li&gt;It runs &lt;strong&gt;unattended automations&lt;/strong&gt; via natural language cron scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AIRTIMES — a platform that runs 24/7 — this is exactly the right architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built: The AIRTIMES Agentic Layer
&lt;/h2&gt;

&lt;p&gt;I installed Hermes directly on the AIRTIMES server and gave it three jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AirPay Transaction Monitor
&lt;/h3&gt;

&lt;p&gt;Hermes watches the AirPay transaction stream continuously. When a payment fails, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Diagnoses the failure (network timeout? invalid address? insufficient funds?)&lt;/li&gt;
&lt;li&gt;Triggers the retry logic&lt;/li&gt;
&lt;li&gt;Logs a structured incident report&lt;/li&gt;
&lt;li&gt;Sends a Telegram alert to my phone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first time it did this, it wrote a skill called &lt;code&gt;airpay_failure_diagnosis.skill&lt;/code&gt; automatically. The next failure? It handled in seconds — no manual input.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AirBank Daily Revenue Briefing
&lt;/h3&gt;

&lt;p&gt;Every morning at 8am, Hermes pulls from the AirBank revenue engine and delivers a plain-language summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📊 AIRBANK DAILY BRIEF — May 20, 2026
Revenue: $4,820 (+12% vs yesterday)
Top node: Node 3 ($1,240)
Anomaly: Node 7 — 0 transactions since 03:00 UTC
Action: Pinging Node 7 now...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hermes cron &lt;span class="s2"&gt;"every day at 8am run airbank_daily_brief"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No dashboard. No manual query. Just a brief, every morning, in my Telegram.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Multi-Platform Command Surface
&lt;/h3&gt;

&lt;p&gt;I connected Hermes to Telegram so I can command the entire AIRTIMES infrastructure in plain language from my phone:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's total AirPay revenue this week?"&lt;br&gt;
"Is Node 7 online?"&lt;br&gt;
"How many transactions failed in the last 24 hours?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hermes fetches, reasons, and responds. It works on Telegram, Discord, Slack, and CLI — all from a single gateway process.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Self-Improving Loop in Action
&lt;/h2&gt;

&lt;p&gt;This is what separates Hermes from every other agent I've used.&lt;/p&gt;

&lt;p&gt;After three days of running on AIRTIMES, Hermes had automatically generated &lt;strong&gt;6 custom skills&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airpay_failure_diagnosis.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First payment failure investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node_health_check.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First uptime check across nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revenue_anomaly_alert.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First time it spotted a revenue dip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;airbank_daily_brief.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the first manual briefing request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multi_node_ping.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After diagnosing Node 7 offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tx_retry_handler.skill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the second payment retry sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I didn't write any of these. Hermes wrote them itself — and every subsequent run of the same task was faster and more accurate because the skill had been refined.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation (60 seconds)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Hermes&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://hermes-agent.nousresearch.com/install.sh | bash

&lt;span class="c"&gt;# Run setup wizard&lt;/span&gt;
hermes setup

&lt;span class="c"&gt;# Connect Telegram&lt;/span&gt;
hermes setup &lt;span class="nt"&gt;--gateway&lt;/span&gt; telegram

&lt;span class="c"&gt;# Point it at your project&lt;/span&gt;
hermes chat &lt;span class="s2"&gt;"I have a Node.js server running AIRTIMES on port 3000. 
Monitor transaction health and report anomalies."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What This Changes
&lt;/h2&gt;

&lt;p&gt;AIRTIMES used to be infrastructure I managed. Now it's infrastructure that manages itself — with me as the oversight layer, not the execution layer.&lt;/p&gt;

&lt;p&gt;Hermes doesn't just execute tasks. It &lt;strong&gt;accumulates operational knowledge&lt;/strong&gt; about AIRTIMES: which nodes fail most often, what payment patterns precede failures, which revenue anomalies are noise vs. signal. That knowledge compounds.&lt;/p&gt;

&lt;p&gt;Six months from now, the version of Hermes running on AIRTIMES will be meaningfully smarter than the version I installed today — without me touching a line of code.&lt;/p&gt;

&lt;p&gt;That's the promise of a self-improving agent. And for a global infrastructure platform like AIRTIMES, that's not a nice-to-have. It's the only architecture that makes sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expanding Hermes to manage AIREXPRESS (EV mesh connectivity dashboard)&lt;/li&gt;
&lt;li&gt;Building a public &lt;code&gt;agentskills.io&lt;/code&gt; skill pack for AIRTIMES operators&lt;/li&gt;
&lt;li&gt;Exploring batch trajectory generation for training a domain-specific AIRTIMES model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;a href="https://hermes-agent.nousresearch.com" rel="noopener noreferrer"&gt;Hermes Agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://github.com/NousResearch/hermes-agent" rel="noopener noreferrer"&gt;GitHub — NousResearch/hermes-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with Hermes Agent by Nous Research · MIT License&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: #hermesagentchallenge #devchallenge #agents #opensource&lt;/em&gt;&lt;br&gt;
 is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  My Tech Stack
&lt;/h3&gt;

&lt;h2&gt;
  
  
  How I Used Hermes Agent
&lt;/h2&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
