iuna

iuna

iuna - experimental devnet protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

index_html.rs (130403B)


      1 pub(super) const INDEX_HTML: &str = r#"<!doctype html>
      2 <html lang="en">
      3 <head>
      4   <meta charset="utf-8">
      5   <meta name="viewport" content="width=device-width, initial-scale=1">
      6   <link rel="icon" href="data:,">
      7   <title>iuna</title>
      8   <style>
      9     [x-cloak] { display: none !important; }
     10     .visually-hidden { position: absolute !important; width: 1px !important; height: 1px !important; overflow: hidden !important; clip: rect(0 0 0 0) !important; clip-path: inset(50%) !important; white-space: nowrap !important; }
     11     :root {
     12       color-scheme: dark;
     13       font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
     14       background: #0f1012;
     15       color: #e8edf0;
     16     }
     17     * { box-sizing: border-box; }
     18     html { width: 100%; max-width: 100%; overflow-x: hidden; }
     19     body { margin: 0; min-height: 100vh; max-width: 100%; overflow-x: hidden; background: #0f1012; color: #e8edf0; }
     20     .app-shell { width: 100%; max-width: 100%; min-height: 100vh; display: block; overflow-x: hidden; }
     21     .sidebar { position: fixed; z-index: 5; inset: 0 auto 0 0; width: 84px; height: 100vh; display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 16px 10px; background: #15171a; border-right: 1px solid #262b2f; }
     22     .brand-mark { position: relative; width: 38px; height: 38px; display: grid; place-items: center; overflow: hidden; border: 1px solid #e8ff8d; border-radius: 8px; background: linear-gradient(145deg, #ecff8a 0%, #d5f55f 54%, #8de9cd 100%); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .42), 0 10px 24px rgba(213, 245, 95, .16); user-select: none; cursor: default; }
     23     .brand-mark::after { content: ""; position: absolute; inset: -40% -70%; background: linear-gradient(100deg, transparent 42%, rgba(255, 255, 255, .34) 50%, transparent 58%); transform: translateX(-58%) rotate(8deg); opacity: 0; pointer-events: none; }
     24     .brand-mark svg { position: relative; z-index: 1; width: 24px; height: 24px; display: block; }
     25     .brand-mark .mark-loop { fill: none; stroke: #101315; stroke-width: 4.2; stroke-linecap: round; stroke-linejoin: round; }
     26     .brand-mark .mark-dot { fill: #101315; }
     27     .brand-mark:hover::after { animation: mark-sheen .72s ease both; }
     28     @keyframes mark-sheen { from { opacity: 0; transform: translateX(-58%) rotate(8deg); } 32% { opacity: 1; } to { opacity: 0; transform: translateX(58%) rotate(8deg); } }
     29     .side-nav { display: grid; gap: 10px; width: 100%; }
     30     .nav-button { width: 64px; min-height: 58px; display: grid; place-items: center; gap: 4px; border: 1px solid transparent; border-radius: 8px; padding: 7px 4px; background: transparent; color: #9fa8ad; }
     31     .nav-button svg { width: 21px; height: 21px; stroke: currentColor; stroke-width: 2; fill: none; }
     32     .nav-button svg.chain-icon { stroke-width: 1.35; }
     33     .nav-button span { font-size: 11px; font-weight: 800; }
     34     .nav-button:hover, .nav-button.active { background: #202328; border-color: #3b4448; color: #d5f55f; }
     35     .settings-button { margin-top: auto; width: 64px; min-height: 54px; display: grid; place-items: center; border: 1px solid transparent; border-radius: 8px; padding: 7px 4px; color: #9fa8ad; background: transparent; text-align: center; }
     36     .settings-button svg { width: 23px; height: 23px; stroke: currentColor; stroke-width: 1.9; fill: none; }
     37     .settings-button:hover, .settings-button.active { background: #202328; border-color: #3b4448; color: #d5f55f; }
     38     .version-panel { width: 64px; display: grid; gap: 4px; justify-items: center; border: 1px solid transparent; border-radius: 8px; padding: 7px 4px; color: #7f888e; background: transparent; font-size: 10px; font-weight: 850; text-align: center; }
     39     .version-panel.update { border-color: #566d25; color: #d5f55f; background: #1c2516; cursor: pointer; }
     40     .version-panel.checking { color: #a8b2b8; }
     41     .version-panel.failed { color: #ffb1a8; }
     42     .version-dot { width: 6px; height: 6px; border-radius: 999px; background: #3a4248; }
     43     .version-panel.update .version-dot { background: #d5f55f; box-shadow: 0 0 0 3px rgba(213, 245, 95, .12); }
     44     .version-panel.failed .version-dot { background: #ff8f82; }
     45     .version-label { line-height: 1; }
     46     .version-update { color: #d5f55f; font-size: 9px; line-height: 1; text-transform: uppercase; }
     47     .content { width: 100%; min-width: 0; overflow-x: hidden; padding: 22px 24px 48px 108px; }
     48     main { width: 100%; }
     49     main > section { width: 100%; }
     50     header { display: flex; justify-content: space-between; gap: 18px; align-items: flex-start; padding: 0 0 18px; }
     51     .header-actions { display: flex; gap: 10px; align-items: center; }
     52     .basic-status-row { display: inline-flex; gap: 8px; align-items: center; margin-top: 5px; }
     53     .basic-status { display: inline-flex; gap: 7px; align-items: center; border: 1px solid transparent; border-radius: 999px; padding: 3px 7px; color: #9eb3bc; font-size: 12px; font-weight: 800; }
     54     .basic-status::before { content: ""; width: 6px; height: 6px; flex: 0 0 auto; border-radius: 999px; background: #7f888e; }
     55     .basic-status.healthy { color: #d5f55f; }
     56     .basic-status.healthy::before { background: #d5f55f; box-shadow: 0 0 12px rgba(213, 245, 95, .45); }
     57     .basic-status.syncing { color: #ffd070; }
     58     .basic-status.syncing::before { background: #ffd070; }
     59     .basic-status.isolated, .basic-status.stale, .basic-status.banned, .basic-status.error { border-color: #6a332c; background: #261817; color: #ffb8ad; font-weight: 900; }
     60     .basic-status.isolated::before, .basic-status.stale::before, .basic-status.banned::before, .basic-status.error::before { background: #ff7668; box-shadow: 0 0 12px rgba(255, 118, 104, .38); }
     61     .basic-status-detail { padding: 3px 7px; border-color: #3a4248; background: #202328; color: #9fa8ad; font-size: 11px; }
     62     .basic-status-detail:hover { border-color: #d5f55f; color: #d5f55f; }
     63     .lock-button { padding: 5px 8px; border-color: #3a4248; background: #202328; color: #9fa8ad; font-size: 12px; }
     64     .lock-button:hover { border-color: #d5f55f; color: #d5f55f; }
     65     h1 { margin: 0 0 4px; font-size: 28px; }
     66     h2 { margin: 0 0 12px; font-size: 18px; }
     67     h3 { margin: 0 0 10px; font-size: 15px; }
     68     button { border: 1px solid #3a4248; border-radius: 6px; padding: 8px 11px; font: inherit; font-weight: 700; background: #191c20; color: #e8edf0; cursor: pointer; }
     69     button:hover { border-color: #d5f55f; color: #d5f55f; }
     70     button.primary { background: #d5f55f; border-color: #d5f55f; color: #15171a; }
     71     button.primary:hover { background: #e4ff83; color: #15171a; }
     72     button.subtle { background: transparent; }
     73     button.danger { border-color: #8f3730; background: #341918; color: #ffb1a8; }
     74     button.danger:hover { border-color: #ff7668; background: #451c1a; color: #ffd8d3; }
     75     button:disabled { cursor: default; opacity: .5; }
     76     .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
     77     .metric, .panel { background: #181b1f; border: 1px solid #2a3035; border-radius: 8px; padding: 13px; }
     78     .metric .label { color: #8d989f; font-size: 11px; text-transform: uppercase; }
     79     .metric .value { margin-top: 7px; font-weight: 850; overflow-wrap: anywhere; }
     80     .panel { margin-bottom: 12px; }
     81     .split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, .72fr); gap: 12px; }
     82     form { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
     83     label { display: grid; gap: 5px; color: #a8b2b8; font-size: 13px; }
     84     input, textarea { min-width: 180px; border: 1px solid #3a444b; border-radius: 6px; padding: 9px 10px; font: inherit; background: #101215; color: #edf2f5; }
     85     textarea { min-height: 118px; resize: vertical; line-height: 1.45; }
     86     input:focus, textarea:focus { outline: 2px solid #d5f55f; outline-offset: 1px; }
     87     table { width: 100%; border-collapse: collapse; font-size: 13px; }
     88     th, td { text-align: left; border-bottom: 1px solid #2a3035; padding: 8px; vertical-align: top; }
     89     th { color: #8d989f; font-size: 11px; text-transform: uppercase; }
     90     code { overflow-wrap: anywhere; color: #c7f5ea; }
     91     .table-wrap { overflow-x: auto; }
     92     .muted { color: #8d989f; }
     93     .flash { position: fixed; top: 18px; right: 18px; z-index: 80; width: min(420px, calc(100vw - 36px)); border-radius: 6px; padding: 10px 12px; border: 1px solid; font-weight: 700; box-shadow: 0 18px 48px rgba(0, 0, 0, .38); }
     94     .flash.success { color: #d5f55f; background: #1c2516; border-color: #566d25; }
     95     .flash.error { color: #ffb1a8; background: #2a1717; border-color: #713434; }
     96     .persistent-banner { border: 1px solid #566d25; border-radius: 8px; padding: 10px 12px; margin: -4px 0 16px; color: #d5f55f; background: #1c2516; font-weight: 800; }
     97     .ok { color: #d5f55f; }
     98     .page-title { margin-bottom: 16px; }
     99     .setup-overlay { position: fixed; inset: 0; z-index: 30; display: grid; place-items: center; padding: 22px; background: rgba(8, 9, 10, .72); backdrop-filter: blur(8px); }
    100     .transaction-overlay { z-index: 40; }
    101     .setup-modal { width: min(980px, 100%); max-height: calc(100vh - 44px); overflow: auto; border: 1px solid #3b4448; border-radius: 8px; padding: 18px; background: #181b1f; box-shadow: 0 24px 80px rgba(0, 0, 0, .42); }
    102     .setup-modal-head { display: grid; gap: 5px; margin-bottom: 16px; }
    103     .setup-modal-head h2 { margin: 0; font-size: 24px; }
    104     .setup-welcome { color: #d5f55f; font-size: 12px; font-weight: 900; text-transform: uppercase; }
    105     .setup-copy { max-width: 620px; color: #a8b2b8; line-height: 1.45; }
    106     .setup-feedback { border: 1px solid; border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; font-weight: 800; }
    107     .setup-feedback.success { color: #d5f55f; background: #1c2516; border-color: #566d25; }
    108     .setup-feedback.error { color: #ffb1a8; background: #2a1717; border-color: #713434; }
    109     .setup-grid { width: 100%; display: grid; grid-template-columns: minmax(0, .9fr) minmax(320px, .7fr); gap: 12px; align-items: start; }
    110     .setup-section { border: 1px solid #2f363c; border-radius: 8px; padding: 13px; background: #111316; }
    111     .setup-node-mode, .setup-network, .setup-wallet-section { grid-column: 1 / -1; }
    112     .segmented.setup-mode-picker { width: 100%; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
    113     .setup-mode-picker button { min-width: 0; min-height: 38px; white-space: normal; }
    114     .setup-network-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-items: end; }
    115     .setup-network-copy { margin-top: 8px; color: #a8b2b8; line-height: 1.45; }
    116     .setup-network-link { color: #d5f55f; font-size: 12px; font-weight: 900; text-decoration: none; }
    117     .setup-network-link:hover { text-decoration: underline; }
    118     .setup-field { display: grid; gap: 6px; }
    119     .setup-field-label { color: #8d989f; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 0; }
    120     .setup-address-box { display: flex; justify-content: space-between; gap: 10px; align-items: center; }
    121     .setup-address-box code { min-width: 0; }
    122     .setup-address-box button { flex: 0 0 auto; }
    123     .setup-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 14px; }
    124     .segmented { display: inline-flex; gap: 4px; padding: 4px; border: 1px solid #2f363c; border-radius: 8px; background: #181b1f; }
    125     .segmented button { border-color: transparent; background: transparent; color: #9fa8ad; }
    126     .segmented button.active { background: #d5f55f; color: #15171a; }
    127     .seed-panel { display: grid; gap: 12px; }
    128     .seed-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
    129     .seed-word { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 7px; align-items: center; border: 1px solid #2f363c; border-radius: 6px; padding: 7px 8px; background: #181b1f; }
    130     .seed-word .index { color: #8d989f; font-size: 11px; font-weight: 800; }
    131     .seed-word .word { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight: 800; color: #c7f5ea; }
    132     .verify-grid { display: grid; gap: 8px; }
    133     .setup-status { border: 1px solid #566d25; border-radius: 8px; padding: 10px; background: #1c2516; color: #d5f55f; font-weight: 800; }
    134     .auth-form { width: min(420px, 100%); display: grid; gap: 10px; }
    135     .auth-form form { display: grid; gap: 10px; align-items: stretch; }
    136     .auth-form input { width: 100%; }
    137     .settings-grid { width: min(760px, 100%); display: grid; gap: 12px; }
    138     .settings-mode-row { display: flex; justify-content: space-between; gap: 14px; align-items: center; }
    139     .settings-mode-copy { min-width: 0; display: grid; gap: 4px; }
    140     .settings-mode-title { color: #e8edf0; font-size: 15px; font-weight: 850; }
    141     .settings-form { display: grid; gap: 10px; align-items: stretch; }
    142     .public-p2p-form { margin-top: 14px; }
    143     .settings-form label, .settings-form input { width: 100%; }
    144     .danger-panel { border-color: #6a332c; background: #201313; }
    145     .danger-panel h3, .danger-title { color: #ffb1a8; }
    146     .danger-copy { color: #d69a92; line-height: 1.45; }
    147     .danger-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }
    148     .metrics-shell { display: grid; gap: 12px; }
    149     .metrics-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; }
    150     .metrics-head h2 { margin: 0; }
    151     .metrics-range { flex: 0 0 auto; }
    152     .metrics-range button { padding: 5px 9px; font-size: 12px; white-space: nowrap; }
    153     .metrics-subhead { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; margin-top: 8px; }
    154     .metrics-subhead h2 { margin: 0; }
    155     .metrics-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
    156     .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 430px), 1fr)); gap: 12px; }
    157     .metric-chart-card { display: grid; gap: 10px; min-width: 0; border: 1px solid #2a3035; border-radius: 8px; padding: 12px; background: #181b1f; }
    158     .metric-chart-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
    159     .metric-chart-title { margin: 0; color: #e8edf0; font-size: 14px; font-weight: 850; }
    160     .metric-chart-value { color: #d5f55f; font-size: 13px; font-weight: 850; font-variant-numeric: tabular-nums; }
    161     .metric-chart-frame { display: grid; grid-template-columns: 50px minmax(0, 1fr); grid-template-rows: 156px 18px; column-gap: 6px; row-gap: 4px; min-width: 0; }
    162     .metric-chart-plot { position: relative; min-width: 0; }
    163     .metric-chart-svg { width: 100%; height: 156px; display: block; border: 1px solid #2f363c; border-radius: 8px; background: #111316; }
    164     .metric-chart-gridline { stroke: #3a4248; stroke-width: .8; stroke-dasharray: 3 7; opacity: .58; }
    165     .metric-chart-axis { stroke: #3a4248; stroke-width: 1.2; }
    166     .metric-chart-axis-label { color: #8d989f; font-size: 10px; font-weight: 750; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    167     .metric-chart-y-axis { position: relative; min-width: 0; }
    168     .metric-chart-y-axis .metric-chart-axis-label { position: absolute; right: 0; transform: translateY(-50%); max-width: 100%; }
    169     .metric-chart-x-axis { position: relative; grid-column: 2; min-width: 0; overflow: visible; }
    170     .metric-chart-x-axis .metric-chart-axis-label { position: absolute; top: 0; transform: translateX(-50%); }
    171     .metric-chart-line { fill: none; stroke: #d5f55f; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; }
    172     .metric-chart-hover-point { position: absolute; width: 8px; height: 8px; border-radius: 50%; background: #d5f55f; pointer-events: none; transform: translate(-50%, -50%); box-shadow: 0 0 0 4px rgba(213, 245, 95, .18); }
    173     .metric-chart-tooltip { position: absolute; z-index: 1; max-width: min(180px, 80%); border: 1px solid #566d25; border-radius: 6px; padding: 5px 7px; background: #202615; color: #e8edf0; font-size: 11px; font-weight: 850; font-variant-numeric: tabular-nums; line-height: 1.25; pointer-events: none; box-shadow: 0 8px 20px rgba(0, 0, 0, .28); white-space: nowrap; }
    174     .metrics-empty { border: 1px dashed #3a4248; border-radius: 8px; padding: 14px; color: #8d989f; background: #111316; }
    175     .leaderboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 12px; }
    176     .leaderboard-card { min-width: 0; border: 1px solid #2a3035; border-radius: 8px; padding: 12px; background: #181b1f; }
    177     .leaderboard-card h3 { margin: 0 0 10px; }
    178     .leaderboard-list { display: grid; gap: 8px; }
    179     .leaderboard-row { display: grid; grid-template-columns: 54px minmax(0, 1fr) auto; gap: 10px; align-items: center; border: 1px solid #2f363c; border-radius: 8px; padding: 9px; background: #111316; }
    180     .leaderboard-rank { display: grid; place-items: center; min-height: 30px; border: 1px solid #3a4248; border-radius: 999px; color: #a8b2b8; font-size: 11px; font-weight: 900; }
    181     .leaderboard-rank.medal-1 { border-color: #ffd070; background: #2d2513; color: #ffd070; }
    182     .leaderboard-rank.medal-2 { border-color: #c7d0d5; background: #20252a; color: #e8edf0; }
    183     .leaderboard-rank.medal-3 { border-color: #c69262; background: #2a1f17; color: #ffc18a; }
    184     .leaderboard-main { min-width: 0; display: grid; gap: 3px; }
    185     .leaderboard-amount { color: #d5f55f; font-size: 13px; font-weight: 900; font-variant-numeric: tabular-nums; white-space: nowrap; }
    186     .wallet-grid { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, .8fr); gap: 12px; align-items: start; }
    187     .wallet-actions { display: grid; gap: 12px; }
    188     .advanced-toggle { flex-basis: 100%; width: max-content; align-self: flex-start; border-color: #3a4248; padding: 4px 7px; background: #202328; color: #9fa8ad; font-size: 12px; }
    189     .advanced-toggle:hover { border-color: #5a646b; color: #d6dee2; }
    190     .send-utxo-list { display: grid; gap: 8px; max-height: 260px; overflow: auto; border: 1px solid #2f363c; border-radius: 8px; padding: 8px; background: #111316; }
    191     .send-utxo-list-head { display: flex; justify-content: space-between; gap: 8px; align-items: center; color: #8d989f; font-size: 12px; font-weight: 800; }
    192     .send-utxo-actions { display: flex; gap: 6px; align-items: center; }
    193     .utxo-select-button { padding: 3px 7px; border-color: #3a4248; background: #202328; color: #9fa8ad; font-size: 12px; }
    194     .utxo-select-button:hover { border-color: #5a646b; color: #d6dee2; }
    195     .send-utxo-option { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px; align-items: start; border: 1px solid #2f363c; border-radius: 8px; padding: 8px; background: #181b1f; user-select: none; }
    196     .send-utxo-option.disabled { border-color: #262c31; background: #14171a; color: #687178; }
    197     .send-utxo-option.disabled code, .send-utxo-option.disabled .utxo-node-amount { color: #687178; }
    198     .send-utxo-option input { min-width: auto; margin-top: 3px; pointer-events: none; }
    199     .utxo-status { color: #8d989f; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    200     .send-utxo-summary { flex-basis: 100%; width: 100%; display: grid; gap: 5px; color: #9eb3bc; font-size: 13px; }
    201     .wallet-balance-line { display: inline-grid; grid-template-columns: auto auto; gap: 10px; align-items: baseline; padding: 8px 10px; border: 1px solid #2f363c; border-radius: 8px; background: #111316; color: inherit; cursor: pointer; }
    202     .wallet-balance-line:hover, .wallet-balance-line:focus-visible { border-color: #d5f55f; outline: none; }
    203     .wallet-balance-line .tx-value { font-size: 16px; font-weight: 850; }
    204     .mining-grid { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: start; }
    205     .panel-description { max-width: 760px; margin: -4px 0 12px; color: #9eb3bc; font-size: 13px; line-height: 1.45; }
    206     .mining-form { width: 100%; display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
    207     .burn-fields { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
    208     .mine-action-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 12px; align-items: center; }
    209     .mine-settings-form { display: grid; gap: 10px; }
    210     .mine-fee-fields { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
    211     .fee-preview { flex-basis: 100%; color: #9eb3bc; font-size: 12px; font-weight: 700; }
    212     .mine-stats { display: grid; grid-template-columns: repeat(4, minmax(112px, 1fr)); gap: 8px; min-width: 0; }
    213     .local-mining-stats { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
    214     .fee-history { grid-template-columns: repeat(3, minmax(112px, 1fr)); margin-top: 12px; }
    215     .mine-stat { min-width: 0; border: 1px solid #2f363c; border-radius: 8px; padding: 9px 10px; background: #111316; }
    216     .mine-stat-label { display: flex; gap: 5px; align-items: center; color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    217     .mine-stat-value { margin-top: 5px; color: #dce4e7; font-size: 14px; font-weight: 850; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
    218     .mine-stat-value.money { color: #d5f55f; }
    219     .mine-reward-control { display: grid; gap: 7px; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; }
    220     .mine-reward-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; color: #a8b2b8; font-size: 13px; font-weight: 800; }
    221     .mine-reward-head strong { color: #d5f55f; font-size: 14px; font-variant-numeric: tabular-nums; }
    222     .mine-reward-control input[type="range"] { width: 100%; min-width: 0; padding: 0; accent-color: #d5f55f; }
    223     .mine-slider-hints { display: flex; justify-content: space-between; gap: 10px; color: #879198; font-size: 11px; font-weight: 750; }
    224     .mine-include-status { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 7px; color: #9eb3bc; font-size: 12px; font-weight: 800; }
    225     .mine-include-status.waiting { color: #ffd280; }
    226     .mine-include-status.ready { color: #d5f55f; }
    227     .mine-include-status.muted { color: #879198; }
    228     .mine-save-row { display: flex; justify-content: flex-start; }
    229     .mining-event-log { display: grid; gap: 8px; max-height: 360px; overflow: auto; margin-top: 12px; border: 1px solid #2f363c; border-radius: 8px; padding: 8px; background: #0f1114; }
    230     .mining-event-log-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    231     .mining-event { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px; align-items: start; border: 1px solid #30383d; border-radius: 8px; padding: 10px; background: #111316; }
    232     .mining-event-dot { width: 8px; height: 8px; margin-top: 5px; border-radius: 999px; background: #7f888e; }
    233     .mining-event.active .mining-event-dot { background: #d5f55f; box-shadow: 0 0 12px rgba(213, 245, 95, .45); }
    234     .mining-event.warning .mining-event-dot { background: #ffd070; }
    235     .mining-event.info .mining-event-dot { background: #8de9cd; }
    236     .mining-event-title { color: #eef6f8; font-weight: 850; overflow-wrap: anywhere; }
    237     .mining-event-detail { margin-top: 3px; color: #9fa8ad; font-size: 12px; line-height: 1.35; overflow-wrap: anywhere; }
    238     .mining-event-time { color: #7f888e; font-size: 11px; font-weight: 800; white-space: nowrap; }
    239     .mining-event-empty { height: 42px; border: 1px solid #30383d; border-radius: 8px; background: #111316; }
    240     .mining-event-empty .skeleton-line { width: 100%; height: 100%; border-radius: 8px; opacity: .55; }
    241     .panel-separator { border-top: 1px solid #2f363c; margin: 14px 0 12px; }
    242     .stratum-config { display: grid; gap: 10px; }
    243     .stratum-note { max-width: 760px; color: #9eb3bc; font-size: 12px; line-height: 1.45; }
    244     .stratum-note code { color: #dce4e7; }
    245     .stratum-fields { display: grid; gap: 8px; }
    246     .stratum-field { display: grid; grid-template-columns: 86px minmax(0, 1fr); gap: 10px; align-items: baseline; min-width: 0; }
    247     .stratum-label { color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    248     .stratum-value { min-width: 0; color: #dce4e7; font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
    249     .stratum-value.hash { color: #9eb3bc; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 500; }
    250     .info-button { display: inline-grid; place-items: center; width: 18px; height: 18px; padding: 0; border-radius: 999px; border-color: #3a4248; background: #181b1f; color: #9eb3bc; font-size: 11px; line-height: 1; }
    251     .info-button:hover, .info-button:focus-visible { border-color: #d5f55f; color: #d5f55f; outline: none; }
    252     .info-copy { display: grid; gap: 10px; color: #c3cbd0; line-height: 1.45; }
    253     .info-copy p { margin: 0; }
    254     .info-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
    255     .info-fact { border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; }
    256     .info-fact .label { color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    257     .info-fact .value { margin-top: 5px; color: #d5f55f; font-weight: 850; }
    258     .mining-head { align-items: center; }
    259     .toggle-switch { display: inline-flex; grid-template-columns: none; align-items: center; gap: 9px; color: #9fa8ad; font-size: 12px; font-weight: 850; cursor: pointer; user-select: none; }
    260     .toggle-switch input { position: absolute; width: 1px; height: 1px; min-width: 0; margin: 0; opacity: 0; pointer-events: none; }
    261     .toggle-track { position: relative; width: 46px; height: 26px; border: 1px solid #3a4248; border-radius: 999px; background: #101215; transition: background .16s ease, border-color .16s ease; }
    262     .toggle-thumb { position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 999px; background: #879198; transition: transform .16s ease, background .16s ease; }
    263     .toggle-switch.active { color: #d5f55f; }
    264     .toggle-switch.active .toggle-track { border-color: #d5f55f; background: #263219; }
    265     .toggle-switch.active .toggle-thumb { transform: translateX(20px); background: #d5f55f; }
    266     .toggle-switch:focus-within .toggle-track { outline: 2px solid #d5f55f; outline-offset: 2px; }
    267     .toggle-text { min-width: 22px; text-align: right; }
    268     .compact-number-field { display: inline-flex; align-items: center; gap: 8px; color: #9fa8ad; font-size: 12px; font-weight: 850; }
    269     .compact-number-field input { width: 58px; min-width: 0; border: 1px solid #3a4248; border-radius: 8px; padding: 7px 8px; background: #101215; color: #dce4e7; font: inherit; font-variant-numeric: tabular-nums; }
    270     .compact-number-field input:focus { border-color: #d5f55f; outline: 2px solid rgba(213,245,95,.2); outline-offset: 2px; }
    271     .receive-address { display: grid; gap: 8px; }
    272     .address-box { border: 1px solid #2f363c; border-radius: 8px; padding: 11px; background: #111316; }
    273     .address-book-list { display: grid; gap: 8px; margin-top: 12px; }
    274     .address-book-row { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; text-align: left; border: 1px solid #2f363c; border-radius: 8px; padding: 9px; background: #111316; color: inherit; }
    275     .address-book-row:hover { border-color: #4c565c; background: #15181b; }
    276     .address-book-row > svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; color: #9fa8ad; }
    277     .address-book-name { font-weight: 800; color: #eef6f8; overflow-wrap: anywhere; }
    278     .address-book-actions { display: flex; gap: 6px; align-items: center; }
    279     .address-book-modal { width: min(560px, 100%); }
    280     .address-book-modal form { width: 100%; display: grid; gap: 10px; }
    281     .address-book-form { display: grid; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid #2f363c; }
    282     .address-book-modal-actions { flex: 1 0 100%; width: 100%; display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
    283     .address-book-picker-list { display: grid; gap: 8px; }
    284     .address-book-picker-row { width: 100%; display: grid; gap: 3px; text-align: left; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; color: inherit; }
    285     .address-book-picker-row:hover { border-color: #4c565c; background: #15181b; }
    286     .recipient-field { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: end; }
    287     .icon-button { display: inline-grid; place-items: center; width: 38px; height: 38px; padding: 0; line-height: 0; border-radius: 8px; }
    288     .icon-button svg { width: 19px; height: 19px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
    289     .modal-delete-button { color: #ffb4b4; }
    290     input.invalid { border-color: #e36a6a; outline: 2px solid rgba(227,106,106,.16); outline-offset: 2px; }
    291     .panel-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; margin-bottom: 12px; }
    292     .panel-head h2, .panel-head h3 { margin-bottom: 0; }
    293     .switch { display: inline-flex; grid-template-columns: none; align-items: center; gap: 8px; color: #d6dee2; font-weight: 700; }
    294     .switch input { width: auto; min-width: 0; accent-color: #d5f55f; }
    295     .wallet-tx-panel { min-width: 0; overflow: hidden; }
    296     .wallet-tx-panel .panel-head { flex-wrap: wrap; }
    297     .wallet-tx-filters { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; align-items: center; }
    298     .tx-filter { display: inline-flex; align-items: center; gap: 6px; border: 1px solid #3a4248; border-radius: 999px; padding: 4px 8px; color: #9fa8ad; background: #111316; font-size: 12px; font-weight: 850; cursor: pointer; user-select: none; }
    299     .tx-filter input { position: absolute; width: 1px; height: 1px; min-width: 0; margin: 0; opacity: 0; pointer-events: none; }
    300     .tx-filter.active { border-color: #d5f55f; background: #202616; color: #d5f55f; }
    301     .tx-filter:focus-within { outline: 2px solid #d5f55f; outline-offset: 2px; }
    302     .wallet-tx-list { max-height: min(620px, calc(100vh - 220px)); min-width: 0; display: grid; gap: 8px; overflow-y: auto; overscroll-behavior-y: contain; padding-right: 4px; }
    303     .wallet-tx-row { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; align-items: start; border: 1px solid #2f363c; border-radius: 8px; padding: 12px; background: #111316; cursor: pointer; text-align: left; }
    304     .wallet-tx-row:hover, .wallet-tx-row:focus-visible, .tx-card:hover, .tx-card:focus-visible, .mempool-item:hover, .mempool-item:focus-visible { border-color: #d5f55f; box-shadow: 0 0 0 1px rgba(213, 245, 95, .22); outline: none; }
    305     .wallet-tx-row.pending { border-color: #3a4147; background: #191c20; box-shadow: inset 3px 0 0 #6f7880; }
    306     .wallet-tx-row .pill { position: absolute; top: 10px; right: 10px; }
    307     .wallet-tx-main { display: grid; gap: 5px; min-width: 0; padding-right: 92px; }
    308     .tx-field { display: grid; grid-template-columns: 74px minmax(0, 1fr); gap: 8px; align-items: baseline; min-width: 0; }
    309     .tx-label { color: #879198; font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: 0; }
    310     .tx-value { min-width: 0; color: #dce4e7; font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
    311     .tx-value.money { color: #d5f55f; font-variant-numeric: tabular-nums; }
    312     .tx-value.hash { color: #9eb3bc; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 500; }
    313     .tx-value.number { color: #c7d0d5; font-variant-numeric: tabular-nums; }
    314     .tx-value.text { color: #e8edf0; }
    315     .metric-context { display: grid; gap: 5px; margin-top: 12px; }
    316     .peer-toolbar { display: flex; justify-content: space-between; gap: 12px; align-items: start; flex-wrap: wrap; margin-bottom: 12px; }
    317     .peer-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 8px; margin-bottom: 12px; }
    318     .peer-summary-item { min-width: 0; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; }
    319     .peer-summary-label { color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    320     .peer-summary-value { margin-top: 5px; color: #dce4e7; font-size: 15px; font-weight: 850; }
    321     .peer-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
    322     .peer-form label { min-width: min(320px, 100%); }
    323     .peer-form input { width: 100%; }
    324     .peer-status { display: inline-flex; align-items: center; border: 1px solid #3a4248; border-radius: 999px; padding: 3px 8px; color: #a8b2b8; font-size: 11px; font-weight: 850; }
    325     .peer-status.synced, .peer-status.active { border-color: #566d25; color: #d5f55f; background: #1c2516; }
    326     .peer-status.stale { border-color: #5f5125; color: #ffe08a; background: #211d12; }
    327     .peer-status.banned { border-color: #713434; color: #ffb1a8; background: #2a1717; }
    328     .peer-status.error { border-color: #713434; color: #ffb1a8; background: #2a1717; }
    329     .peer-actions { display: flex; gap: 6px; align-items: center; }
    330     .peer-remove { padding: 4px 7px; border-color: #4f3737; background: #221717; color: #ffb1a8; font-size: 12px; }
    331     .peer-remove:hover { border-color: #ffb1a8; color: #ffd4cf; }
    332     .network-health { display: grid; grid-template-columns: minmax(180px, .8fr) minmax(0, 1.2fr); gap: 12px; align-items: stretch; margin-bottom: 12px; }
    333     .network-health-state { display: grid; align-content: center; gap: 5px; border: 1px solid #3a4248; border-radius: 8px; padding: 12px; background: #111316; }
    334     .network-health-state.healthy { border-color: #566d25; background: #182112; }
    335     .network-health-state.syncing, .network-health-state.stale { border-color: #5f5125; background: #211d12; }
    336     .network-health-state.isolated, .network-health-state.error, .network-health-state.banned { border-color: #713434; background: #241716; }
    337     .network-health-label { color: #879198; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    338     .network-health-value { color: #e8edf0; font-size: 20px; font-weight: 900; text-transform: capitalize; }
    339     .network-health-detail { color: #a8b2b8; font-size: 12px; }
    340     .network-health-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(116px, 1fr)); gap: 8px; }
    341     .panel .grid + form { margin-top: 12px; }
    342     .explorer-shell { width: 100%; display: grid; gap: 12px; }
    343     .block-rail-wrap { background: #181b1f; border: 1px solid #2a3035; border-radius: 8px; padding: 12px; overflow: hidden; }
    344     .block-rail-head { display: flex; justify-content: space-between; gap: 10px; align-items: center; margin-bottom: 10px; }
    345     .block-rail { display: flex; gap: 8px; overflow-x: auto; padding: 1px 0 10px; scroll-snap-type: x proximity; }
    346     .block-card { flex: 0 0 122px; min-height: 100px; display: grid; gap: 6px; border: 1px solid #2f363c; border-radius: 8px; padding: 9px; background: #111316; color: #e8edf0; text-align: left; scroll-snap-align: start; }
    347     .block-card:hover { border-color: #d5f55f; color: #d5f55f; }
    348     .block-card.selected { background: #202616; border-color: #d5f55f; box-shadow: inset 0 0 0 1px #d5f55f; }
    349     .block-card.new-block { animation: block-arrive .45s ease both; }
    350     @keyframes block-arrive { from { opacity: .2; transform: translateX(-12px); } to { opacity: 1; transform: translateX(0); } }
    351     .block-height { font-size: 18px; font-weight: 900; }
    352     .block-meta { display: flex; gap: 8px; color: #8d989f; font-size: 12px; }
    353     .block-miner { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; overflow-wrap: anywhere; color: #9eb3bc; }
    354     .block-skeleton-group { flex: 0 0 auto; display: flex; gap: 8px; }
    355     .block-card-skeleton { grid-template-rows: 22px 18px minmax(28px, 1fr); align-items: start; cursor: default; }
    356     .skeleton-block-height { width: 46px; height: 22px; border-radius: 6px; background: #30383d; }
    357     .skeleton-block-meta { display: flex; gap: 6px; min-width: 0; }
    358     .skeleton-pill { flex: 0 0 auto; width: 18px; height: 14px; border-radius: 999px; background: #2b3136; }
    359     .skeleton-pill.wide { width: 28px; }
    360     .skeleton-block-miner { align-self: stretch; min-height: 28px; border-radius: 6px; background: #242a2f; }
    361     .skeleton-card { pointer-events: none; position: relative; overflow: hidden; }
    362     .skeleton-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(213, 245, 95, .12), transparent); animation: skeleton-sweep 1.15s ease-in-out infinite; }
    363     @keyframes skeleton-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
    364     .skeleton-line { height: 12px; border-radius: 6px; background: #2b3136; }
    365     .skeleton-line.short { width: 42%; }
    366     .skeleton-line.medium { width: 68%; }
    367     .skeleton-line.long { width: 88%; }
    368     .page-sentinel { min-height: 1px; }
    369     .block-page-sentinel { flex: 0 0 1px; min-height: 100px; }
    370     .dataset-loader { display: grid; gap: 8px; min-width: 0; }
    371     .skeleton-table-cell { height: 12px; width: 100%; border-radius: 6px; background: #2b3136; }
    372     tr.skeleton-card td { padding-top: 11px; padding-bottom: 11px; }
    373     .detail-grid { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: 12px; }
    374     .detail-kv { display: grid; grid-template-columns: 90px minmax(0, 1fr); gap: 8px; font-size: 13px; margin: 7px 0; }
    375     .detail-kv .key { color: #8d989f; }
    376     .detail-link { width: fit-content; max-width: 100%; padding: 0; border: 0; background: transparent; color: #d7f2ff; font: inherit; text-align: left; cursor: pointer; }
    377     .detail-link code { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
    378     .fee-penalty-value.penalty { color: #ffb1a8; font-weight: 900; }
    379     .rank-list { display: grid; gap: 8px; }
    380     .rank-row { display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 10px; align-items: start; border: 1px solid #30383d; border-radius: 8px; padding: 10px; background: #15191d; }
    381     .rank-number { color: #d7f2ff; font-weight: 700; }
    382     .rank-details { display: grid; gap: 6px; min-width: 0; }
    383     .tx-list { display: grid; gap: 8px; }
    384     .tx-section { display: grid; gap: 8px; }
    385     .tx-section + .tx-section { margin-top: 10px; padding-top: 10px; border-top: 1px solid #2f363c; }
    386     .tx-section-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; color: #f4f7f8; font-size: 13px; font-weight: 800; }
    387     summary.tx-section-title { cursor: pointer; }
    388     details.tx-section:not([open]) { gap: 0; }
    389     .tx-section-meta { color: #8e979e; font-size: 12px; font-weight: 600; }
    390     .tx-card, .mempool-item { position: relative; display: grid; align-content: start; grid-auto-rows: min-content; gap: 6px; border: 1px solid #2f363c; border-radius: 8px; padding: 12px; background: #111316; cursor: pointer; text-align: left; }
    391     .mempool-item.before-last-block { opacity: .56; }
    392     .mempool-item.new-since-block { background: #151a12; opacity: 1; }
    393     .mempool-item.new-since-block::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; border-radius: 8px 0 0 8px; background: #d5f55f; }
    394     .mempool-item.blinded-hidden { background: linear-gradient(135deg, #141218, #101316); border-color: #353040; }
    395     .mempool-state { color: #d5f55f; font-size: 10px; font-weight: 850; text-transform: uppercase; }
    396     .mempool-time { color: #8d989f; font-size: 11px; font-weight: 700; }
    397     .mempool-top { display: flex; justify-content: space-between; gap: 8px; align-items: flex-start; min-width: 0; }
    398     .mempool-top-meta { display: grid; gap: 3px; min-width: 0; }
    399     .tx-card .pill, .mempool-item .pill { position: absolute; top: 10px; right: 10px; }
    400     .mempool-item .pill { position: static; flex: 0 0 auto; }
    401     .pill { display: inline-flex; align-items: center; border-radius: 999px; padding: 2px 8px; font-size: 12px; font-weight: 800; background: #2b3136; color: #d6dee2; }
    402     .pill.burn { background: #332918; color: #ffd070; }
    403     .pill.transfer { background: #17312a; color: #8de9cd; }
    404     .pill.mine { background: #172a34; color: #8bdcff; }
    405     .pill.blinded { background: #272433; color: #c8b8ff; }
    406     .pill.reveal, .pill.revealed { background: #2b2f20; color: #d5f55f; }
    407     .pill.error { background: #341918; color: #ffb1a8; }
    408     .mempool-panel { min-width: 0; overflow: hidden; }
    409     .mempool-strip { width: 100%; min-width: 0; display: flex; gap: 8px; overflow-x: auto; overscroll-behavior-x: contain; padding: 1px 0 10px; scroll-snap-type: x proximity; }
    410     .mempool-item { flex: 0 0 220px; scroll-snap-align: start; align-self: stretch; }
    411     .tx-modal { width: min(940px, 100%); max-height: calc(100vh - 44px); overflow: auto; border: 1px solid #3b4448; border-radius: 8px; padding: 16px; background: #181b1f; box-shadow: 0 24px 80px rgba(0, 0, 0, .46); }
    412     .tx-modal-head { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; margin-bottom: 14px; }
    413     .tx-modal-title { display: grid; justify-items: start; gap: 6px; min-width: 0; }
    414     .tx-modal-title h2 { margin: 0; }
    415     .tx-modal-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px; margin-bottom: 12px; }
    416     .utxo-flow { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 12px; align-items: stretch; }
    417     .utxo-column { display: grid; align-content: start; gap: 8px; min-width: 0; }
    418     .utxo-column h3 { margin: 0; color: #8d989f; font-size: 11px; text-transform: uppercase; }
    419     .utxo-node { display: grid; gap: 5px; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; min-width: 0; }
    420     .utxo-node.burned { border-color: #5e4821; background: #1f1a12; }
    421     .utxo-node.fee { border-color: #4b5260; background: #171a20; }
    422     .utxo-node-label { display: flex; justify-content: space-between; gap: 8px; color: #8d989f; font-size: 11px; font-weight: 800; text-transform: uppercase; }
    423     .utxo-node-amount { color: #d5f55f; font-weight: 850; font-variant-numeric: tabular-nums; }
    424     .utxo-node-address, .utxo-node-ref { color: #9eb3bc; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; overflow-wrap: anywhere; }
    425     .utxo-arrow { display: grid; place-items: center; color: #d5f55f; font-size: 24px; font-weight: 900; }
    426     .tx-modal-empty { border: 1px dashed #3a4248; border-radius: 8px; padding: 10px; color: #8d989f; }
    427     .utxo-list { display: grid; gap: 8px; }
    428     .wallet-utxo-row { display: grid; gap: 6px; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; }
    429     @media (max-width: 760px) { .utxo-flow, .mine-action-row, .mine-stats { grid-template-columns: 1fr; } .utxo-arrow { min-height: 28px; transform: rotate(90deg); } .tx-modal-head { align-items: stretch; } }
    430     @media (max-width: 920px) { .setup-grid, .wallet-grid, .mining-grid, .detail-grid, .network-health { grid-template-columns: 1fr; } }
    431     @media (max-width: 760px) {
    432       .app-shell { display: grid; grid-template-columns: 1fr; }
    433       .sidebar { position: sticky; inset: auto; width: auto; height: auto; flex-direction: row; justify-content: space-between; padding: 8px; border-right: 0; border-bottom: 1px solid #262b2f; }
    434       .brand-mark { width: 34px; height: 34px; }
    435       .brand-mark svg { width: 22px; height: 22px; }
    436       .side-nav { display: flex; width: auto; gap: 8px; }
    437       .nav-button { width: 52px; min-height: 48px; }
    438       .nav-button span { font-size: 10px; }
    439       .settings-button, .version-panel { margin-top: 0; width: 48px; min-height: 48px; padding: 6px 3px; }
    440       .settings-button svg { width: 21px; height: 21px; }
    441       .content { padding: 16px 12px 36px; }
    442       header, .split, .setup-grid, .wallet-grid, .mining-grid, .detail-grid, .wallet-tx-row { grid-template-columns: 1fr; }
    443       header { display: grid; }
    444       .settings-mode-row { align-items: flex-start; }
    445       .metrics-head { align-items: flex-start; flex-direction: column; }
    446       .metrics-range { width: 100%; }
    447       .metrics-range button { flex: 1 1 0; }
    448       .segmented.setup-mode-picker { grid-template-columns: 1fr; }
    449       .metrics-grid { grid-template-columns: 1fr; }
    450       input { min-width: 0; width: 100%; }
    451       .switch input { width: auto; }
    452       .seed-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    453       .block-card { flex-basis: 108px; }
    454     }
    455   </style>
    456   <script defer src="/assets/iuna-ui.js?v=103"></script>
    457   <script defer src="/assets/alpine.min.js"></script>
    458 </head>
    459 <body x-data="iunaApp()" x-init="init()" @keydown.window.escape="closeModals()" x-cloak>
    460   <div class="app-shell">
    461     <aside class="sidebar" aria-label="iuna navigation">
    462       <div class="brand-mark" title="iuna" aria-label="iuna"><svg viewBox="0 0 32 32" aria-hidden="true" focusable="false"><circle class="mark-dot" cx="9.4" cy="7.6" r="2.8"></circle><path class="mark-loop" d="M9.4 13v7.1c0 3.7 2.9 6.4 6.6 6.4s6.6-2.7 6.6-6.4V13"></path></svg></div>
    463       <nav class="side-nav">
    464         <button class="nav-button" :class="{ active: tab === 'wallet' }" @click="setTab('wallet')" type="button" title="Wallet" aria-label="Wallet">
    465           <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 7h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3z"></path><path d="M3 7V5a2 2 0 0 1 2-2h12"></path><path d="M16 13h3"></path></svg>
    466           <span>Wallet</span>
    467         </button>
    468         <button class="nav-button" x-show="advancedMode()" :class="{ active: tab === 'mining' }" @click="setTab('mining')" type="button" title="Mining" aria-label="Mining">
    469           <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 19V5"></path><path d="M4 19h16"></path><path d="M7 15l4-4 3 3 5-7"></path></svg>
    470           <span>Mining</span>
    471         </button>
    472         <button class="nav-button" :class="{ active: tab === 'p2p' }" @click="setTab('p2p')" type="button" title="P2P" aria-label="P2P">
    473           <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="6" r="3"></circle><circle cx="18" cy="18" r="3"></circle><path d="M8.5 10.5 15.5 7.5"></path><path d="M8.5 13.5 15.5 16.5"></path></svg>
    474           <span>P2P</span>
    475         </button>
    476         <button class="nav-button" :class="{ active: tab === 'chain' }" @click="setTab('chain')" type="button" title="Explorer" aria-label="Explorer">
    477           <svg class="chain-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="1.5" y="9" width="5.5" height="5.5"></rect><rect x="9.25" y="9" width="5.5" height="5.5"></rect><rect x="17" y="9" width="5.5" height="5.5"></rect></svg>
    478           <span>Chain</span>
    479         </button>
    480         <button class="nav-button" x-show="developmentMode()" :class="{ active: tab === 'metrics' }" @click="setTab('metrics')" type="button" title="Metrics" aria-label="Metrics">
    481           <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 19V5"></path><path d="M4 19h16"></path><path d="M7 15l3-4 3 2 4-7"></path><path d="M7 17h10"></path></svg>
    482           <span>Metrics</span>
    483         </button>
    484       </nav>
    485       <button class="settings-button" :class="{ active: tab === 'settings' }" type="button" @click="setTab('settings')" title="Settings" aria-label="Settings">
    486         <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.7 3.2 9.2 5.5a7.2 7.2 0 0 0-1.4.8L5.6 5.6 3.2 9.8l1.7 1.6a7.8 7.8 0 0 0 0 1.6l-1.7 1.6 2.4 4.2 2.2-.7a7.2 7.2 0 0 0 1.4.8l.5 2.3h4.8l.5-2.3a7.2 7.2 0 0 0 1.4-.8l2.2.7 2.4-4.2-1.7-1.6a7.8 7.8 0 0 0 0-1.6L21 9.8l-2.4-4.2-2.2.7a7.2 7.2 0 0 0-1.4-.8l-.5-2.3H9.7Z"></path><circle cx="12" cy="12.2" r="3.1"></circle></svg>
    487       </button>
    488       <button class="version-panel" type="button" :class="{ update: updateAvailable(), checking: releaseCheckState === 'checking', failed: releaseCheckState === 'failed' }" :title="versionPanelTitle()" @click="openLatestRelease">
    489         <span class="version-dot" aria-hidden="true"></span>
    490         <span class="version-label" x-text="appVersionLabel()"></span>
    491         <span class="version-update" x-show="updateAvailable()">Update</span>
    492       </button>
    493     </aside>
    494 
    495     <main class="content">
    496     <header>
    497       <div>
    498         <h1 x-text="pageTitle()">iuna</h1>
    499         <div class="basic-status-row" x-show="basicMode()">
    500           <span class="basic-status" :class="networkHealthClass()" x-text="basicNetworkStatusLabel()"></span>
    501           <button class="basic-status-detail" type="button" x-show="basicNetworkNeedsAttention()" @click="setTab('p2p')">Details</button>
    502         </div>
    503       </div>
    504       <div class="header-actions">
    505         <div class="muted" x-text="lastUpdatedLabel()"></div>
    506         <button class="lock-button" type="button" x-show="auth.authenticated" @click="logout">Lock</button>
    507       </div>
    508     </header>
    509 
    510     <div class="flash" :class="flash?.kind" x-show="flash" x-transition x-text="flash?.message"></div>
    511     <div class="persistent-banner" x-show="p2pRestartRequired()" x-transition x-text="p2pRestartMessage()"></div>
    512 
    513     <section x-show="tab === 'wallet'">
    514       <div class="page-title">
    515         <button class="wallet-balance-line" type="button" @click="openWalletUtxosModal" title="Show wallet UTXOs">
    516           <span class="tx-label">Balance</span>
    517           <span class="tx-value money">IUNA <span x-text="amountLabel(status.wallet_balance)"></span></span>
    518         </button>
    519       </div>
    520       <div class="wallet-grid">
    521         <div class="wallet-actions">
    522           <div class="panel">
    523             <h3>Send</h3>
    524             <form @submit.prevent="sendTransfer">
    525               <div class="recipient-field">
    526                 <label>Recipient<input x-model="transferTo" @input="scheduleFeeEstimates" autocomplete="off" required></label>
    527                 <button class="icon-button" type="button" @click="openAddressBookPicker()" title="Choose contact" aria-label="Choose contact">
    528                   <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5.5A2.5 2.5 0 0 1 6.5 3H20v18H6.5A2.5 2.5 0 0 1 4 18.5z"></path><path d="M8 7h8"></path><path d="M8 11h6"></path><path d="M8 15h4"></path></svg>
    529                 </button>
    530               </div>
    531               <label>Amount<input x-model="transferAmount" @input="scheduleFeeEstimates" type="number" min="0.000001" step="0.000001" required></label>
    532               <label>Fee / byte<input x-model="transferFee" @input="scheduleFeeEstimates" type="number" min="0" step="0.000001" required></label>
    533               <div class="fee-preview" x-text="feeEstimateLabel('transfer')"></div>
    534               <button class="advanced-toggle" type="button" @click="toggleSendAdvanced" x-text="showSendAdvanced ? 'Hide UTXOs' : 'UTXOs'"></button>
    535               <div class="send-utxo-summary" x-show="showSendAdvanced">
    536                 <div>Selected UTXOs: <span x-text="selectedTransferUtxos.length"></span></div>
    537                 <div>Selected total: IUNA <span x-text="amountLabel(selectedTransferUtxoTotal())"></span></div>
    538                 <div>Required: IUNA <span x-text="amountLabel(transferRequiredTotal())"></span></div>
    539                 <div class="setup-feedback error" x-show="!selectedTransferUtxosCoverTransfer()">Selected UTXOs do not cover amount plus fee</div>
    540                 <div class="send-utxo-list">
    541                   <div class="send-utxo-list-head">
    542                     <span>UTXOs</span>
    543                     <span class="send-utxo-actions">
    544                       <button class="utxo-select-button" type="button" @click="selectAllTransferUtxos" :disabled="walletUtxoPage.loading && walletUtxos.length === 0">Select all</button>
    545                       <button class="utxo-select-button" type="button" @click="clearTransferUtxos" :disabled="selectedTransferUtxos.length === 0">None</button>
    546                     </span>
    547                   </div>
    548                   <template x-for="utxo in walletUtxos" :key="utxoOutpoint(utxo)">
    549                     <label class="send-utxo-option" :class="{ disabled: !utxo.spendable }" @click.prevent="toggleTransferUtxoSelection($event, utxo)">
    550                       <input type="checkbox" :value="utxoOutpoint(utxo)" :checked="selectedTransferUtxos.includes(utxoOutpoint(utxo))" :disabled="!utxo.spendable">
    551                       <span>
    552                         <span class="utxo-node-label"><span>UTXO</span><span class="utxo-node-amount">IUNA <span x-text="amountLabel(utxo.amount)"></span></span></span>
    553                         <span class="utxo-status" x-show="!utxo.spendable">Pending</span>
    554                         <code class="tx-value hash" x-text="utxoOutpoint(utxo)"></code>
    555                       </span>
    556                     </label>
    557                   </template>
    558                   <div class="dataset-loader" x-show="walletUtxoPage.loading" aria-hidden="true">
    559                     <div class="send-utxo-option skeleton-card"><span><span class="skeleton-line medium"></span><span class="skeleton-line long"></span></span></div>
    560                     <div class="send-utxo-option skeleton-card"><span><span class="skeleton-line short"></span><span class="skeleton-line long"></span></span></div>
    561                   </div>
    562                   <div class="page-sentinel" x-show="walletUtxoPage.hasMore" x-init="$nextTick(() => observePageSentinel('walletUtxo', $el))"></div>
    563                   <div class="tx-modal-empty" x-show="walletUtxos.length === 0 && !walletUtxoPage.loading">No UTXOs</div>
    564                 </div>
    565               </div>
    566               <button class="primary" type="submit">Send</button>
    567             </form>
    568           </div>
    569           <div class="panel">
    570             <div class="panel-head">
    571               <h3>Receive</h3>
    572               <button type="button" @click="copyAddress">Copy</button>
    573             </div>
    574             <div class="receive-address">
    575               <div class="muted">Public key / address</div>
    576               <div class="address-box"><code x-text="status.wallet_address || '-'"></code></div>
    577             </div>
    578           </div>
    579           <div class="panel">
    580             <div class="panel-head">
    581               <h3>Address Book</h3>
    582               <div class="address-book-actions">
    583                 <button class="icon-button" type="button" @click="openAddressBookModal()" title="Add contact" aria-label="Add contact">
    584                   <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"></path><path d="M5 12h14"></path></svg>
    585                 </button>
    586               </div>
    587             </div>
    588             <div class="address-book-list">
    589               <template x-for="entry in addressBookEntries()" :key="entry.address">
    590                 <button class="address-book-row" type="button" @click="editAddressBookEntry(entry)" :title="`Edit ${entry.name}`">
    591                   <div>
    592                     <div class="address-book-name" x-text="entry.name"></div>
    593                     <code class="tx-value hash" x-text="short(entry.address)"></code>
    594                   </div>
    595                   <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 18l6-6-6-6"></path></svg>
    596                 </button>
    597               </template>
    598               <div class="muted" x-show="addressBookEntries().length === 0">No saved addresses</div>
    599             </div>
    600           </div>
    601         </div>
    602         <div class="panel wallet-tx-panel">
    603           <div class="panel-head">
    604             <h3>Transactions</h3>
    605             <div class="wallet-tx-filters" aria-label="Transaction filters">
    606               <label class="tx-filter" :class="{ active: walletTxFilters.transfer }">
    607                 <input type="checkbox" x-model="walletTxFilters.transfer" @change="refreshWalletTransactions()">
    608                 <span>Tx</span>
    609               </label>
    610               <label class="tx-filter" :class="{ active: walletTxFilters.mine }">
    611                 <input type="checkbox" x-model="walletTxFilters.mine" @change="refreshWalletTransactions()">
    612                 <span>Mine</span>
    613               </label>
    614               <label class="tx-filter" :class="{ active: walletTxFilters.burn }">
    615                 <input type="checkbox" x-model="walletTxFilters.burn" @change="refreshWalletTransactions()">
    616                 <span>Burn</span>
    617               </label>
    618             </div>
    619           </div>
    620           <div class="wallet-tx-list">
    621             <template x-for="tx in walletTransactions()" :key="tx.status + '-' + tx.signature">
    622               <div class="wallet-tx-row" :class="{ pending: tx.status === 'pending' }" role="button" tabindex="0" @click="openTransactionModal(tx, { source: 'Wallet' })" @keydown.enter.prevent="openTransactionModal(tx, { source: 'Wallet' })" @keydown.space.prevent="openTransactionModal(tx, { source: 'Wallet' })">
    623                 <span class="pill" :class="tx.kind" x-text="tx.direction"></span>
    624                 <div class="wallet-tx-main">
    625                   <div class="tx-field"><span class="tx-label">Amount</span><span class="tx-value money">IUNA <span x-text="amountLabel(tx.amount)"></span></span></div>
    626                   <div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(tx)"></span></div>
    627                   <div class="tx-field"><span class="tx-label">Status</span><span class="tx-value text" x-text="txTitle(tx)"></span></div>
    628                   <div class="tx-field"><span class="tx-label">Time</span><span class="tx-value text" x-text="walletTxTimeLabel(tx)"></span></div>
    629                   <div class="tx-field"><span class="tx-label">From</span><code class="tx-value hash" x-text="shortAddressLabel(tx.from)"></code></div>
    630                   <div class="tx-field" x-show="tx.to"><span class="tx-label">To</span><code class="tx-value hash" x-text="shortAddressLabel(tx.to)"></code></div>
    631                   <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Bits</span><span class="tx-value number"><span x-text="txProofBits(tx) ?? '-'"></span> / <span x-text="txDifficultyBits(tx) ?? '-'"></span></span></div>
    632                   <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Hash</span><code class="tx-value hash" x-text="short(txProofHash(tx))"></code></div>
    633                   <div class="tx-field"><span class="tx-label">Signature</span><code class="tx-value hash" x-text="short(tx.signature)"></code></div>
    634                 </div>
    635               </div>
    636             </template>
    637             <div class="dataset-loader" x-show="walletTxPage.loading" aria-hidden="true">
    638               <div class="wallet-tx-row skeleton-card"><div class="wallet-tx-main"><div class="skeleton-line medium"></div><div class="skeleton-line short"></div><div class="skeleton-line long"></div></div></div>
    639               <div class="wallet-tx-row skeleton-card"><div class="wallet-tx-main"><div class="skeleton-line short"></div><div class="skeleton-line medium"></div><div class="skeleton-line long"></div></div></div>
    640             </div>
    641             <div class="page-sentinel" x-show="walletTxPage.hasMore" x-init="$nextTick(() => observePageSentinel('walletTx', $el))"></div>
    642             <div class="muted" x-show="walletTransactions().length === 0 && !walletTxPage.loading">No wallet transactions</div>
    643           </div>
    644         </div>
    645       </div>
    646     </section>
    647 
    648     <section x-show="tab === 'mining'">
    649       <div class="page-title">
    650         <div class="muted">PoB/VDF block production with PoW issuance actions</div>
    651       </div>
    652       <div class="mining-grid">
    653         <div class="panel">
    654           <h3>Status</h3>
    655           <div class="mine-stats local-mining-stats" aria-label="Local mining status">
    656             <div class="mine-stat">
    657               <div class="mine-stat-label">PoB State</div>
    658               <div class="mine-stat-value" x-text="pobStatusLabel()"></div>
    659             </div>
    660             <div class="mine-stat">
    661               <div class="mine-stat-label">PoB Detail</div>
    662               <div class="mine-stat-value" x-text="pobDetailLabel()" :title="pobDetailLabel()"></div>
    663             </div>
    664             <div class="mine-stat">
    665               <div class="mine-stat-label">PoW State</div>
    666               <div class="mine-stat-value" x-text="powStatusShortLabel()"></div>
    667             </div>
    668             <div class="mine-stat">
    669               <div class="mine-stat-label">Selected Finalizer</div>
    670               <code class="mine-stat-value" x-text="currentFinalizerLabel()"></code>
    671             </div>
    672             <div class="mine-stat">
    673               <div class="mine-stat-label">Mempool</div>
    674               <div class="mine-stat-value" x-text="localMiningMempoolLabel()"></div>
    675             </div>
    676           </div>
    677           <div class="mining-event-log" x-show="developmentMode()" aria-label="Mining event log">
    678             <div class="mining-event-log-head"><span>Event log</span><span x-text="`${miningEventLog().length} lines`"></span></div>
    679             <template x-if="miningEventLog().length === 0">
    680               <div class="mining-event-empty skeleton-card" aria-hidden="true"><div class="skeleton-line"></div></div>
    681             </template>
    682             <template x-for="event in miningEventLog()" :key="event.key">
    683               <div class="mining-event" :class="event.kind">
    684                 <span class="mining-event-dot" aria-hidden="true"></span>
    685                 <div>
    686                   <div class="mining-event-title" x-text="event.title"></div>
    687                   <div class="mining-event-detail" x-text="event.detail"></div>
    688                 </div>
    689                 <div class="mining-event-time" x-text="event.time"></div>
    690               </div>
    691             </template>
    692           </div>
    693         </div>
    694         <div class="panel">
    695           <div class="panel-head mining-head">
    696             <h3>Burn</h3>
    697             <label class="toggle-switch" :class="{ active: miningEnabled }">
    698               <input type="checkbox" :checked="miningEnabled" @change="setMiningEnabled($event.target.checked)">
    699               <span class="toggle-track" aria-hidden="true"><span class="toggle-thumb"></span></span>
    700               <span class="toggle-text" x-text="miningEnabled ? 'On' : 'Off'"></span>
    701             </label>
    702           </div>
    703           <div class="panel-description">Burn IUNA to compete for block finalization. Winning burns finalize PoB/VDF blocks and earn the transaction fees in those blocks.</div>
    704           <form class="mining-form" @submit.prevent="saveBurn">
    705             <div class="burn-fields">
    706               <label>IUNA per block<input x-model="burnAmountDraft" @input="burnAmountDirty = true; scheduleFeeEstimates()" type="number" min="0.000001" step="0.000001"></label>
    707               <label>Fee / byte<input x-model="burnFeeDraft" @input="burnAmountDirty = true; scheduleFeeEstimates()" type="number" min="0" step="0.000001" required></label>
    708               <button class="primary" type="submit">Save</button>
    709             </div>
    710             <div class="fee-preview" x-text="feeEstimateLabel('burn')"></div>
    711           </form>
    712           <div class="mine-stats fee-history" aria-label="Recent block fees">
    713             <div class="mine-stat">
    714               <div class="mine-stat-label">Last block fees</div>
    715               <div class="mine-stat-value money">IUNA <span x-text="amountLabel(recentBlockFeeAverage(1))"></span></div>
    716             </div>
    717             <div class="mine-stat">
    718               <div class="mine-stat-label">5 block avg</div>
    719               <div class="mine-stat-value money">IUNA <span x-text="amountLabel(recentBlockFeeAverage(5))"></span></div>
    720             </div>
    721             <div class="mine-stat">
    722               <div class="mine-stat-label">30 block avg</div>
    723               <div class="mine-stat-value money">IUNA <span x-text="amountLabel(recentBlockFeeAverage(30))"></span></div>
    724             </div>
    725           </div>
    726         </div>
    727         <div class="panel">
    728           <h3>Mine</h3>
    729           <div class="panel-description">Search for PoW actions that mint a fixed IUNA reward.</div>
    730           <div class="mine-settings-form">
    731             <div class="mine-action-row">
    732               <div class="mine-stats" aria-label="PoW issuance settings">
    733                 <div class="mine-stat">
    734                   <div class="mine-stat-label">You receive</div>
    735                   <div class="mine-stat-value money">IUNA <span x-text="amountLabel(powMineReward())"></span></div>
    736                 </div>
    737                 <div class="mine-stat">
    738                   <div class="mine-stat-label">Finalizer earns</div>
    739                   <div class="mine-stat-value">IUNA <span x-text="amountLabel(feeEstimates.mine?.fee ?? 0)"></span></div>
    740                 </div>
    741                 <div class="mine-stat">
    742                   <div class="mine-stat-label">Difficulty <button class="info-button" type="button" @click="openPowDifficultyInfo" title="How difficulty is adjusted" aria-label="How PoW difficulty is adjusted">i</button></div>
    743                   <div class="mine-stat-value"><span x-text="powDifficultyLabel()"></span> bits</div>
    744                 </div>
    745               </div>
    746               <label class="toggle-switch" :class="{ active: powMiningEnabled }" title="Continuously search for PoW mine actions with a small local work budget">
    747                 <input type="checkbox" :checked="powMiningEnabled" @change="setPowMiningEnabled($event.target.checked)">
    748                 <span class="toggle-track"><span class="toggle-thumb"></span></span>
    749                 <span class="toggle-text" x-text="powMiningEnabled ? 'On' : 'Off'"></span>
    750               </label>
    751               <label class="compact-number-field" title="Local PoW worker count">
    752                 <span>Workers</span>
    753                 <input type="number" min="1" :max="maxPowMiningWorkers" :value="powMiningWorkers" @change="setPowMiningWorkers($event.target.value)">
    754               </label>
    755             </div>
    756             <div class="fee-preview" x-text="autoPowStatusLabel()"></div>
    757           </div>
    758           <div class="panel-separator"></div>
    759           <div class="stratum-config">
    760             <div class="stratum-note">Start the node with <code>--stratum 0.0.0.0:3333</code> to expose a Stratum V1 endpoint for ASIC miners. Use the pool URL below in the miner configuration.</div>
    761             <div class="stratum-fields" aria-label="Stratum settings">
    762               <div class="stratum-field">
    763                 <div class="stratum-label">Status</div>
    764                 <div class="stratum-value" x-text="status.stratum?.enabled ? 'On' : 'Off'"></div>
    765               </div>
    766               <div class="stratum-field">
    767                 <div class="stratum-label">Listener</div>
    768                 <code class="stratum-value hash" x-text="stratumListenAddr()"></code>
    769               </div>
    770               <div class="stratum-field">
    771                 <div class="stratum-label">Pool URL</div>
    772                 <code class="stratum-value hash" x-text="stratumPoolUrl()"></code>
    773               </div>
    774             </div>
    775           </div>
    776         </div>
    777       </div>
    778     </section>
    779 
    780     <section x-show="tab === 'p2p'">
    781       <div class="panel">
    782         <div class="peer-toolbar">
    783           <div>
    784             <h2>Peers</h2>
    785             <div class="panel-description" x-text="p2pAcceptInbound ? 'Manage outbound peers and inspect inbound or outbound sessions. Public node is accepting inbound P2P connections.' : 'Manage outbound peers and inspect sync health. This node is outbound-only and does not open an inbound P2P port.'"></div>
    786           </div>
    787           <form class="peer-form" @submit.prevent="addPeer">
    788             <label>Peer address<input x-model="peerAddress" placeholder="seed.example:9444"></label>
    789             <button class="primary" type="submit">Add</button>
    790           </form>
    791         </div>
    792         <div class="network-health" x-show="developmentMode()">
    793           <div class="network-health-state" :class="networkHealthClass()">
    794             <div class="network-health-label">Network Health</div>
    795             <div class="network-health-value" x-text="networkHealth.state || '-'"></div>
    796             <div class="network-health-detail" x-text="networkHealth.last_error || 'No peer errors reported'"></div>
    797           </div>
    798           <div class="network-health-grid">
    799             <div class="peer-summary-item"><div class="peer-summary-label">Local Height</div><div class="peer-summary-value" x-text="networkHealth.local_height ?? '-'"></div></div>
    800             <div class="peer-summary-item"><div class="peer-summary-label">Best Known</div><div class="peer-summary-value" x-text="networkHealth.best_known_height ?? '-'"></div></div>
    801             <div class="peer-summary-item"><div class="peer-summary-label">Lag</div><div class="peer-summary-value" x-text="networkLagLabel()"></div></div>
    802             <div class="peer-summary-item"><div class="peer-summary-label">Stale</div><div class="peer-summary-value" x-text="networkHealth.stale_peers ?? '-'"></div></div>
    803             <div class="peer-summary-item"><div class="peer-summary-label">Banned</div><div class="peer-summary-value" x-text="networkHealth.banned_peers ?? '-'"></div></div>
    804             <div class="peer-summary-item"><div class="peer-summary-label">Mempool</div><div class="peer-summary-value" x-text="networkHealth.pending_transactions ?? '-'"></div></div>
    805             <div class="peer-summary-item"><div class="peer-summary-label">Plain Tx</div><div class="peer-summary-value" x-text="networkHealth.pending_plain_transactions ?? '-'"></div></div>
    806             <div class="peer-summary-item"><div class="peer-summary-label">Commits</div><div class="peer-summary-value" x-text="networkHealth.pending_blinded_transactions ?? '-'"></div></div>
    807             <div class="peer-summary-item"><div class="peer-summary-label">Reveals</div><div class="peer-summary-value" x-text="networkHealth.pending_blinded_reveals ?? '-'"></div></div>
    808             <div class="peer-summary-item"><div class="peer-summary-label">Time Offset</div><div class="peer-summary-value" x-text="networkTimeOffsetLabel()"></div></div>
    809             <div class="peer-summary-item"><div class="peer-summary-label">Clock Warnings</div><div class="peer-summary-value" x-text="networkHealth.bad_clock_peers ?? '-'"></div></div>
    810           </div>
    811         </div>
    812         <div class="peer-summary" x-show="developmentMode()">
    813           <div class="peer-summary-item"><div class="peer-summary-label">Outbound</div><div class="peer-summary-value" x-text="outboundPeers().length"></div></div>
    814           <div class="peer-summary-item"><div class="peer-summary-label">Inbound</div><div class="peer-summary-value" x-text="inboundPeers().length"></div></div>
    815           <div class="peer-summary-item"><div class="peer-summary-label">Healthy</div><div class="peer-summary-value" x-text="healthyPeers().length"></div></div>
    816           <div class="peer-summary-item"><div class="peer-summary-label">Errors</div><div class="peer-summary-value" x-text="failedPeers().length"></div></div>
    817           <div class="peer-summary-item"><div class="peer-summary-label">Shared Height</div><div class="peer-summary-value" x-text="sharedHeightLabel()"></div></div>
    818         </div>
    819         <div class="table-wrap">
    820           <table>
    821             <thead><tr><th>Status</th><th>Address</th><th>Direction</th><th>Last Contact</th><th x-show="developmentMode()">Clock</th><th x-show="developmentMode()">Ban</th><th x-show="developmentMode()">Score</th><th>Height</th><th x-show="developmentMode()">Delta</th><th x-show="developmentMode()">Tip</th><th x-show="developmentMode()">Sent</th><th x-show="developmentMode()">Received</th><th x-show="developmentMode()">Last Error</th><th>Actions</th></tr></thead>
    822             <tbody>
    823               <template x-for="peer in peers" :key="peer.address">
    824                 <tr>
    825                   <td><span class="peer-status" :class="peerStatus(peer)" x-text="peerStatusLabel(peer)"></span></td>
    826                   <td><code x-text="peer.address"></code></td>
    827                   <td x-text="peer.direction"></td>
    828                   <td x-text="peerLastContactLabel(peer)"></td>
    829                   <td x-show="developmentMode()" x-text="peerClockLabel(peer)"></td>
    830                   <td x-show="developmentMode()" x-text="peerBanLabel(peer)"></td>
    831                   <td x-show="developmentMode()" x-text="peer.misbehavior_score ?? 0"></td>
    832                   <td x-text="peer.last_known_height ?? '-'"></td>
    833                   <td x-show="developmentMode()" x-text="peerHeightDelta(peer)"></td>
    834                   <td x-show="developmentMode()"><code x-text="short(peer.last_known_tip_hash)"></code></td>
    835                   <td x-show="developmentMode()" x-text="peer.messages_sent"></td>
    836                   <td x-show="developmentMode()" x-text="peer.messages_received"></td>
    837                   <td x-show="developmentMode()" x-text="peer.last_error || ''"></td>
    838                   <td><div class="peer-actions"><button class="peer-remove" type="button" x-show="canRemovePeer(peer)" @click="removePeer(peer)">Remove</button><span class="muted" x-show="!canRemovePeer(peer)">Observed</span></div></td>
    839                 </tr>
    840               </template>
    841               <tr class="skeleton-card" x-show="peerPage.loading" aria-hidden="true">
    842                 <td colspan="14"><div class="skeleton-table-cell"></div></td>
    843               </tr>
    844               <tr class="skeleton-card" x-show="peerPage.loading" aria-hidden="true">
    845                 <td colspan="14"><div class="skeleton-table-cell"></div></td>
    846               </tr>
    847               <tr x-show="peerPage.hasMore"><td colspan="14"><div class="page-sentinel" x-init="$nextTick(() => observePageSentinel('peer', $el))"></div></td></tr>
    848               <tr x-show="peers.length === 0 && !peerPage.loading"><td colspan="14">No peers</td></tr>
    849             </tbody>
    850           </table>
    851         </div>
    852       </div>
    853       <div class="panel" x-show="developmentMode()">
    854         <h2>Metrics</h2>
    855         <div class="grid">
    856           <div class="metric"><div class="label">Inbound Sessions</div><div class="value" x-text="p2pMetrics.inbound_sessions_started ?? 0"></div></div>
    857           <div class="metric"><div class="label">Inbound Rejects</div><div class="value" x-text="p2pMetrics.inbound_sessions_rejected ?? 0"></div></div>
    858           <div class="metric"><div class="label">Outbound Attempts</div><div class="value" x-text="p2pMetrics.outbound_connect_attempts ?? 0"></div></div>
    859           <div class="metric"><div class="label">Connect Failures</div><div class="value" x-text="p2pMetrics.outbound_connect_failures ?? 0"></div></div>
    860           <div class="metric"><div class="label">Session Failures</div><div class="value" x-text="p2pMetrics.session_failures ?? 0"></div></div>
    861           <div class="metric"><div class="label">Parse Errors</div><div class="value" x-text="p2pMetrics.parse_errors ?? 0"></div></div>
    862           <div class="metric"><div class="label">Empty Frames</div><div class="value" x-text="p2pMetrics.empty_frames ?? 0"></div></div>
    863           <div class="metric"><div class="label">Self Rejects</div><div class="value" x-text="p2pMetrics.self_peer_rejections ?? 0"></div></div>
    864           <div class="metric"><div class="label">Self Skips</div><div class="value" x-text="p2pMetrics.self_peer_skips ?? 0"></div></div>
    865           <div class="metric"><div class="label">Received</div><div class="value" x-text="p2pMetrics.envelopes_received ?? 0"></div></div>
    866           <div class="metric"><div class="label">Bytes In</div><div class="value" x-text="p2pMetrics.bytes_received ?? 0"></div></div>
    867           <div class="metric"><div class="label">Status Rx</div><div class="value" x-text="p2pMetrics.peer_status_envelopes_received ?? 0"></div></div>
    868           <div class="metric"><div class="label">Hello Rx</div><div class="value" x-text="p2pMetrics.hello_envelopes_received ?? 0"></div></div>
    869           <div class="metric"><div class="label">Inventory Rx</div><div class="value" x-text="p2pMetrics.inventory_envelopes_received ?? 0"></div></div>
    870           <div class="metric"><div class="label">Data Rx</div><div class="value" x-text="p2pMetrics.data_envelopes_received ?? 0"></div></div>
    871           <div class="metric"><div class="label">Commit Rx</div><div class="value" x-text="p2pMetrics.blinded_transactions_received ?? 0"></div></div>
    872           <div class="metric"><div class="label">Commit Batches Rx</div><div class="value" x-text="p2pMetrics.blinded_transaction_envelopes_received ?? 0"></div></div>
    873           <div class="metric"><div class="label">Reveal Rx</div><div class="value" x-text="p2pMetrics.blinded_reveals_received ?? 0"></div></div>
    874           <div class="metric"><div class="label">Reveal Batches Rx</div><div class="value" x-text="p2pMetrics.blinded_reveal_envelopes_received ?? 0"></div></div>
    875           <div class="metric"><div class="label">Control Rx</div><div class="value" x-text="p2pMetrics.control_envelopes_received ?? 0"></div></div>
    876         </div>
    877         <div class="metric-context">
    878           <div class="tx-field"><span class="tx-label">Last Failure</span><span class="tx-value text" x-text="p2pMetrics.last_session_failure || '-'"></span></div>
    879           <div class="tx-field"><span class="tx-label">Last Empty</span><span class="tx-value text" x-text="p2pMetrics.last_empty_frame_remote || '-'"></span></div>
    880           <div class="tx-field"><span class="tx-label">Last Parse</span><span class="tx-value text" x-text="p2pMetrics.last_parse_error || '-'"></span></div>
    881         </div>
    882       </div>
    883     </section>
    884 
    885     <section x-show="tab === 'chain'">
    886       <div class="explorer-shell">
    887         <div class="block-rail-wrap">
    888           <div class="block-rail-head">
    889             <h2>Blocks</h2>
    890             <div class="muted"><span x-text="blocks.length"></span> loaded</div>
    891           </div>
    892           <div class="block-rail" x-ref="blockRail" @scroll.debounce.200ms="maybeLoadOlderBlocks($event)">
    893             <template x-for="block in blocks" :key="block.hash">
    894               <button class="block-card" :class="{ selected: selectedBlock?.hash === block.hash, 'new-block': newBlockHashes.has(block.hash) }" @click="selectBlock(block)" type="button">
    895                 <div class="block-height" x-text="block.height"></div>
    896                 <div class="block-meta">
    897                   <span x-text="burnCountLabel(block)"></span>
    898                   <span x-text="transferCountLabel(block)"></span>
    899                   <span x-text="commitCountLabel(block)"></span>
    900                   <span x-text="mineCountLabel(block)"></span>
    901                 </div>
    902                 <div class="block-miner" x-text="blockFinalizerLabel(block)"></div>
    903               </button>
    904             </template>
    905             <template x-if="loadingInitialBlocks && blocks.length === 0">
    906               <div class="block-skeleton-group" aria-hidden="true">
    907                 <div class="block-card block-card-skeleton skeleton-card">
    908                   <div class="skeleton-block-height"></div>
    909                   <div class="skeleton-block-meta"><span class="skeleton-pill wide"></span><span class="skeleton-pill"></span><span class="skeleton-pill wide"></span></div>
    910                   <div class="skeleton-block-miner"></div>
    911                 </div>
    912                 <div class="block-card block-card-skeleton skeleton-card">
    913                   <div class="skeleton-block-height"></div>
    914                   <div class="skeleton-block-meta"><span class="skeleton-pill"></span><span class="skeleton-pill wide"></span><span class="skeleton-pill"></span></div>
    915                   <div class="skeleton-block-miner"></div>
    916                 </div>
    917                 <div class="block-card block-card-skeleton skeleton-card">
    918                   <div class="skeleton-block-height"></div>
    919                   <div class="skeleton-block-meta"><span class="skeleton-pill wide"></span><span class="skeleton-pill wide"></span><span class="skeleton-pill"></span></div>
    920                   <div class="skeleton-block-miner"></div>
    921                 </div>
    922                 <div class="block-card block-card-skeleton skeleton-card">
    923                   <div class="skeleton-block-height"></div>
    924                   <div class="skeleton-block-meta"><span class="skeleton-pill"></span><span class="skeleton-pill"></span><span class="skeleton-pill wide"></span></div>
    925                   <div class="skeleton-block-miner"></div>
    926                 </div>
    927               </div>
    928             </template>
    929             <template x-if="loadingOlder">
    930               <div class="block-card block-card-skeleton skeleton-card" aria-hidden="true">
    931                 <div class="skeleton-block-height"></div>
    932                 <div class="skeleton-block-meta"><span class="skeleton-pill wide"></span><span class="skeleton-pill"></span><span class="skeleton-pill wide"></span></div>
    933                 <div class="skeleton-block-miner"></div>
    934               </div>
    935             </template>
    936             <div class="page-sentinel block-page-sentinel" x-show="hasMoreBlocks" x-init="$nextTick(() => observeBlockSentinel($el))"></div>
    937           </div>
    938         </div>
    939 
    940         <section class="panel">
    941           <h2>Block Detail</h2>
    942           <template x-if="selectedBlock">
    943             <div class="detail-grid">
    944               <div>
    945                 <div class="detail-kv"><div class="key">Height</div><div x-text="selectedBlock.height"></div></div>
    946                 <div class="detail-kv"><div class="key">Time</div><div x-text="blockTimestampLabel(selectedBlock)"></div></div>
    947                 <div class="detail-kv"><div class="key">Hash</div><code x-text="selectedBlock.hash"></code></div>
    948                 <div class="detail-kv"><div class="key">Previous</div><code x-text="short(selectedBlock.prev_hash)"></code></div>
    949                 <div class="detail-kv">
    950                   <div class="key">Finalizer</div>
    951                   <button class="detail-link" type="button" @click="openBurnLeaderRanksModal(selectedBlock)" title="Burn leader ranks">
    952                     <code x-text="shortAddressLabel(selectedBlock.miner)"></code>
    953                   </button>
    954                 </div>
    955                 <div class="detail-kv"><div class="key">Mode</div><div x-text="selectedBlock.finalizer_mode === 'recovery' ? 'Recovery' : `Rank ${selectedBlock.finalizer_rank ?? 0}`"></div></div>
    956                 <div class="detail-kv"><div class="key">Reward</div><div>IUNA <span x-text="amountLabel(selectedBlock.reward)"></span></div></div>
    957                 <div class="detail-kv"><div class="key">Reveal Lists</div><div x-text="blockRevealListRatio(selectedBlock)"></div></div>
    958                 <div class="detail-kv"><div class="key">Fee Penalty</div><div class="fee-penalty-value" :class="{ penalty: blockRevealFeePenaltyAmount(selectedBlock) > 0 }">IUNA <span x-text="amountLabel(blockRevealFeePenaltyAmount(selectedBlock))"></span></div></div>
    959                 <div class="detail-kv"><div class="key">Burns</div><div x-text="blockBurnCount(selectedBlock)"></div></div>
    960                 <div class="detail-kv"><div class="key">Transfers</div><div x-text="blockTransferCount(selectedBlock)"></div></div>
    961                 <div class="detail-kv"><div class="key">Total Burned</div><div>IUNA <span x-text="amountLabel(blockBurned(selectedBlock))"></span></div></div>
    962                 <div class="detail-kv">
    963                   <div class="key">Bytes</div>
    964                   <button class="detail-link" type="button" @click="openBlockBytesModal(selectedBlock)" title="Block byte breakdown">
    965                     <span x-text="blockTotalBytes(selectedBlock)"></span>B
    966                   </button>
    967                 </div>
    968                 <div class="detail-kv"><div class="key">VDF</div><div><span x-text="selectedBlock.vdf_rounds"></span> rounds</div></div>
    969               </div>
    970               <div class="tx-list">
    971                 <h3>Transactions</h3>
    972                 <div class="tx-section">
    973                   <div class="tx-section-title"><span>Envelope</span><span class="tx-section-meta" x-text="shortAddressLabel(selectedBlock.miner)"></span></div>
    974                   <template x-for="tx in selectedBlock.transactions" :key="tx.signature">
    975                     <div class="tx-card" role="button" tabindex="0" @click="openTransactionModal(tx, { source: 'Envelope', blockHeight: selectedBlock.height, blockFinalizer: selectedBlock.miner })" @keydown.enter.prevent="openTransactionModal(tx, { source: 'Envelope', blockHeight: selectedBlock.height, blockFinalizer: selectedBlock.miner })" @keydown.space.prevent="openTransactionModal(tx, { source: 'Envelope', blockHeight: selectedBlock.height, blockFinalizer: selectedBlock.miner })">
    976                       <span class="pill" :class="txPillClass(tx)" x-text="txPillLabel(tx)"></span>
    977                       <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Amount</span><span class="tx-value money">IUNA <span x-text="amountLabel(txAmount(tx))"></span></span></div>
    978                       <div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(tx)"></span></div>
    979                       <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">From</span><code class="tx-value hash" x-text="shortAddressLabel(txFrom(tx))"></code></div>
    980                       <div class="tx-field" x-show="txTo(tx)"><span class="tx-label">To</span><code class="tx-value hash" x-text="shortAddressLabel(txTo(tx))"></code></div>
    981                       <div class="tx-field" x-show="isBlindedMempoolItem(tx)"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="short(tx.commitment || tx.signature)"></code></div>
    982                       <div class="tx-field" x-show="tx.encrypted_size || tx.encryptedSize"><span class="tx-label">Bytes</span><span class="tx-value number" x-text="tx.encrypted_size || tx.encryptedSize"></span></div>
    983                       <div class="tx-field" x-show="tx.expires_at_height || tx.expiresAtHeight"><span class="tx-label">Expires</span><span class="tx-value number" x-text="tx.expires_at_height || tx.expiresAtHeight"></span></div>
    984                       <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Bits</span><span class="tx-value number"><span x-text="txProofBits(tx) ?? '-'"></span> / <span x-text="txDifficultyBits(tx) ?? '-'"></span></span></div>
    985                       <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Hash</span><code class="tx-value hash" x-text="short(txProofHash(tx))"></code></div>
    986                       <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Signature</span><code class="tx-value hash" x-text="short(tx.signature)"></code></div>
    987                     </div>
    988                   </template>
    989                   <div class="muted" x-show="selectedBlock.transactions.length === 0">No envelope transactions</div>
    990                 </div>
    991                 <template x-for="bundle in selectedBlock.reveal_bundles || selectedBlock.revealBundles || []" :key="bundle.hash">
    992                   <details class="tx-section">
    993                     <summary class="tx-section-title"><span x-text="`Reveal bundle ${bundle.slot}`"></span><span class="tx-section-meta"><span x-text="shortAddressLabel(bundle.member)"></span> · <span x-text="bundle.byte_size || bundle.byteSize || 0"></span>B</span></summary>
    994                     <template x-for="tx in bundle.reveals" :key="tx.signature">
    995                       <div class="tx-card" role="button" tabindex="0" @click="openTransactionModal(tx, { source: 'Reveal bundle', blockHeight: selectedBlock.height, blockFinalizer: bundle.member })" @keydown.enter.prevent="openTransactionModal(tx, { source: 'Reveal bundle', blockHeight: selectedBlock.height, blockFinalizer: bundle.member })" @keydown.space.prevent="openTransactionModal(tx, { source: 'Reveal bundle', blockHeight: selectedBlock.height, blockFinalizer: bundle.member })">
    996                         <span class="pill" :class="txPillClass(tx)" x-text="txPillLabel(tx)"></span>
    997                         <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Amount</span><span class="tx-value money">IUNA <span x-text="amountLabel(txAmount(tx))"></span></span></div>
    998                         <div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(tx)"></span></div>
    999                         <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">From</span><code class="tx-value hash" x-text="shortAddressLabel(txFrom(tx))"></code></div>
   1000                         <div class="tx-field" x-show="txTo(tx)"><span class="tx-label">To</span><code class="tx-value hash" x-text="shortAddressLabel(txTo(tx))"></code></div>
   1001                         <div class="tx-field" x-show="isBlindedMempoolItem(tx)"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="short(tx.commitment || tx.signature)"></code></div>
   1002                         <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Signature</span><code class="tx-value hash" x-text="short(tx.signature)"></code></div>
   1003                       </div>
   1004                     </template>
   1005                     <div class="muted" x-show="bundle.reveals.length === 0">No reveals in bundle</div>
   1006                   </details>
   1007                 </template>
   1008                 <div class="muted" x-show="selectedBlock.transactions.length === 0 && !(selectedBlock.reveal_bundles || selectedBlock.revealBundles || []).length">No transactions</div>
   1009               </div>
   1010             </div>
   1011           </template>
   1012           <div class="muted" x-show="!selectedBlock">Select a block</div>
   1013         </section>
   1014 
   1015         <section class="panel mempool-panel" x-show="mempool.length > 0 || mempoolPage.loading">
   1016           <h2>Mempool</h2>
   1017           <div class="mempool-strip">
   1018             <template x-for="tx in mempool" :key="tx.signature">
   1019               <div class="mempool-item" :class="mempoolItemClass(tx)" role="button" tabindex="0" @click="openTransactionModal(tx, { source: 'Mempool' })" @keydown.enter.prevent="openTransactionModal(tx, { source: 'Mempool' })" @keydown.space.prevent="openTransactionModal(tx, { source: 'Mempool' })">
   1020                 <div class="mempool-top">
   1021                   <div class="mempool-top-meta">
   1022                     <div class="mempool-state" x-show="mempoolItemClass(tx).includes('new-since-block')">New since last block</div>
   1023                     <div class="mempool-time" x-show="mempoolSeenTimeLabel(tx)" x-text="mempoolSeenTimeLabel(tx)"></div>
   1024                   </div>
   1025                   <span class="pill" :class="tx.kind" x-text="tx.kind"></span>
   1026                 </div>
   1027                 <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Amount</span><span class="tx-value money">IUNA <span x-text="amountLabel(txAmount(tx))"></span></span></div>
   1028                 <div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(tx)"></span></div>
   1029                 <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">From</span><code class="tx-value hash" x-text="shortAddressLabel(txFrom(tx))"></code></div>
   1030                 <div class="tx-field" x-show="txTo(tx)"><span class="tx-label">To</span><code class="tx-value hash" x-text="shortAddressLabel(txTo(tx))"></code></div>
   1031                 <div class="tx-field" x-show="isBlindedMempoolItem(tx)"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="short(tx.commitment || tx.signature)"></code></div>
   1032                 <div class="tx-field" x-show="tx.encrypted_size || tx.encryptedSize"><span class="tx-label">Bytes</span><span class="tx-value number" x-text="tx.encrypted_size || tx.encryptedSize"></span></div>
   1033                 <div class="tx-field" x-show="tx.expires_at_height || tx.expiresAtHeight"><span class="tx-label">Expires</span><span class="tx-value number" x-text="tx.expires_at_height || tx.expiresAtHeight"></span></div>
   1034                 <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Bits</span><span class="tx-value number"><span x-text="txProofBits(tx) ?? '-'"></span> / <span x-text="txDifficultyBits(tx) ?? '-'"></span></span></div>
   1035                 <div class="tx-field" x-show="isMineTx(tx)"><span class="tx-label">Proof Hash</span><code class="tx-value hash" x-text="short(txProofHash(tx))"></code></div>
   1036                 <div class="tx-field" x-show="!isBlindedMempoolItem(tx)"><span class="tx-label">Signature</span><code class="tx-value hash" x-text="short(tx.signature)"></code></div>
   1037               </div>
   1038             </template>
   1039             <template x-if="mempoolPage.loading">
   1040               <div class="mempool-item skeleton-card" aria-hidden="true">
   1041                 <div class="skeleton-line short"></div>
   1042                 <div class="skeleton-line medium"></div>
   1043                 <div class="skeleton-line long"></div>
   1044               </div>
   1045             </template>
   1046             <div class="page-sentinel" x-show="mempoolPage.hasMore" x-init="$nextTick(() => observePageSentinel('mempool', $el))"></div>
   1047           </div>
   1048         </section>
   1049       </div>
   1050     </section>
   1051     <section x-show="tab === 'metrics'">
   1052       <div class="metrics-shell">
   1053         <div class="metrics-head">
   1054           <h2>Metrics</h2>
   1055           <div class="segmented metrics-range" role="group" aria-label="Metrics block range">
   1056             <button type="button" :class="{ active: metricsRange === 100 }" @click="setMetricsRange(100)">Last 100</button>
   1057             <button type="button" :class="{ active: metricsRange === 1000 }" @click="setMetricsRange(1000)">Last 1000</button>
   1058             <button type="button" :class="{ active: metricsRange === 'all' }" @click="setMetricsRange('all')">All</button>
   1059           </div>
   1060         </div>
   1061         <div class="metrics-summary">
   1062           <div class="metric"><div class="label">Latest block</div><div class="value" x-text="metricsLatest().height ?? '-'"></div></div>
   1063           <div class="metric"><div class="label">Supply</div><div class="value" x-text="metricAmountLabel(metricsLatest().circulatingSupply)"></div></div>
   1064           <div class="metric"><div class="label">Known addresses</div><div class="value" x-text="metricsLatest().knownWalletAddresses ?? '-'"></div></div>
   1065           <div class="metric"><div class="label">Total burned</div><div class="value" x-text="metricAmountLabel(metricsLatest().totalBurnedAmount)"></div></div>
   1066           <div class="metric"><div class="label">Difficulty</div><div class="value" x-text="metricsLatest().mineDifficultyBits ?? '-'"></div></div>
   1067         </div>
   1068         <div class="metrics-grid" x-show="loadingMetrics && metricsCharts().length === 0">
   1069           <article class="metric-chart-card skeleton-card" aria-hidden="true">
   1070             <div class="metric-chart-head"><div class="skeleton-line medium"></div><div class="skeleton-line short"></div></div>
   1071             <div class="metric-chart-frame"><div class="skeleton-line long"></div></div>
   1072           </article>
   1073           <article class="metric-chart-card skeleton-card" aria-hidden="true">
   1074             <div class="metric-chart-head"><div class="skeleton-line short"></div><div class="skeleton-line medium"></div></div>
   1075             <div class="metric-chart-frame"><div class="skeleton-line long"></div></div>
   1076           </article>
   1077         </div>
   1078         <div class="metrics-empty" x-show="metricsCharts().length === 0 && !loadingMetrics">No metrics collected yet</div>
   1079         <div class="metrics-grid">
   1080           <template x-for="chart in metricsCharts()" :key="chart.id">
   1081             <article class="metric-chart-card">
   1082               <div class="metric-chart-head">
   1083                 <h3 class="metric-chart-title" x-text="chart.title"></h3>
   1084                 <div class="metric-chart-value" x-text="metricLatestValueLabel(chart)"></div>
   1085               </div>
   1086               <div class="metric-chart-frame">
   1087                 <div class="metric-chart-y-axis">
   1088                   <template x-for="tick in metricYAxisTicks(chart)" :key="`${chart.id}-y-${tick}`">
   1089                     <span class="metric-chart-axis-label" :style="metricYAxisLabelStyle(chart, tick)" x-text="metricAxisValueLabel(chart, tick)"></span>
   1090                   </template>
   1091                 </div>
   1092                 <div class="metric-chart-plot" @mousemove="setMetricHoverFromPlot(chart, $event)" @mouseleave="clearMetricHover(chart)">
   1093                   <svg class="metric-chart-svg" viewBox="0 0 300 148" preserveAspectRatio="none" role="img" :aria-label="chart.title">
   1094                     <path class="metric-chart-gridline" :d="metricGridPath(chart)"></path>
   1095                     <line class="metric-chart-axis" x1="4" y1="8" x2="4" y2="132"></line>
   1096                     <line class="metric-chart-axis" x1="4" y1="132" x2="296" y2="132"></line>
   1097                     <polyline class="metric-chart-line" :points="metricChartPoints(chart)"></polyline>
   1098                   </svg>
   1099                   <template x-if="metricHover?.chartId === chart.id">
   1100                     <div class="metric-chart-hover-point" :style="metricHoverPointStyle(chart)" :title="metricTooltipLabel(chart)"></div>
   1101                   </template>
   1102                   <template x-if="metricHover?.chartId === chart.id">
   1103                     <div class="metric-chart-tooltip" :style="metricTooltipStyle(chart)" x-text="metricTooltipLabel(chart)"></div>
   1104                   </template>
   1105                 </div>
   1106                 <div class="metric-chart-x-axis">
   1107                   <template x-for="tick in metricXAxisTicks(chart)" :key="`${chart.id}-x-${tick}`">
   1108                     <span class="metric-chart-axis-label" :style="metricXAxisLabelStyle(chart, tick)" x-text="`#${tick}`"></span>
   1109                   </template>
   1110                 </div>
   1111               </div>
   1112             </article>
   1113           </template>
   1114         </div>
   1115         <div class="metrics-subhead">
   1116           <h2>Leaderboards</h2>
   1117         </div>
   1118         <div class="leaderboard-grid">
   1119           <template x-for="board in [{ key: 'balances', title: 'Top 10 Balance' }, { key: 'miners', title: 'Top 10 Miners' }, { key: 'burners', title: 'Top 10 Burners' }]" :key="board.key">
   1120             <article class="leaderboard-card">
   1121               <h3 x-text="board.title"></h3>
   1122               <div class="leaderboard-list">
   1123                 <template x-for="(row, index) in leaderboardRows(board.key)" :key="`${board.key}-${row.address}`">
   1124                   <div class="leaderboard-row">
   1125                     <div class="leaderboard-rank" :class="leaderboardRankClass(index)" x-text="leaderboardRankLabel(index)"></div>
   1126                     <div class="leaderboard-main">
   1127                       <code class="tx-value hash" x-text="shortAddressLabel(row.address)"></code>
   1128                       <div class="muted" x-text="leaderboardCountLabel(board.key, row)"></div>
   1129                     </div>
   1130                     <div class="leaderboard-amount" x-text="leaderboardAmountLabel(row)"></div>
   1131                   </div>
   1132                 </template>
   1133                 <div class="metrics-empty" x-show="leaderboardRows(board.key).length === 0">No entries</div>
   1134               </div>
   1135             </article>
   1136           </template>
   1137         </div>
   1138       </div>
   1139     </section>
   1140     <section x-show="tab === 'settings'">
   1141       <div class="settings-grid">
   1142         <div class="panel">
   1143           <div class="panel-head">
   1144             <h2>Settings</h2>
   1145             <span class="pill" x-text="advancedMode() ? 'Node mode' : 'Wallet mode'"></span>
   1146           </div>
   1147           <div class="settings-mode-row">
   1148             <div class="settings-mode-copy">
   1149               <div class="settings-mode-title">Mode</div>
   1150               <div class="muted" x-text="advancedMode() ? 'Node mode shows mining and peer controls.' : 'Wallet mode keeps the interface focused on wallet and chain views.'"></div>
   1151             </div>
   1152             <label class="toggle-switch" :class="{ active: advancedMode() }">
   1153               <input type="checkbox" :checked="advancedMode()" @change="setUiMode($event.target.checked ? 'advanced' : 'basic')">
   1154               <span class="toggle-track" aria-hidden="true"><span class="toggle-thumb"></span></span>
   1155               <span class="toggle-text" x-text="advancedMode() ? 'Node' : 'Wallet'"></span>
   1156             </label>
   1157           </div>
   1158         </div>
   1159         <div class="panel">
   1160           <div class="settings-mode-row">
   1161             <div class="settings-mode-copy">
   1162               <div class="settings-mode-title">Development mode</div>
   1163               <div class="muted" x-text="developmentMode() ? 'Detailed P2P data and the metrics screen are available.' : 'P2P stays focused on a simple peer list.'"></div>
   1164             </div>
   1165             <label class="toggle-switch" :class="{ active: keepTrackOfMetrics }">
   1166               <input type="checkbox" :checked="keepTrackOfMetrics" @change="setKeepTrackOfMetrics($event.target.checked)">
   1167               <span class="toggle-track" aria-hidden="true"><span class="toggle-thumb"></span></span>
   1168               <span class="toggle-text" x-text="developmentMode() ? 'On' : 'Off'"></span>
   1169             </label>
   1170           </div>
   1171         </div>
   1172         <div class="panel" x-show="advancedMode()">
   1173           <div class="settings-mode-row">
   1174             <div class="settings-mode-copy">
   1175               <div class="settings-mode-title">Recovery VDF</div>
   1176               <div class="muted">Top <span x-text="recoveryVdfTopRankPercent"></span>% threshold for fallback/recovery work.</div>
   1177             </div>
   1178             <label>Top ranks
   1179               <input type="range" min="0" max="100" step="5" :value="recoveryVdfTopRankPercent" @change="setRecoveryVdfTopRankPercent($event.target.value)">
   1180             </label>
   1181           </div>
   1182         </div>
   1183         <div class="panel" x-show="advancedMode()">
   1184           <h3>Node Networking</h3>
   1185           <div class="settings-mode-row">
   1186             <div class="settings-mode-copy">
   1187               <div class="settings-mode-title">Public node</div>
   1188               <div class="muted" x-text="p2pAcceptInbound ? 'Accepting inbound P2P connections.' : 'Outbound-only P2P; no inbound port is open.'"></div>
   1189             </div>
   1190             <label class="toggle-switch" :class="{ active: p2pAcceptInbound }">
   1191               <input type="checkbox" :checked="p2pAcceptInbound" @change="setP2pAcceptInbound($event.target.checked)">
   1192               <span class="toggle-track" aria-hidden="true"><span class="toggle-thumb"></span></span>
   1193               <span class="toggle-text" x-text="p2pAcceptInbound ? 'Public' : 'Private'"></span>
   1194             </label>
   1195           </div>
   1196           <form class="settings-form public-p2p-form" x-show="p2pAcceptInbound" x-transition @submit.prevent="saveP2pAnnounce">
   1197             <label>Bind port<input x-model.number="p2pBindPort" @input="p2pBindPortDirty = true" type="number" min="1" max="65535" step="1" required></label>
   1198             <label>Public P2P address<input x-model="p2pAnnounceAddr" @input="p2pAnnounceDirty = true" placeholder="203.0.113.10:9444"></label>
   1199             <div class="muted">Use this only when TCP port <span x-text="p2pBindPort"></span> is reachable from the internet.</div>
   1200             <div class="setup-actions"><button class="primary" type="submit">Save</button></div>
   1201           </form>
   1202         </div>
   1203         <div class="panel">
   1204           <h3>Change Password</h3>
   1205           <div class="setup-feedback" :class="settingsFeedback?.kind" x-show="settingsFeedback" x-transition x-text="settingsFeedback?.message"></div>
   1206           <form class="settings-form" @submit.prevent="changePassword">
   1207             <input class="visually-hidden" type="text" name="username" value="iuna" autocomplete="username" tabindex="-1" aria-hidden="true">
   1208             <label>Current password<input x-model="settingsOldPassword" type="password" autocomplete="current-password" required></label>
   1209             <label>New password<input x-model="settingsNewPassword" type="password" autocomplete="new-password" minlength="12" required></label>
   1210             <label>Confirm new password<input x-model="settingsPasswordConfirm" type="password" autocomplete="new-password" minlength="12" required></label>
   1211             <div class="setup-actions"><button class="primary" type="submit">Change password</button></div>
   1212           </form>
   1213         </div>
   1214         <div class="panel danger-panel">
   1215           <h3>Danger Zone</h3>
   1216           <div class="settings-mode-row">
   1217             <div class="settings-mode-copy">
   1218               <div class="settings-mode-title danger-title">Delete local chain</div>
   1219               <div class="danger-copy">Remove the local blockchain database and request a fresh sync from connected peers. Wallet and settings stay on this device.</div>
   1220             </div>
   1221             <button class="danger" type="button" @click="openChainResetModal">Delete chain</button>
   1222           </div>
   1223         </div>
   1224       </div>
   1225     </section>
   1226     </main>
   1227   </div>
   1228   <div class="setup-overlay transaction-overlay" x-show="chainResetModalOpen" x-transition.opacity @click.self="closeChainResetModal()" role="dialog" aria-modal="true" aria-labelledby="chain-reset-title">
   1229     <section class="tx-modal">
   1230       <div class="tx-modal-head">
   1231         <div class="tx-modal-title">
   1232           <span class="pill error">Danger</span>
   1233           <h2 id="chain-reset-title">Delete local chain</h2>
   1234         </div>
   1235         <button type="button" @click="closeChainResetModal" :disabled="chainResetBusy">Close</button>
   1236       </div>
   1237       <div class="info-copy">
   1238         <p>This deletes the local chain database and clears local chain views. Your wallet and settings stay intact.</p>
   1239         <p>Type <strong>RESET</strong> to confirm.</p>
   1240       </div>
   1241       <form class="settings-form" @submit.prevent="resetLocalChain">
   1242         <label>Confirmation<input x-model="chainResetConfirm" autocomplete="off" spellcheck="false" placeholder="RESET"></label>
   1243         <div class="danger-actions">
   1244           <button class="subtle" type="button" @click="closeChainResetModal" :disabled="chainResetBusy">Cancel</button>
   1245           <button class="danger" type="submit" :disabled="chainResetConfirm.trim() !== 'RESET' || chainResetBusy" x-text="chainResetBusy ? 'Deleting...' : 'Delete and resync'"></button>
   1246         </div>
   1247       </form>
   1248     </section>
   1249   </div>
   1250   <div class="setup-overlay" x-show="showingAuth()" x-transition.opacity role="dialog" aria-modal="true" aria-labelledby="auth-title">
   1251     <section class="setup-modal auth-form">
   1252       <div class="setup-modal-head">
   1253         <div class="setup-welcome">iuna Access</div>
   1254         <h2 id="auth-title" x-text="auth.configured ? 'Unlock iuna' : 'Set Password'"></h2>
   1255         <div class="setup-copy" x-show="!auth.configured">Choose a local password before wallet setup continues.</div>
   1256         <div class="setup-copy" x-show="auth.configured">Enter the local password to unlock this node.</div>
   1257       </div>
   1258       <div class="setup-feedback" :class="authFeedback?.kind" x-show="authFeedback" x-transition x-text="authFeedback?.message"></div>
   1259       <form x-show="!auth.configured" @submit.prevent="setupPassword">
   1260         <input class="visually-hidden" type="text" name="username" value="iuna" autocomplete="username" tabindex="-1" aria-hidden="true">
   1261         <label>Password<input x-model="authPassword" type="password" autocomplete="new-password" minlength="12" required></label>
   1262         <label>Confirm password<input x-model="authPasswordConfirm" type="password" autocomplete="new-password" minlength="12" required></label>
   1263         <div class="setup-actions"><button class="primary" type="submit">Set password</button></div>
   1264       </form>
   1265       <form x-show="auth.configured && !auth.authenticated" @submit.prevent="login">
   1266         <input class="visually-hidden" type="text" name="username" value="iuna" autocomplete="username" tabindex="-1" aria-hidden="true">
   1267         <label>Password<input x-model="loginPassword" type="password" autocomplete="current-password" required></label>
   1268         <div class="setup-actions"><button class="primary" type="submit">Unlock</button></div>
   1269       </form>
   1270     </section>
   1271   </div>
   1272   <div class="setup-overlay transaction-overlay" x-show="showWalletUtxos" x-transition.opacity @click.self="closeWalletUtxosModal()" role="dialog" aria-modal="true" aria-labelledby="wallet-utxos-title">
   1273     <section class="tx-modal">
   1274       <div class="tx-modal-head">
   1275         <div class="tx-modal-title">
   1276           <h2 id="wallet-utxos-title">Wallet UTXOs</h2>
   1277           <div class="tx-field"><span class="tx-label">Total</span><span class="tx-value money">IUNA <span x-text="amountLabel(status.wallet_balance)"></span></span></div>
   1278         </div>
   1279         <button type="button" @click="closeWalletUtxosModal">Close</button>
   1280       </div>
   1281       <div class="utxo-list">
   1282         <template x-for="utxo in walletUtxos" :key="`${utxo.outpoint.txid}:${utxo.outpoint.index}`">
   1283           <div class="wallet-utxo-row">
   1284             <div class="utxo-node-label"><span>UTXO</span><span class="utxo-node-amount">IUNA <span x-text="amountLabel(utxo.amount)"></span></span></div>
   1285             <div class="tx-field"><span class="tx-label">Outpoint</span><code class="tx-value hash" x-text="txInputOutpoint({ outpoint: utxo.outpoint })"></code></div>
   1286             <div class="tx-field"><span class="tx-label">Address</span><code class="tx-value hash" x-text="addressLabel(utxo.address)"></code></div>
   1287           </div>
   1288         </template>
   1289         <div class="dataset-loader" x-show="walletUtxoPage.loading" aria-hidden="true">
   1290           <div class="wallet-utxo-row skeleton-card"><div class="skeleton-line medium"></div><div class="skeleton-line long"></div></div>
   1291           <div class="wallet-utxo-row skeleton-card"><div class="skeleton-line short"></div><div class="skeleton-line long"></div></div>
   1292         </div>
   1293         <div class="page-sentinel" x-show="walletUtxoPage.hasMore" x-init="$nextTick(() => observePageSentinel('walletUtxo', $el))"></div>
   1294         <div class="tx-modal-empty" x-show="walletUtxos.length === 0 && !walletUtxoPage.loading">No wallet UTXOs</div>
   1295       </div>
   1296     </section>
   1297   </div>
   1298   <div class="setup-overlay transaction-overlay" x-show="showPowDifficultyInfo" x-transition.opacity @click.self="closePowDifficultyInfo()" role="dialog" aria-modal="true" aria-labelledby="pow-difficulty-title">
   1299     <section class="tx-modal">
   1300       <div class="tx-modal-head">
   1301         <div class="tx-modal-title">
   1302           <h2 id="pow-difficulty-title">PoW Difficulty</h2>
   1303         </div>
   1304         <button type="button" @click="closePowDifficultyInfo">Close</button>
   1305       </div>
   1306       <div class="info-copy">
   1307         <p>Difficulty is adjusted to target about one mine action per block.</p>
   1308         <div class="info-facts">
   1309           <div class="info-fact"><div class="label">Window</div><div class="value">10 blocks</div></div>
   1310           <div class="info-fact"><div class="label">Target</div><div class="value">10 mine actions</div></div>
   1311           <div class="info-fact"><div class="label">Max step</div><div class="value">2 bits</div></div>
   1312         </div>
   1313         <p>If a window includes more mine actions than the target, difficulty rises. If it includes fewer, difficulty falls. The initial difficulty is 12 bits.</p>
   1314       </div>
   1315     </section>
   1316   </div>
   1317   <div class="setup-overlay transaction-overlay" x-show="selectedByteBlock" x-transition.opacity @click.self="closeBlockBytesModal()" role="dialog" aria-modal="true" aria-labelledby="block-bytes-title">
   1318     <section class="tx-modal">
   1319       <div class="tx-modal-head">
   1320         <div class="tx-modal-title">
   1321           <h2 id="block-bytes-title" x-text="selectedByteBlock ? `Block ${selectedByteBlock.height} Bytes` : 'Block Bytes'"></h2>
   1322           <div class="tx-field"><span class="tx-label">Total</span><span class="tx-value number"><span x-text="blockTotalBytes(selectedByteBlock)"></span>B</span></div>
   1323         </div>
   1324         <button type="button" @click="closeBlockBytesModal">Close</button>
   1325       </div>
   1326       <div class="rank-list">
   1327         <template x-for="row in blockByteBreakdown(selectedByteBlock)" :key="row[0]">
   1328           <div class="rank-row">
   1329             <div class="rank-number" x-text="`${row[1]}B`"></div>
   1330             <div class="rank-details">
   1331               <div class="tx-field">
   1332                 <span class="tx-label">Category</span>
   1333                 <span class="pill" x-show="row[2]" :class="row[2]" x-text="row[0]"></span>
   1334                 <span class="tx-value text" x-show="!row[2]" x-text="row[0]"></span>
   1335               </div>
   1336             </div>
   1337           </div>
   1338         </template>
   1339       </div>
   1340     </section>
   1341   </div>
   1342   <div class="setup-overlay transaction-overlay" x-show="selectedBurnLeaderBlock" x-transition.opacity @click.self="closeBurnLeaderRanksModal()" role="dialog" aria-modal="true" aria-labelledby="burn-ranks-title">
   1343     <section class="tx-modal">
   1344       <div class="tx-modal-head">
   1345         <div class="tx-modal-title">
   1346           <h2 id="burn-ranks-title" x-text="burnLeaderRanksTitle(selectedBurnLeaderBlock)"></h2>
   1347           <div class="tx-field"><span class="tx-label">Finalizer</span><code class="tx-value hash" x-text="selectedBurnLeaderBlock ? addressLabel(selectedBurnLeaderBlock.miner) : '-'"></code></div>
   1348         </div>
   1349         <button type="button" @click="closeBurnLeaderRanksModal">Close</button>
   1350       </div>
   1351       <div class="rank-list">
   1352         <template x-for="rank in burnLeaderRanks(selectedBurnLeaderBlock)" :key="`${selectedBurnLeaderBlock.hash}-${rank.rank}-${rank.ticket_id ?? rank.ticketId}`">
   1353           <div class="rank-row">
   1354             <div class="rank-number" x-text="burnLeaderRankLabel(rank)"></div>
   1355             <div class="rank-details">
   1356               <div class="tx-field"><span class="tx-label">Owner</span><code class="tx-value hash" x-text="addressLabel(rank.owner)"></code></div>
   1357               <div class="tx-field"><span class="tx-label">Burn</span><span class="tx-value money">IUNA <span x-text="amountLabel(rank.amount)"></span></span></div>
   1358               <div class="tx-field"><span class="tx-label">Ticket</span><code class="tx-value hash" x-text="short(rank.ticket_id ?? rank.ticketId)"></code></div>
   1359               <div class="tx-field"><span class="tx-label">Eligible</span><span class="tx-value number" x-text="burnLeaderEligibilityLabel(rank)"></span></div>
   1360             </div>
   1361           </div>
   1362         </template>
   1363         <div class="tx-modal-empty" x-show="burnLeaderRanks(selectedBurnLeaderBlock).length === 0">No burn leader ranks</div>
   1364       </div>
   1365     </section>
   1366   </div>
   1367   <div class="setup-overlay transaction-overlay" x-show="selectedTransaction" x-transition.opacity @click.self="closeTransactionModal()" role="dialog" aria-modal="true" aria-labelledby="tx-modal-title">
   1368     <section class="tx-modal">
   1369       <div class="tx-modal-head">
   1370         <div class="tx-modal-title">
   1371           <span class="pill" :class="txPillClass(selectedTransaction?.tx)" x-text="txPillLabel(selectedTransaction?.tx)"></span>
   1372           <h2 id="tx-modal-title">Transaction</h2>
   1373           <code class="tx-value hash" x-text="selectedTransaction?.tx?.signature || '-'"></code>
   1374         </div>
   1375         <button type="button" @click="closeTransactionModal">Close</button>
   1376       </div>
   1377       <div class="tx-modal-summary">
   1378         <div class="tx-field"><span class="tx-label">Source</span><span class="tx-value text" x-text="selectedTransactionLabel()"></span></div>
   1379         <div class="tx-field" x-show="!isBlindedMempoolItem(selectedTransaction?.tx)"><span class="tx-label">Amount</span><span class="tx-value money">IUNA <span x-text="amountLabel(txAmount(selectedTransaction?.tx || {}))"></span></span></div>
   1380         <div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(selectedTransaction?.tx)"></span></div>
   1381         <div class="tx-field" x-show="!isBlindedMempoolItem(selectedTransaction?.tx)"><span class="tx-label">From</span><code class="tx-value hash" x-text="addressLabel(txFrom(selectedTransaction?.tx || {}))"></code></div>
   1382         <div class="tx-field" x-show="txTo(selectedTransaction?.tx || {})"><span class="tx-label">To</span><code class="tx-value hash" x-text="addressLabel(txTo(selectedTransaction?.tx || {}))"></code></div>
   1383         <div class="tx-field" x-show="isBlindedMempoolItem(selectedTransaction?.tx) || selectedTransaction?.tx?.commitment"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="selectedTransaction?.tx?.commitment || selectedTransaction?.tx?.signature || '-'"></code></div>
   1384         <div class="tx-field" x-show="selectedTransaction?.tx?.encrypted_size || selectedTransaction?.tx?.encryptedSize"><span class="tx-label">Encrypted Bytes</span><span class="tx-value number" x-text="selectedTransaction?.tx?.encrypted_size || selectedTransaction?.tx?.encryptedSize"></span></div>
   1385         <div class="tx-field" x-show="selectedTransaction?.tx?.expires_at_height || selectedTransaction?.tx?.expiresAtHeight"><span class="tx-label">Expires</span><span class="tx-value number" x-text="selectedTransaction?.tx?.expires_at_height || selectedTransaction?.tx?.expiresAtHeight"></span></div>
   1386         <div class="tx-field" x-show="isMineTx(selectedTransaction?.tx)"><span class="tx-label">Difficulty</span><span class="tx-value number" x-text="txDifficultyBits(selectedTransaction?.tx) ?? '-'"></span></div>
   1387         <div class="tx-field" x-show="isMineTx(selectedTransaction?.tx)"><span class="tx-label">Proof Bits</span><span class="tx-value number" x-text="txProofBits(selectedTransaction?.tx) ?? '-'"></span></div>
   1388         <div class="tx-field" x-show="isMineTx(selectedTransaction?.tx)"><span class="tx-label">Proof Hash</span><code class="tx-value hash" x-text="txProofHash(selectedTransaction?.tx) || '-'"></code></div>
   1389       </div>
   1390       <div class="utxo-flow">
   1391         <div class="utxo-column">
   1392           <h3>Inputs</h3>
   1393           <template x-for="(input, index) in txInputs(selectedTransaction?.tx || {})" :key="txInputKey(input, index)">
   1394             <div class="utxo-node">
   1395               <div class="utxo-node-label"><span>Input <span x-text="index + 1"></span></span><span>spent</span></div>
   1396               <div class="utxo-node-ref" x-text="txInputOutpoint(input)"></div>
   1397               <div class="tx-field"><span class="tx-label">Value</span><span class="tx-value money" x-text="txInputAmountLabel(input)"></span></div>
   1398               <div class="tx-field"><span class="tx-label">Owner</span><code class="tx-value hash" x-text="addressLabel(input.owner)"></code></div>
   1399               <div class="tx-field"><span class="tx-label">Sig</span><code class="tx-value hash" x-text="short(input.signature)"></code></div>
   1400             </div>
   1401           </template>
   1402           <div class="tx-modal-empty" x-show="txInputs(selectedTransaction?.tx || {}).length === 0">No inputs</div>
   1403         </div>
   1404         <div class="utxo-arrow" aria-hidden="true">&rarr;</div>
   1405         <div class="utxo-column">
   1406           <h3>Outputs</h3>
   1407           <template x-for="(output, index) in txVisualOutputs(selectedTransaction?.tx || {})" :key="txOutputKey(output, index)">
   1408             <div class="utxo-node" :class="{ burned: output.kind === 'burned', fee: output.kind === 'fee' }">
   1409               <div class="utxo-node-label"><span x-text="output.label"></span><span x-text="output.kind"></span></div>
   1410               <div class="utxo-node-amount">IUNA <span x-text="amountLabel(output.amount)"></span></div>
   1411               <template x-if="output.address">
   1412                 <div class="tx-field"><span class="tx-label">To</span><code class="tx-value hash" x-text="addressLabel(output.address)"></code></div>
   1413               </template>
   1414               <template x-if="output.detail">
   1415                 <div class="tx-field"><span class="tx-label" x-text="output.detailLabel"></span><code class="tx-value hash" x-text="output.detail"></code></div>
   1416               </template>
   1417             </div>
   1418           </template>
   1419           <div class="tx-modal-empty" x-show="txVisualOutputs(selectedTransaction?.tx || {}).length === 0">No outputs</div>
   1420         </div>
   1421       </div>
   1422     </section>
   1423   </div>
   1424   <div class="setup-overlay transaction-overlay" x-show="addressBookPickerOpen" x-transition.opacity @click.self="closeAddressBookPicker()" role="dialog" aria-modal="true" aria-labelledby="address-book-picker-title">
   1425     <section class="tx-modal address-book-modal">
   1426       <div class="tx-modal-head">
   1427         <div class="tx-modal-title">
   1428           <span class="pill">Send</span>
   1429           <h2 id="address-book-picker-title">Choose Contact</h2>
   1430         </div>
   1431         <div class="address-book-actions">
   1432           <button class="icon-button" type="button" x-show="!addressBookModalOpen" @click="openAddressBookModal()" title="Add contact" aria-label="Add contact">
   1433             <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"></path><path d="M5 12h14"></path></svg>
   1434           </button>
   1435           <button class="icon-button" type="button" @click="closeAddressBookPicker()" title="Close" aria-label="Close">
   1436             <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>
   1437           </button>
   1438         </div>
   1439       </div>
   1440       <div class="address-book-picker-list" x-show="!addressBookModalOpen">
   1441         <template x-for="entry in addressBookEntries()" :key="entry.address">
   1442           <button class="address-book-picker-row" type="button" @click="selectTransferContact(entry.address)" :title="`Send to ${entry.name}`">
   1443             <span class="address-book-name" x-text="entry.name"></span>
   1444             <code class="tx-value hash" x-text="short(entry.address)"></code>
   1445           </button>
   1446         </template>
   1447         <div class="tx-modal-empty" x-show="addressBookEntries().length === 0">No saved addresses</div>
   1448       </div>
   1449       <form class="address-book-form" x-show="addressBookModalOpen" @submit.prevent="saveAddressBookEntry">
   1450         <label>Name<input x-model="addressBookDraftName" autocomplete="off" required></label>
   1451         <label>Address<input x-model="addressBookDraftAddress" autocomplete="off" required :class="{ invalid: addressBookDraftAddress && !validAddressBookAddress(addressBookDraftAddress) }"></label>
   1452         <div class="setup-feedback error" x-show="addressBookDraftAddress && !validAddressBookAddress(addressBookDraftAddress)">Address must be a 64 character hex public key</div>
   1453         <div class="address-book-modal-actions">
   1454           <button class="icon-button modal-delete-button" type="button" x-show="addressBookEditingAddress" @click="removeAddressBookEntry({ address: addressBookEditingAddress, name: addressBookDraftName || addressBookEditingAddress })" title="Delete contact" aria-label="Delete contact">
   1455             <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16"></path><path d="M10 11v6"></path><path d="M14 11v6"></path><path d="M6 7l1 14h10l1-14"></path><path d="M9 7V4h6v3"></path></svg>
   1456           </button>
   1457           <button class="icon-button" type="button" @click="closeAddressBookModal()" title="Back to contacts" aria-label="Back to contacts">
   1458             <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 18l-6-6 6-6"></path></svg>
   1459           </button>
   1460           <button class="primary icon-button" type="submit" title="Save contact" aria-label="Save contact">
   1461             <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z"></path><path d="M7 3v6h8"></path><path d="M7 21v-8h10v8"></path></svg>
   1462           </button>
   1463         </div>
   1464       </form>
   1465     </section>
   1466   </div>
   1467   <div class="setup-overlay" x-show="showingSetup()" x-transition.opacity role="dialog" aria-modal="true" aria-labelledby="setup-title">
   1468     <section class="setup-modal">
   1469       <div class="setup-modal-head">
   1470         <div class="setup-welcome">Welcome to iuna</div>
   1471         <h2 id="setup-title">Initial Setup</h2>
   1472         <div class="setup-copy">Connect this node to the network, then set up the local wallet.</div>
   1473       </div>
   1474       <div class="setup-feedback" :class="setupFeedback?.kind" x-show="setupFeedback" x-transition x-text="setupFeedback?.message"></div>
   1475       <div class="setup-grid">
   1476         <div class="setup-section setup-node-mode">
   1477           <div class="panel-head">
   1478             <h3>Mode</h3>
   1479             <span class="pill">Change later in Settings</span>
   1480           </div>
   1481           <div class="segmented setup-mode-picker" role="tablist" aria-label="Initial node mode">
   1482             <button type="button" :class="{ active: setupNodeMode === 'wallet' }" @click="selectSetupNodeMode('wallet')">Wallet</button>
   1483             <button type="button" :class="{ active: setupNodeMode === 'non-listening' }" @click="selectSetupNodeMode('non-listening')">Non-listening node</button>
   1484             <button type="button" :class="{ active: setupNodeMode === 'listening' }" @click="selectSetupNodeMode('listening')">Listening node</button>
   1485           </div>
   1486           <div class="setup-network-copy" x-text="setupNodeModeCopy()"></div>
   1487         </div>
   1488         <div class="setup-section setup-network">
   1489           <div class="panel-head">
   1490             <h3>Network</h3>
   1491             <a class="setup-network-link" href="https://getiuna.org/git/iuna/file/KNOWN_NODES.txt.html" target="_blank" rel="noreferrer">Known nodes</a>
   1492           </div>
   1493           <div class="setup-network-row">
   1494             <label><span x-text="setupRequiresPeer() ? 'Bootstrap peer (required)' : 'Bootstrap peer'"></span><input x-model="setupPeerAddress" placeholder="iuna.jhx.app:9444"></label>
   1495             <label x-show="setupNodeMode === 'listening'" x-transition>Bind port<input x-model.number="p2pBindPort" @input="p2pBindPortDirty = true" type="number" min="1" max="65535" step="1" required></label>
   1496           </div>
   1497           <div class="setup-network-copy" x-text="setupRequiresPeer() ? 'A bootstrap peer is required before this node can join the network. Known nodes help discovery; they do not control your wallet or decide valid blocks.' : 'You can add a bootstrap peer now or later from the P2P screen. Known nodes help discovery; they do not control your wallet or decide valid blocks.'"></div>
   1498         </div>
   1499         <div class="setup-section setup-wallet-section seed-panel">
   1500           <div class="panel-head">
   1501             <h3>Wallet</h3>
   1502           </div>
   1503           <div class="segmented" role="tablist" aria-label="Wallet setup mode">
   1504             <button type="button" :class="{ active: setupWalletMode === 'create' }" @click="selectSetupWalletMode('create')">Create</button>
   1505             <button type="button" :class="{ active: setupWalletMode === 'import' }" @click="selectSetupWalletMode('import')">Import</button>
   1506           </div>
   1507           <div x-show="setupWalletMode === 'create'" class="seed-panel">
   1508             <div class="setup-field">
   1509               <div class="setup-field-label">Address</div>
   1510               <div class="address-box setup-address-box">
   1511                 <code x-text="setupAddress()"></code>
   1512                 <button type="button" @click="copyAddress">Copy</button>
   1513               </div>
   1514             </div>
   1515             <template x-if="setupSeedWords().length > 0 && setupSeedStep === 'write'">
   1516               <div class="seed-panel">
   1517                 <div class="setup-field">
   1518                   <div class="setup-field-label">Recovery phrase</div>
   1519                   <div class="seed-grid">
   1520                     <template x-for="(word, index) in setupSeedWords()" :key="index">
   1521                       <div class="seed-word">
   1522                         <span class="index" x-text="index + 1"></span>
   1523                         <span class="word" x-text="word"></span>
   1524                       </div>
   1525                     </template>
   1526                   </div>
   1527                 </div>
   1528                 <div class="setup-actions">
   1529                   <button type="button" class="subtle" @click="generateSetupSeed">Regenerate</button>
   1530                   <button type="button" class="subtle" x-show="setupWallet.dev_verify_bypass" @click="skipSeedVerificationForDev">Skip verification</button>
   1531                   <button type="button" class="primary" @click="beginSeedVerification">I wrote it down</button>
   1532                 </div>
   1533               </div>
   1534             </template>
   1535             <template x-if="setupSeedWords().length === 0">
   1536               <div class="seed-panel">
   1537                 <div class="muted">This wallet does not have a recovery phrase yet.</div>
   1538                 <button type="button" class="primary" @click="generateSetupSeed">Generate recovery phrase</button>
   1539               </div>
   1540             </template>
   1541             <template x-if="setupSeedStep === 'verify'">
   1542               <div class="seed-panel">
   1543                 <div class="verify-grid">
   1544                   <template x-for="challenge in verifyChallenges" :key="challenge.index">
   1545                     <label>
   1546                       <span>Word <span x-text="challenge.position"></span></span>
   1547                       <input x-model="verifyAnswers[challenge.index]" autocomplete="off">
   1548                     </label>
   1549                   </template>
   1550                 </div>
   1551                 <div class="setup-actions">
   1552                   <button type="button" class="subtle" @click="setupSeedStep = 'write'">Back</button>
   1553                   <button type="button" class="primary" @click="verifyGeneratedSeed">Verify</button>
   1554                 </div>
   1555               </div>
   1556             </template>
   1557             <template x-if="setupSeedStep === 'verified' && walletVerified">
   1558               <div class="setup-status">Recovery phrase verified</div>
   1559             </template>
   1560           </div>
   1561           <div x-show="setupWalletMode === 'import'" class="seed-panel">
   1562             <form @submit.prevent="importSetupSeed">
   1563               <label>Recovery phrase<textarea x-model="importSeedPhrase" autocomplete="off" spellcheck="false" placeholder="24 words, separated by spaces or new lines"></textarea></label>
   1564               <button class="primary" type="submit">Import</button>
   1565             </form>
   1566             <template x-if="walletVerified">
   1567               <div class="setup-status">Recovery phrase imported</div>
   1568             </template>
   1569           </div>
   1570         </div>
   1571       </div>
   1572       <div class="setup-actions">
   1573         <button class="primary" type="button" :disabled="!setupCanContinue()" @click="completeSetup">Continue</button>
   1574       </div>
   1575     </section>
   1576   </div>
   1577 </body>
   1578 </html>"#;