iuna

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

index_html.rs (126237B)


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