blockyard 0.0.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +359 -1
  2. package/README.md +48 -27
  3. package/SECURITY.md +2 -2
  4. package/bin/blockyard.js +2 -1
  5. package/docs/API.md +17 -15
  6. package/docs/ARCHITECTURE.md +128 -10
  7. package/docs/CONFIGURATION.md +39 -30
  8. package/docs/DEFECTS.md +4 -1
  9. package/docs/GETTING-STARTED.md +18 -8
  10. package/docs/INSTALL.md +97 -37
  11. package/docs/MEASUREMENTS.md +147 -0
  12. package/docs/PLAN-SCORCHED-YARD.md +456 -0
  13. package/docs/PLAN-SKIES.md +142 -0
  14. package/docs/SECURITY-AUDIT-2026-09-16.md +647 -0
  15. package/docs/SECURITY.md +58 -22
  16. package/docs/TROUBLESHOOTING.md +44 -5
  17. package/docs/USER-GUIDE.md +505 -35
  18. package/package.json +4 -2
  19. package/public/404.html +1 -1
  20. package/public/css/app.css +393 -82
  21. package/public/donate-qr.png +0 -0
  22. package/public/index.html +353 -109
  23. package/public/js/agents.js +228 -51
  24. package/public/js/app.js +131 -16
  25. package/public/js/blockanoid.js +15 -7
  26. package/public/js/blockout.js +15 -7
  27. package/public/js/blockscene3d.js +230 -38
  28. package/public/js/charts.js +21 -21
  29. package/public/js/depthchart.js +31 -27
  30. package/public/js/details3d.js +1481 -73
  31. package/public/js/doom.js +31 -0
  32. package/public/js/dosaudio.js +48 -0
  33. package/public/js/dosgame.js +389 -0
  34. package/public/js/dosio.js +186 -0
  35. package/public/js/dospc.js +1353 -0
  36. package/public/js/dosworker.js +196 -0
  37. package/public/js/explorer.js +7 -1
  38. package/public/js/livingsky.js +494 -0
  39. package/public/js/login.js +8 -2
  40. package/public/js/markets.js +46 -8
  41. package/public/js/mining.js +314 -36
  42. package/public/js/panels.js +41 -28
  43. package/public/js/pricechart.js +14 -13
  44. package/public/js/quake.js +20 -0
  45. package/public/js/safenext.js +14 -0
  46. package/public/js/scorched.js +1051 -0
  47. package/public/js/scorchedai.js +227 -0
  48. package/public/js/scorchedair.js +286 -0
  49. package/public/js/scorchedfx.js +376 -0
  50. package/public/js/scorchedshop.js +105 -0
  51. package/public/js/scorchedwind.js +69 -0
  52. package/public/js/scorchedyard.js +1338 -0
  53. package/public/js/settings.js +368 -100
  54. package/public/js/soundcard.js +459 -0
  55. package/public/js/tetrust.js +15 -6
  56. package/public/js/tetsound.js +35 -5
  57. package/public/js/theme.js +235 -0
  58. package/public/js/wolf3d.js +22 -0
  59. package/public/js/x86.js +1978 -0
  60. package/scripts/check.js +46 -0
  61. package/scripts/donate-qr.py +12 -9
  62. package/scripts/dos-bench.js +56 -0
  63. package/scripts/index-build.js +9 -2
  64. package/scripts/pool-map.js +152 -36
  65. package/scripts/setup.js +142 -22
  66. package/scripts/shots.mjs +27 -0
  67. package/scripts/smoke.sh +7 -6
  68. package/scripts/tls.js +31 -0
  69. package/scripts/ui.js +4 -2
  70. package/server/auth/sessions.js +33 -13
  71. package/server/chain/blockfile.js +64 -5
  72. package/server/chain/index/build.js +451 -58
  73. package/server/chain/index/heights.js +29 -3
  74. package/server/chain/index/live.js +13 -7
  75. package/server/chain/index/rows.js +6 -1
  76. package/server/chain/index/store.js +28 -5
  77. package/server/chain/index/worker.js +23 -11
  78. package/server/collect/logparse.js +65 -18
  79. package/server/collect/markets.js +76 -7
  80. package/server/collect/mining.js +32 -0
  81. package/server/collect/monitor.js +54 -12
  82. package/server/collect/network.js +305 -0
  83. package/server/config.js +53 -22
  84. package/server/http/api.js +119 -18
  85. package/server/http/games.js +77 -0
  86. package/server/http/server.js +30 -5
  87. package/server/http/sse.js +53 -7
  88. package/server/main.js +66 -11
  89. package/server/rpc/allowlist.js +26 -0
  90. package/server/rpc/client.js +30 -2
  91. package/server/store/audit.js +6 -1
  92. package/server/store/history.js +19 -3
  93. package/server/store/ledger.js +15 -4
  94. package/server/tls/selfsigned.js +160 -0
  95. package/systemd/blockyard.service +41 -8
  96. package/docs/PRIVATE-LEADERBOARD.md +0 -230
  97. package/docs/STATE-2026-09-09.md +0 -200
package/public/index.html CHANGED
@@ -99,10 +99,23 @@
99
99
  <button data-page="tetrust" role="menuitem" title="Tetrust: trust, but verify">Tetrust</button>
100
100
  <button data-page="blockout" role="menuitem" title="Blockout: break the wall">Blockout</button>
101
101
  <button data-page="blockanoid" role="menuitem" title="Blockanoid: Vaus, capsules and the minions">Blockanoid</button>
102
+ <button data-page="scorched" role="menuitem" title="Scorched Yard: artillery on a deformable block landscape">Scorched Yard</button>
103
+ <button data-page="wolf3d" role="menuitem" title="Wolfenstein 3D: the shareware WOLF3D.EXE on an emulated PC">Wolfenstein 3D</button>
104
+ <button data-page="doom" role="menuitem" title="DOOM: the shareware DOOM.EXE on an emulated 486">DOOM</button>
105
+ <button data-page="quake" role="menuitem" title="Quake: the shareware QUAKE.EXE on an emulated PC">Quake</button>
102
106
  </div>
103
107
 
104
108
  <!-- Display settings (settings.js). Hidden until the gear is pressed; the contents are
105
109
  rendered from settings.js PANEL, so a value and its control cannot drift apart. -->
110
+ <!-- "View more »" on the Mining tab's cards (2026-09-15): the same card, full screen, drawn by
111
+ mining.js renderExpand while it is open; Esc, the scrim or the close button shut it. -->
112
+ <div class="expandwrap hidden" id="expandWrap" role="dialog" aria-modal="true" aria-labelledby="expandTitle">
113
+ <div class="cfgscrim" id="expandScrim"></div>
114
+ <section class="expand card">
115
+ <h3><span id="expandTitle">…</span> <span class="sp"></span><span class="src" id="expandSrc"></span> <button type="button" class="btn" id="expandClose" aria-label="Close">close</button></h3>
116
+ <div class="expandbody" id="expandBody"></div>
117
+ </section>
118
+ </div>
106
119
  <div class="cfgwrap hidden" id="settingsWrap">
107
120
  <div class="cfgscrim" id="settingsScrim"></div>
108
121
  <section class="cfg" id="settingsPanel" role="dialog" aria-modal="true" aria-labelledby="cfgTitle">
@@ -110,8 +123,7 @@
110
123
  <button class="btn" id="cfgReset" title="Put every setting back to the shipped default">reset</button>
111
124
  <button class="btn" id="cfgClose" aria-label="Close settings">close</button>
112
125
  </h2>
113
- <p class="cfgnote">Kept in this browser only, and applied as soon as you change them. They
114
- change what is drawn, not what is measured: every number on the page is the same either way.</p>
126
+ <p class="cfgnote">Saved on the server, so every screen sees the same board; applied as you change them. They change what is drawn, never what is measured.</p>
115
127
  <div id="cfgBody"></div>
116
128
  </section>
117
129
  </div>
@@ -139,7 +151,7 @@
139
151
  is left and scrolls inside it. -->
140
152
  <div class="ovcols">
141
153
  <div class="ovcol">
142
- <div class="card w8">
154
+ <div class="card w8 flowtight">
143
155
  <h3>Block flow <span class="sp"></span><span class="src">being assembled → <b>the current block</b> → history · <a href="#mining">packages &amp; goggles</a></span></h3>
144
156
  <div id="ovTrain" class="flowwrap"></div>
145
157
  </div>
@@ -256,7 +268,7 @@
256
268
  <div class="card w4">
257
269
  <h3>Transaction rate <span class="sp"></span><span class="src">getchaintxstats txrate</span></h3>
258
270
  <canvas class="chart" id="chTxRateChart"></canvas>
259
- <div class="kv two mt-6" id="chTxStats"></div>
271
+ <div class="mksum mt-6 chtx" id="chTxStats"></div>
260
272
  </div>
261
273
 
262
274
  <div class="card w4">
@@ -333,6 +345,7 @@
333
345
  <section class="abcard"><h3>Bitcoin node</h3><div id="abNode"></div></section>
334
346
  </div>
335
347
 
348
+ <div class="ablinks">
336
349
  <a class="abgh" href="https://github.com/BobClawblaw/blockyard" target="_blank" rel="noopener noreferrer">
337
350
  <!-- The GitHub mark, GitHub's trademark, used to link to the project on GitHub. -->
338
351
  <svg viewBox="0 0 16 16" width="20" height="20" aria-hidden="true" focusable="false" fill="currentColor"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
@@ -342,14 +355,17 @@
342
355
  <!-- The donation address: one click copies it (operator, 2026-09-14). A button, not a link:
343
356
  there is nowhere to go, and the address is the whole point. -->
344
357
  <button type="button" class="abdon" id="abDonate" data-copy="bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc" title="click to copy the address">
345
- <!-- The QR is the address itself, generated once (scripts/donate-qr.py) and checked to decode back to it. -->
346
- <svg class="abqr" viewBox="0 0 41 41" width="132" height="132" role="img" aria-label="QR code of the donation address" shape-rendering="crispEdges"><rect width="41" height="41" fill="#fff"/><path fill="#000" d="M6 6h1v1h-1zM7 6h1v1h-1zM8 6h1v1h-1zM9 6h1v1h-1zM10 6h1v1h-1zM11 6h1v1h-1zM12 6h1v1h-1zM16 6h1v1h-1zM18 6h1v1h-1zM20 6h1v1h-1zM22 6h1v1h-1zM23 6h1v1h-1zM24 6h1v1h-1zM25 6h1v1h-1zM26 6h1v1h-1zM28 6h1v1h-1zM29 6h1v1h-1zM30 6h1v1h-1zM31 6h1v1h-1zM32 6h1v1h-1zM33 6h1v1h-1zM34 6h1v1h-1zM6 7h1v1h-1zM12 7h1v1h-1zM14 7h1v1h-1zM16 7h1v1h-1zM19 7h1v1h-1zM20 7h1v1h-1zM21 7h1v1h-1zM23 7h1v1h-1zM24 7h1v1h-1zM28 7h1v1h-1zM34 7h1v1h-1zM6 8h1v1h-1zM8 8h1v1h-1zM9 8h1v1h-1zM10 8h1v1h-1zM12 8h1v1h-1zM18 8h1v1h-1zM20 8h1v1h-1zM21 8h1v1h-1zM25 8h1v1h-1zM26 8h1v1h-1zM28 8h1v1h-1zM30 8h1v1h-1zM31 8h1v1h-1zM32 8h1v1h-1zM34 8h1v1h-1zM6 9h1v1h-1zM8 9h1v1h-1zM9 9h1v1h-1zM10 9h1v1h-1zM12 9h1v1h-1zM14 9h1v1h-1zM15 9h1v1h-1zM16 9h1v1h-1zM18 9h1v1h-1zM19 9h1v1h-1zM20 9h1v1h-1zM22 9h1v1h-1zM23 9h1v1h-1zM26 9h1v1h-1zM28 9h1v1h-1zM30 9h1v1h-1zM31 9h1v1h-1zM32 9h1v1h-1zM34 9h1v1h-1zM6 10h1v1h-1zM8 10h1v1h-1zM9 10h1v1h-1zM10 10h1v1h-1zM12 10h1v1h-1zM15 10h1v1h-1zM20 10h1v1h-1zM22 10h1v1h-1zM24 10h1v1h-1zM25 10h1v1h-1zM26 10h1v1h-1zM28 10h1v1h-1zM30 10h1v1h-1zM31 10h1v1h-1zM32 10h1v1h-1zM34 10h1v1h-1zM6 11h1v1h-1zM12 11h1v1h-1zM14 11h1v1h-1zM15 11h1v1h-1zM17 11h1v1h-1zM18 11h1v1h-1zM19 11h1v1h-1zM20 11h1v1h-1zM21 11h1v1h-1zM22 11h1v1h-1zM23 11h1v1h-1zM24 11h1v1h-1zM26 11h1v1h-1zM28 11h1v1h-1zM34 11h1v1h-1zM6 12h1v1h-1zM7 12h1v1h-1zM8 12h1v1h-1zM9 12h1v1h-1zM10 12h1v1h-1zM11 12h1v1h-1zM12 12h1v1h-1zM14 12h1v1h-1zM16 12h1v1h-1zM18 12h1v1h-1zM20 12h1v1h-1zM22 12h1v1h-1zM24 12h1v1h-1zM26 12h1v1h-1zM28 12h1v1h-1zM29 12h1v1h-1zM30 12h1v1h-1zM31 12h1v1h-1zM32 12h1v1h-1zM33 12h1v1h-1zM34 12h1v1h-1zM15 13h1v1h-1zM16 13h1v1h-1zM17 13h1v1h-1zM22 13h1v1h-1zM25 13h1v1h-1zM6 14h1v1h-1zM7 14h1v1h-1zM8 14h1v1h-1zM9 14h1v1h-1zM10 14h1v1h-1zM12 14h1v1h-1zM13 14h1v1h-1zM14 14h1v1h-1zM15 14h1v1h-1zM17 14h1v1h-1zM19 14h1v1h-1zM21 14h1v1h-1zM22 14h1v1h-1zM23 14h1v1h-1zM26 14h1v1h-1zM27 14h1v1h-1zM29 14h1v1h-1zM31 14h1v1h-1zM33 14h1v1h-1zM7 15h1v1h-1zM10 15h1v1h-1zM11 15h1v1h-1zM13 15h1v1h-1zM14 15h1v1h-1zM16 15h1v1h-1zM18 15h1v1h-1zM20 15h1v1h-1zM22 15h1v1h-1zM23 15h1v1h-1zM25 15h1v1h-1zM27 15h1v1h-1zM28 15h1v1h-1zM29 15h1v1h-1zM30 15h1v1h-1zM34 15h1v1h-1zM7 16h1v1h-1zM11 16h1v1h-1zM12 16h1v1h-1zM13 16h1v1h-1zM16 16h1v1h-1zM20 16h1v1h-1zM21 16h1v1h-1zM23 16h1v1h-1zM24 16h1v1h-1zM26 16h1v1h-1zM27 16h1v1h-1zM30 16h1v1h-1zM31 16h1v1h-1zM6 17h1v1h-1zM8 17h1v1h-1zM9 17h1v1h-1zM10 17h1v1h-1zM11 17h1v1h-1zM14 17h1v1h-1zM18 17h1v1h-1zM21 17h1v1h-1zM24 17h1v1h-1zM26 17h1v1h-1zM28 17h1v1h-1zM29 17h1v1h-1zM7 18h1v1h-1zM8 18h1v1h-1zM9 18h1v1h-1zM10 18h1v1h-1zM11 18h1v1h-1zM12 18h1v1h-1zM13 18h1v1h-1zM14 18h1v1h-1zM15 18h1v1h-1zM16 18h1v1h-1zM18 18h1v1h-1zM19 18h1v1h-1zM23 18h1v1h-1zM26 18h1v1h-1zM27 18h1v1h-1zM31 18h1v1h-1zM32 18h1v1h-1zM33 18h1v1h-1zM8 19h1v1h-1zM9 19h1v1h-1zM10 19h1v1h-1zM11 19h1v1h-1zM13 19h1v1h-1zM14 19h1v1h-1zM20 19h1v1h-1zM22 19h1v1h-1zM24 19h1v1h-1zM25 19h1v1h-1zM28 19h1v1h-1zM29 19h1v1h-1zM30 19h1v1h-1zM34 19h1v1h-1zM7 20h1v1h-1zM8 20h1v1h-1zM9 20h1v1h-1zM11 20h1v1h-1zM12 20h1v1h-1zM13 20h1v1h-1zM14 20h1v1h-1zM17 20h1v1h-1zM18 20h1v1h-1zM19 20h1v1h-1zM21 20h1v1h-1zM23 20h1v1h-1zM27 20h1v1h-1zM29 20h1v1h-1zM32 20h1v1h-1zM33 20h1v1h-1zM7 21h1v1h-1zM9 21h1v1h-1zM11 21h1v1h-1zM13 21h1v1h-1zM14 21h1v1h-1zM17 21h1v1h-1zM27 21h1v1h-1zM28 21h1v1h-1zM29 21h1v1h-1zM30 21h1v1h-1zM34 21h1v1h-1zM8 22h1v1h-1zM9 22h1v1h-1zM10 22h1v1h-1zM11 22h1v1h-1zM12 22h1v1h-1zM17 22h1v1h-1zM19 22h1v1h-1zM20 22h1v1h-1zM22 22h1v1h-1zM23 22h1v1h-1zM25 22h1v1h-1zM26 22h1v1h-1zM29 22h1v1h-1zM31 22h1v1h-1zM32 22h1v1h-1zM33 22h1v1h-1zM6 23h1v1h-1zM10 23h1v1h-1zM11 23h1v1h-1zM13 23h1v1h-1zM14 23h1v1h-1zM15 23h1v1h-1zM18 23h1v1h-1zM22 23h1v1h-1zM24 23h1v1h-1zM25 23h1v1h-1zM26 23h1v1h-1zM28 23h1v1h-1zM30 23h1v1h-1zM31 23h1v1h-1zM33 23h1v1h-1zM34 23h1v1h-1zM6 24h1v1h-1zM8 24h1v1h-1zM10 24h1v1h-1zM12 24h1v1h-1zM13 24h1v1h-1zM14 24h1v1h-1zM19 24h1v1h-1zM20 24h1v1h-1zM21 24h1v1h-1zM23 24h1v1h-1zM27 24h1v1h-1zM6 25h1v1h-1zM9 25h1v1h-1zM10 25h1v1h-1zM13 25h1v1h-1zM16 25h1v1h-1zM18 25h1v1h-1zM20 25h1v1h-1zM24 25h1v1h-1zM30 25h1v1h-1zM33 25h1v1h-1zM34 25h1v1h-1zM6 26h1v1h-1zM7 26h1v1h-1zM8 26h1v1h-1zM12 26h1v1h-1zM13 26h1v1h-1zM14 26h1v1h-1zM15 26h1v1h-1zM18 26h1v1h-1zM19 26h1v1h-1zM20 26h1v1h-1zM23 26h1v1h-1zM26 26h1v1h-1zM27 26h1v1h-1zM28 26h1v1h-1zM29 26h1v1h-1zM30 26h1v1h-1zM31 26h1v1h-1zM32 26h1v1h-1zM33 26h1v1h-1zM14 27h1v1h-1zM22 27h1v1h-1zM25 27h1v1h-1zM26 27h1v1h-1zM30 27h1v1h-1zM32 27h1v1h-1zM33 27h1v1h-1zM34 27h1v1h-1zM6 28h1v1h-1zM7 28h1v1h-1zM8 28h1v1h-1zM9 28h1v1h-1zM10 28h1v1h-1zM11 28h1v1h-1zM12 28h1v1h-1zM14 28h1v1h-1zM16 28h1v1h-1zM17 28h1v1h-1zM18 28h1v1h-1zM19 28h1v1h-1zM21 28h1v1h-1zM22 28h1v1h-1zM23 28h1v1h-1zM25 28h1v1h-1zM26 28h1v1h-1zM28 28h1v1h-1zM30 28h1v1h-1zM31 28h1v1h-1zM6 29h1v1h-1zM12 29h1v1h-1zM15 29h1v1h-1zM17 29h1v1h-1zM20 29h1v1h-1zM24 29h1v1h-1zM26 29h1v1h-1zM30 29h1v1h-1zM31 29h1v1h-1zM33 29h1v1h-1zM34 29h1v1h-1zM6 30h1v1h-1zM8 30h1v1h-1zM9 30h1v1h-1zM10 30h1v1h-1zM12 30h1v1h-1zM14 30h1v1h-1zM17 30h1v1h-1zM21 30h1v1h-1zM22 30h1v1h-1zM23 30h1v1h-1zM26 30h1v1h-1zM27 30h1v1h-1zM28 30h1v1h-1zM29 30h1v1h-1zM30 30h1v1h-1zM32 30h1v1h-1zM33 30h1v1h-1zM34 30h1v1h-1zM6 31h1v1h-1zM8 31h1v1h-1zM9 31h1v1h-1zM10 31h1v1h-1zM12 31h1v1h-1zM14 31h1v1h-1zM18 31h1v1h-1zM19 31h1v1h-1zM22 31h1v1h-1zM25 31h1v1h-1zM27 31h1v1h-1zM29 31h1v1h-1zM33 31h1v1h-1zM34 31h1v1h-1zM6 32h1v1h-1zM8 32h1v1h-1zM9 32h1v1h-1zM10 32h1v1h-1zM12 32h1v1h-1zM14 32h1v1h-1zM16 32h1v1h-1zM19 32h1v1h-1zM20 32h1v1h-1zM21 32h1v1h-1zM22 32h1v1h-1zM23 32h1v1h-1zM26 32h1v1h-1zM28 32h1v1h-1zM30 32h1v1h-1zM31 32h1v1h-1zM32 32h1v1h-1zM33 32h1v1h-1zM6 33h1v1h-1zM12 33h1v1h-1zM14 33h1v1h-1zM15 33h1v1h-1zM18 33h1v1h-1zM19 33h1v1h-1zM22 33h1v1h-1zM24 33h1v1h-1zM25 33h1v1h-1zM26 33h1v1h-1zM31 33h1v1h-1zM33 33h1v1h-1zM6 34h1v1h-1zM7 34h1v1h-1zM8 34h1v1h-1zM9 34h1v1h-1zM10 34h1v1h-1zM11 34h1v1h-1zM12 34h1v1h-1zM14 34h1v1h-1zM16 34h1v1h-1zM18 34h1v1h-1zM20 34h1v1h-1zM21 34h1v1h-1zM23 34h1v1h-1zM25 34h1v1h-1zM28 34h1v1h-1zM29 34h1v1h-1zM30 34h1v1h-1zM31 34h1v1h-1zM32 34h1v1h-1z"/></svg>
358
+ <!-- The QR is the address itself, generated once (scripts/donate-qr.py) and checked to decode back to it.
359
+ A PNG, not an inline SVG: Safari on the first Mac install painted the SVG's white ground and
360
+ not one of its 800 module rects (2026-09-15), and a bitmap is painted the same everywhere. -->
361
+ <img class="abqr" src="/donate-qr.png" width="132" height="132" alt="QR code of the donation address" decoding="async">
347
362
  <span class="abdontext">
348
363
  <span class="abdonlbl"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="11" fill="var(--accent)"/><path fill="#1a1206" d="M9.2 6.2h1.5V4.8h1.6v1.4h1.1V4.8H15v1.5c1.6.3 2.6 1.2 2.6 2.7 0 1-.5 1.8-1.3 2.2 1.2.4 1.9 1.3 1.9 2.6 0 1.8-1.3 2.8-3.2 3v1.5h-1.6v-1.4h-1.1v1.4h-1.6v-1.5H9.2v-1.8h1.1V8h-1.1zm2.6 3.9h1.7c1 0 1.6-.4 1.6-1.2 0-.8-.6-1.2-1.6-1.2h-1.7zm0 4.4h2c1.1 0 1.8-.5 1.8-1.3s-.7-1.3-1.8-1.3h-2z"/></svg> If BlockYard is useful, tips are welcome</span>
349
364
  <code>bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc</code>
350
365
  <span class="abdonhint">scan, or click to copy</span>
351
366
  </span>
352
367
  </button>
368
+ </div>
353
369
 
354
370
  <p class="abcredit">Entirely made by machines. Directed by human hands.</p>
355
371
  </div>
@@ -465,12 +481,13 @@
465
481
  <div class="khead"><b>Block space</b><span class="faint">the next block's worth of the mempool</span></div>
466
482
  <div class="treemapwrap kboard"><canvas class="treemap" id="kSpace"></canvas><div class="viewer-ctl"><div class="refresh-in" data-refresh></div><button type="button" class="refresh-now" data-refresh-now disabled>refresh now</button></div><div class="goggles-tip hidden"></div></div>
467
483
  </div>
468
- <div class="kpanel kbf">
469
- <div class="khead"><b>Block flow</b><span class="faint">projected being built the chain</span></div>
484
+ <div class="kpanel kbf flowtight">
485
+ <!-- the full-screen button lives in this head now (2026-09-15): floated at the kiosk's
486
+ corner it sat over the flow's cards once the strip was tightened -->
487
+ <div class="khead"><b>Block flow</b><span class="faint">projected → being built → the chain</span><span class="sp"></span><button type="button" class="kfull" id="kFull" title="full screen">full screen</button></div>
470
488
  <div id="kTrain" class="flowwrap"></div>
471
489
  </div>
472
490
  </div>
473
- <button type="button" class="kfull" id="kFull" title="full screen">full screen</button>
474
491
  </div>
475
492
  </section>
476
493
  <!-- TETRUST: a Tetris on the 3D engine (operator, 2026-09-12). The well is a board3d, so it wears
@@ -499,8 +516,7 @@
499
516
  <div class="tetkeys">move the mouse over the court, or ← → / A D · space or click serves · P pause · Enter play</div>
500
517
  <h3>Switches</h3>
501
518
  <div class="tetswitches">
502
- <button type="button" class="tetsw" id="boStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
503
- <button type="button" class="tetsw" id="boGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
519
+ <button type="button" class="tetsw" id="boSkySw" aria-pressed="false" title="the sky behind the court: Galaxy, Earth, or none">☀ sky</button>
504
520
  <button type="button" class="tetsw" id="boNeon" aria-pressed="false" title="the wall under lit neon tubes">◉ neon</button>
505
521
  <button type="button" class="tetsw" id="boSfx" aria-pressed="false" title="bat, bricks, walls and a lost ball">♫ sound</button>
506
522
  </div>
@@ -544,8 +560,7 @@
544
560
  <div class="tetkeys">move the mouse over the court, or ← → / A D · space or click serves · the laser fires itself while armed · P pause · Enter play</div>
545
561
  <h3>Switches</h3>
546
562
  <div class="tetswitches">
547
- <button type="button" class="tetsw" id="baStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
548
- <button type="button" class="tetsw" id="baGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
563
+ <button type="button" class="tetsw" id="baSkySw" aria-pressed="false" title="the sky behind the court: Galaxy, Earth, or none">☀ sky</button>
549
564
  <button type="button" class="tetsw" id="baNeon" aria-pressed="false" title="the wall under lit neon tubes">◉ neon</button>
550
565
  <button type="button" class="tetsw" id="baSfx" aria-pressed="false" title="Vaus, bricks, capsules, the laser and a lost ball">♫ sound</button>
551
566
  </div>
@@ -578,8 +593,7 @@
578
593
  <div class="tetkeys">← → or A D move · ↑ W or X rotate · Z or Q rotate back · ↓ or S soft drop · space hard drop · P pause · Enter play</div>
579
594
  <h3>Switches</h3>
580
595
  <div class="tetswitches">
581
- <button type="button" class="tetsw" id="tetStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
582
- <button type="button" class="tetsw" id="tetGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
596
+ <button type="button" class="tetsw" id="tetSkySw" aria-pressed="false" title="the sky behind the well: Galaxy, Earth, or none">☀ sky</button>
583
597
  <button type="button" class="tetsw" id="tetMusic" aria-pressed="false" title="Korobeiniki, on oscillators">♪ music</button>
584
598
  <button type="button" class="tetsw" id="tetSfx" aria-pressed="false" title="move, rotate, drop, clear, game over">♫ sound</button>
585
599
  </div>
@@ -589,6 +603,164 @@
589
603
  </div>
590
604
  </section>
591
605
 
606
+ <section class="page" data-page="scorched">
607
+ <div class="tetrust" id="scorched">
608
+ <div class="card tetcard">
609
+ <canvas class="tetsky" id="sySky"></canvas>
610
+ <h3>Scorched Yard <span class="sp"></span><span class="src">artillery on the block space — the land is dirt, and it falls</span></h3>
611
+ <div class="treemapwrap tetwell seyard idle" id="syFieldWrap">
612
+ <canvas class="sywind" id="syWind"></canvas>
613
+ <canvas class="treemap" id="syLand"></canvas>
614
+ <canvas class="treemap" id="syField"></canvas>
615
+ <div class="syweapons hidden" id="syWeapons"></div>
616
+ <div class="sytalk hidden" id="syTalk"></div>
617
+ </div>
618
+ <div class="tetover" id="syOver">
619
+ <div class="tetmsg" id="syMsg">Scorched Yard</div>
620
+ <div class="tetsub" id="sySub"></div>
621
+ <button type="button" class="btn primary" id="syResume">play</button>
622
+ <div class="syshop hidden" id="syShop"></div>
623
+ </div>
624
+ </div>
625
+ <div class="card tethud">
626
+ <h3>This turn</h3>
627
+ <div class="tetstats" id="syStats"></div>
628
+ <button type="button" class="btn primary syfire" id="syFire" disabled>fire</button>
629
+ <button type="button" class="btn syrestart" id="syRestart" title="start a new war from round one (F2)">restart</button>
630
+ <h3>Items</h3>
631
+ <div class="syitems" id="syItems"></div>
632
+ <h3>Tanks</h3>
633
+ <div class="sytanks" id="syTanks"></div>
634
+ <h3>Keys</h3>
635
+ <table class="tetkeys sykeys">
636
+ <tr><th>aim</th><td>← → angle · ↑ ↓ power · held keys speed up · shift fine · ctrl coarse · , . power ±1</td></tr>
637
+ <tr><th>mouse</th><td>drag the field to aim · press your tank to turn the barrel only · wheel: power, shift+wheel: angle · click a readout to type it</td></tr>
638
+ <tr><th>target</th><td>click an enemy tank to mark it · C corrects power from your last miss</td></tr>
639
+ <tr><th>weapons</th><td>W the grid · 1–9 pick · Q your last · [ ] cycle</td></tr>
640
+ <tr><th>fire</th><td>space or the button · R repeats the last shot</td></tr>
641
+ <tr><th>items</th><td>click a pill · B battery · S shield · T trigger · H heat guidance · A D drive</td></tr>
642
+ <tr><th>game</th><td>P pause · N next round · F2 restart</td></tr>
643
+ </table>
644
+ <h3>Switches</h3>
645
+ <div class="tetswitches">
646
+ <button type="button" class="tetsw" id="sySkySw" aria-pressed="false" title="the sky behind the field: Galaxy, Earth, or none">☀ sky</button>
647
+ <button type="button" class="tetsw" id="syMusic" aria-pressed="false" title="a march in D minor, on oscillators">♪ music</button>
648
+ <button type="button" class="tetsw" id="sySfx" aria-pressed="false" title="the shot, the blast, a hit, a death">♫ sound</button>
649
+ <button type="button" class="tetsw" id="syTalkSw" aria-pressed="false" title="what the tanks say">💬 talk</button>
650
+ <button type="button" class="tetsw" id="syFast" aria-pressed="false" title="shells at three times the pace">⏩ fast</button>
651
+ <button type="button" class="tetsw" id="syCheat" aria-pressed="false" title="cheat mode: the firing solution drawn while you aim">◎ cheat</button>
652
+ <button type="button" class="tetsw" id="syDemo" aria-pressed="false" title="attract mode: the computer takes every seat and plays on by itself">▶ watch</button>
653
+ </div>
654
+ <h3>High scores <span class="sp"></span><span class="src">this browser</span></h3>
655
+ <table class="tetscores" id="syScores"></table>
656
+ </div>
657
+ </div>
658
+ </section>
659
+
660
+ <!-- WOLFENSTEIN 3D (public/js/wolf3d.js): the shareware WOLF3D.EXE v1.4 from games/wolf3d_dos/,
661
+ unmodified, on the same emulated PC -- this time in real mode, as a 16-bit DOS program. -->
662
+ <section class="page" data-page="wolf3d">
663
+ <div class="tetrust dosgame" id="wolf3d">
664
+ <div class="card doscard">
665
+ <h3>Wolfenstein 3D <span class="sp"></span><span class="src">shareware v1.4 · Escape from Wolfenstein · on an emulated PC</span></h3>
666
+ <div class="doswrap" id="wolfWrap">
667
+ <canvas class="dosscreen" id="wolfScreen" width="320" height="200"></canvas>
668
+ <div class="tetover" id="wolfOver">
669
+ <div class="tetmsg" id="wolfMsg">Wolfenstein 3D</div>
670
+ <div class="tetsub" id="wolfSub"></div>
671
+ <button type="button" class="btn primary" id="wolfPlay">play</button>
672
+ </div>
673
+ <button type="button" class="btn primary dosagain hidden" id="wolfPlay2">play again</button>
674
+ </div>
675
+ </div>
676
+ <div class="card tethud">
677
+ <h3>Machine</h3>
678
+ <div class="tetstats" id="wolfStatus"></div>
679
+ <h3>Keys</h3>
680
+ <div class="tetkeys" id="wolfKeys"></div>
681
+ <div class="tetkeys">click the screen to capture the mouse (Esc gives it back): moving it turns, forward and back walk, left click fires</div>
682
+ <h3>Switches</h3>
683
+ <div class="tetswitches">
684
+ <button type="button" class="tetsw" id="wolfSound" aria-pressed="false" title="the Sound Blaster: AdLib music and digitised effects">♫ sound</button>
685
+ <button type="button" class="tetsw" id="wolfSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
686
+ <button type="button" class="tetsw" id="wolfFull" title="the screen alone, full screen">⛶ full screen</button>
687
+ </div>
688
+ <h3>About</h3>
689
+ <div class="tetkeys">Wolfenstein 3D is id Software's (1992, published by Apogee), from the shareware files in <code>games/wolf3d_dos/</code> on this server. The PC under it is BlockYard's own, here running in real mode: a 16-bit DOS program with a VGA and a Sound Blaster, written in JavaScript with no dependencies. Savegames and settings stay in this browser.</div>
690
+ </div>
691
+ </div>
692
+ </section>
693
+
694
+ <!-- DOOM (public/js/doom.js): the shareware DOOM.EXE v1.9 from games/doom_dos/, unmodified, on a PC this
695
+ repository emulates -- x86.js the CPU, dospc.js DOS/4GW and the hardware, soundcard.js a
696
+ Sound Blaster Pro 2 -- running in a worker (dosworker.js). The canvas is the monitor. -->
697
+ <section class="page" data-page="doom">
698
+ <div class="tetrust dosgame" id="doom">
699
+ <div class="card doscard">
700
+ <h3>DOOM <span class="sp"></span><span class="src">shareware v1.9 · Knee-Deep in the Dead · on an emulated 486</span></h3>
701
+ <div class="doswrap" id="doomWrap">
702
+ <canvas class="dosscreen" id="doomScreen" width="320" height="200"></canvas>
703
+ <div class="tetover" id="doomOver">
704
+ <div class="tetmsg" id="doomMsg">DOOM</div>
705
+ <div class="tetsub" id="doomSub"></div>
706
+ <button type="button" class="btn primary" id="doomPlay">play</button>
707
+ </div>
708
+ <button type="button" class="btn primary dosagain hidden" id="doomPlay2">play again</button>
709
+ </div>
710
+ </div>
711
+ <div class="card tethud">
712
+ <h3>Machine</h3>
713
+ <div class="tetstats" id="doomStatus"></div>
714
+ <h3>Keys</h3>
715
+ <div class="tetkeys" id="doomKeys"></div>
716
+ <div class="tetkeys">click the screen to capture the mouse (Esc gives it back) · left fire · right strafe · middle walk</div>
717
+ <h3>Switches</h3>
718
+ <div class="tetswitches">
719
+ <button type="button" class="tetsw" id="doomSound" aria-pressed="false" title="the Sound Blaster: effects and the OPL music">♫ sound</button>
720
+ <button type="button" class="tetsw" id="doomWasd" aria-pressed="false" title="W A S D to walk and strafe, E to open (on by default); off gives DOOM's own arrows, Space and Alt. Applies at once">⌨ WASD</button>
721
+ <button type="button" class="tetsw" id="doomSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
722
+ <button type="button" class="tetsw" id="doomFull" title="the screen alone, full screen">⛶ full screen</button>
723
+ </div>
724
+ <h3>About</h3>
725
+ <div class="tetkeys">DOOM is id Software's (1993), from the shareware files in <code>games/doom_dos/</code> on this server. The PC under it is BlockYard's own: an i386, DOS/4GW, a VGA and a Sound Blaster written in JavaScript, with no dependencies. Savegames and settings stay in this browser.</div>
726
+ </div>
727
+ </div>
728
+ </section>
729
+
730
+ <!-- QUAKE (public/js/quake.js): the shareware QUAKE.EXE v1.06 from games/quake_dos/, unmodified, on the
731
+ same emulated PC as DOOM -- this time playing the go32 stub and CWSDPMI for a DJGPP program. -->
732
+ <section class="page" data-page="quake">
733
+ <div class="tetrust dosgame" id="quake">
734
+ <div class="card doscard">
735
+ <h3>Quake <span class="sp"></span><span class="src">shareware v1.06 · Dimension of the Doomed · on an emulated PC</span></h3>
736
+ <div class="doswrap" id="quakeWrap">
737
+ <canvas class="dosscreen" id="quakeScreen" width="320" height="200"></canvas>
738
+ <div class="tetover" id="quakeOver">
739
+ <div class="tetmsg" id="quakeMsg">Quake</div>
740
+ <div class="tetsub" id="quakeSub"></div>
741
+ <button type="button" class="btn primary" id="quakePlay">play</button>
742
+ </div>
743
+ <button type="button" class="btn primary dosagain hidden" id="quakePlay2">play again</button>
744
+ </div>
745
+ </div>
746
+ <div class="card tethud">
747
+ <h3>Machine</h3>
748
+ <div class="tetstats" id="quakeStatus"></div>
749
+ <h3>Keys</h3>
750
+ <div class="tetkeys" id="quakeKeys"></div>
751
+ <div class="tetkeys">click the screen to capture the mouse (Esc gives it back) · W A S D and mouse look to start with; rebind anything in Quake's own options menu</div>
752
+ <h3>Switches</h3>
753
+ <div class="tetswitches">
754
+ <button type="button" class="tetsw" id="quakeSound" aria-pressed="false" title="the Sound Blaster: Quake's effects (its music was on the CD)">♫ sound</button>
755
+ <button type="button" class="tetsw" id="quakeSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
756
+ <button type="button" class="tetsw" id="quakeFull" title="the screen alone, full screen">⛶ full screen</button>
757
+ </div>
758
+ <h3>About</h3>
759
+ <div class="tetkeys">Quake is id Software's (1996), from the shareware files in <code>games/quake_dos/</code> on this server. The PC under it is BlockYard's own: an i386 with its FPU, a DPMI host, a VGA and a Sound Blaster written in JavaScript, with no dependencies. Savegames and settings stay in this browser.</div>
760
+ </div>
761
+ </div>
762
+ </section>
763
+
592
764
  <!-- ======================================================== MARKETS -->
593
765
  <!-- Exchange prices on the block-space board (public/js/markets.js, server/collect/markets.js). -->
594
766
  <section class="page" data-page="markets">
@@ -618,12 +790,22 @@
618
790
  <div class="treemapwrap mkboard"><canvas class="treemap" id="mkBoard"></canvas><div class="goggles-tip hidden"></div></div>
619
791
  </div>
620
792
  <canvas class="mkchart" id="mkChart"></canvas>
621
- <div id="mkTable"></div>
793
+ <!-- THE TABLE AND THE BOOK SIDE BY SIDE (2026-09-15, "same treatment on the markets tab"):
794
+ stacked, the depth chart began 1,000px down and was never on the first screen; the
795
+ board now takes what the window leaves after this row, and the row is two columns
796
+ from 900px up. -->
797
+ <div class="mkbottom">
798
+ <div class="mkbottom-l">
799
+ <div id="mkTable"></div>
800
+ <div class="note tiny faint" id="mkNote"></div>
801
+ </div>
802
+ <div class="mkbottom-r">
622
803
  <h3 class="mksub">Order book depth <span class="faint">cumulative bids and asks against price · each exchange's book, their total, and the total as it stood · change bars on the right, log scale</span></h3>
623
804
  <div class="mkbar" id="mkDepthBar"></div>
624
805
  <canvas class="mkchart mkdepth" id="mkDepth"></canvas>
625
806
  <div class="note tiny faint" id="mkDepthNote"></div>
626
- <div class="note tiny faint" id="mkNote"></div>
807
+ </div>
808
+ </div>
627
809
  </div>
628
810
  </div>
629
811
  </section>
@@ -673,20 +855,29 @@
673
855
  <!-- ======================================================== NETWORK -->
674
856
  <section class="page" data-page="network">
675
857
  <div class="grid">
676
- <div class="card w6">
677
- <h3>Throughput <span class="sp"></span><span class="src">getnettotals (RPC)</span></h3>
678
- <div class="metric"><div class="l">network in</div><div><span class="v" id="ovNetIn">–</span><span class="u" id="ovNetInU"></span></div>
679
- <div class="s" id="ovDisk">–</div></div>
680
- <canvas class="chart" id="ovNetChart"></canvas>
681
- </div>
682
- <div class="card w6">
683
- <h3>Upload <span class="sp"></span><span class="src">what the node reports</span></h3>
684
- <div class="note" id="ntUploadNote"></div>
685
- <dl class="kv" id="ntRpc"></dl>
686
- </div>
687
- <div class="card w6">
688
- <h3>Traffic accounting <span class="sp"></span><span class="src">cumulative</span></h3>
689
- <dl class="kv" id="ntAccounting"></dl>
858
+ <!-- TWO STACKS (2026-09-15, "same treatment on the network tab"): Throughput on the left,
859
+ Upload over Traffic accounting on the right, so the accounting card no longer sits
860
+ alone on a row beside half a screen of nothing; the sources table takes the rest. -->
861
+ <div class="ovcols ntcols">
862
+ <div class="ovcol">
863
+ <div class="card">
864
+ <h3>Throughput <span class="sp"></span><span class="src">getnettotals (RPC)</span></h3>
865
+ <div class="metric"><div class="l">network in</div><div><span class="v" id="ovNetIn">–</span><span class="u" id="ovNetInU"></span></div>
866
+ <div class="s" id="ovDisk">–</div></div>
867
+ <canvas class="chart tall" id="ovNetChart"></canvas>
868
+ </div>
869
+ </div>
870
+ <div class="ovcol">
871
+ <div class="card">
872
+ <h3>Upload <span class="sp"></span><span class="src">what the node reports</span></h3>
873
+ <div class="note" id="ntUploadNote"></div>
874
+ <dl class="kv" id="ntRpc"></dl>
875
+ </div>
876
+ <div class="card">
877
+ <h3>Traffic accounting <span class="sp"></span><span class="src">cumulative</span></h3>
878
+ <dl class="kv" id="ntAccounting"></dl>
879
+ </div>
880
+ </div>
690
881
  </div>
691
882
  <div class="card w12">
692
883
  <h3>Where each number comes from <span class="sp"></span><span class="src">explicit sources</span></h3>
@@ -698,47 +889,83 @@
698
889
  <!-- ========================================================= EVENTS -->
699
890
  <section class="page" data-page="mining">
700
891
  <div class="grid">
701
- <!-- TWO INDEPENDENT STACKS (operator, 2026-09-11: "too much wasted space in this
702
- display. Crunch down the panel sizes to fit more info"). One grid coupled the rows:
703
- the square Mempool space viewer set the first row, so Block flow sat in a 591 px
704
- card using ~273, and the landscape was stretched to the packages table. Stacks never
705
- stretch a card to its neighbour; the explanatory Detailed card folds away. -->
706
- <div class="mncols">
707
- <div class="ovcol">
708
- <div class="card">
709
- <h3>Block flow <span class="sp"></span><span class="src">being assembled <b>the current block</b> history receding</span></h3>
710
- <div id="mnFlow" class="flowwrap"></div>
711
- </div>
712
- <div class="mnpair">
713
- <div class="card">
714
- <h3>Packages in the block being built <span class="sp"></span><span class="src">ancestor graph from getrawmempool · depends</span></h3>
715
- <div id="mnPackages"></div>
716
- </div>
717
- <div class="card">
718
- <h3>Feerate landscape <span class="sp"></span><span class="src">the transactions selected for the next block</span></h3>
719
- <canvas class="chart" id="mnFeeLandscape"></canvas>
720
- <div class="note tiny faint">Buckets are sat/vB. Green means the bucket pays well above the block's own median, red below — the same shape a miner is looking at when it decides what still fits.</div>
721
- </div>
722
- </div>
723
- <div class="card">
724
- <h3>Pools in this window <span class="sp"></span><span class="src">coinbase tags, grouped by curated label</span></h3>
725
- <div id="mnPools"></div>
726
- </div>
892
+ <!-- THE NETWORK OVER A WEEK AND A YEAR (operator, 2026-09-15, with mempool.space's mining
893
+ dashboard: "Why don't we have this view in our mining tab?" -- "Do it"; then its
894
+ adjustments table: "add this too"). Every figure is from this node: getblockstats for
895
+ the reward window, block headers for the periods and the daily hashrate samples, the
896
+ coinbases of the last week's blocks for the pools (collect/network.js). -->
897
+ <!-- ONE SCREEN (operator, 2026-09-15: "remove the mempool space viewer from the mining tab.
898
+ Move Block flow up as the first thing along the top. Shuffle around and try to fit as
899
+ much as possible on one screen"): the flow across the top, then three stacks -- the
900
+ figures and the packages; the pool pie and the landscape; the year chart, the
901
+ adjustments, the window's pools and the attribution. The Mempool space viewer is on
902
+ Overview, Block space and Mempool already. -->
903
+ <!-- THE DEFAULT LAYOUT OF mempool.space's MINING DASHBOARD (operator, 2026-09-15: "look at the
904
+ source code for mempool space app, and try to implement this default layout at the very
905
+ least. Notice the 'View more >>' that open up a panel to see a full screen view of just
906
+ that panel"). Its mining-dashboard.component.html is two columns: reward stats |
907
+ difficulty adjustment; pool ranking | hashrate chart, each with "View more »"; recent
908
+ blocks | adjustments. The same six cards here in the same order, the block flow across
909
+ the top as before, and "View more »" opens the card full-screen (#expandWrap). Every
910
+ figure is from this node (collect/network.js); our own panels follow beneath. -->
911
+ <div class="card mnflow flowtight">
912
+ <h3>Block flow <span class="sp"></span><span class="src">being assembled → <b>the current block</b> → history receding</span></h3>
913
+ <div id="mnFlow" class="flowwrap"></div>
914
+ </div>
915
+ <div class="mn2">
916
+ <div class="card">
917
+ <h3>Reward stats <span class="sp"></span><span class="src" id="mnRewardsSrc">last 144 blocks · getblockstats</span></h3>
918
+ <div class="netstats" id="mnRewards"></div>
727
919
  </div>
728
- <div class="ovcol">
729
- <div class="card">
730
- <h3>Mempool space <span class="sp"></span><span class="src">every rectangle is a waiting transaction: area = vbytes, colour = its feerate, richest first</span></h3>
731
- <div class="treemapwrap sq">
732
- <canvas class="treemap" id="gnMempoolTreemap"></canvas><div class="viewer-ctl"><div class="refresh-in" data-refresh></div><button type="button" class="refresh-now" data-refresh-now disabled>refresh now</button></div>
733
- <div class="goggles-tip hidden"></div>
734
- </div>
735
- <div class="note tiny faint" id="gnMempoolNote"></div>
736
- </div>
737
- <div class="card">
738
- <h3>Attribution <span class="sp"></span><span class="src">what we read and from where</span></h3>
739
- <div id="mnCoverage"></div>
740
- </div>
741
- <details class="card mndetailed">
920
+ <div class="card">
921
+ <h3>Difficulty adjustment <span class="sp"></span><span class="src">this period, from its first block's time</span></h3>
922
+ <div class="netstats" id="mnAdjust"></div>
923
+ </div>
924
+ <div class="card">
925
+ <h3>Pools <span class="sp"></span><span class="src" id="mnPoolsWeekSrc">the last week's blocks, by coinbase</span></h3>
926
+ <div class="netstats" id="mnPoolsWeek"></div>
927
+ <canvas class="chart donut" id="mnPoolDonut"></canvas>
928
+ <div class="note tiny faint" id="mnPoolsWeekNote"></div>
929
+ <a class="viewmore" href="#mining" data-expand="pools">View more »</a>
930
+ </div>
931
+ <div class="card">
932
+ <h3>Hashrate &amp; difficulty <span class="sp"></span><span class="src">a year, one sample a day · getnetworkhashps over the week</span></h3>
933
+ <div class="netstats" id="mnHashrate"></div>
934
+ <canvas class="chart tall" id="mnHashChart"></canvas>
935
+ <div class="note tiny faint">Difficulty × 2³² over each day's mean block interval, from the headers — an estimate, as every hashrate figure is; the steps are the difficulty.</div>
936
+ <a class="viewmore" href="#mining" data-expand="hashrate">View more »</a>
937
+ </div>
938
+ <div class="card">
939
+ <h3>Recent blocks <span class="sp"></span><span class="src">height, pool, reward, fees · the attributed window</span></h3>
940
+ <div id="mnRecent"></div>
941
+ <a class="viewmore" href="#mining" data-expand="blocks">View more »</a>
942
+ </div>
943
+ <div class="card">
944
+ <h3>Adjustments <span class="sp"></span><span class="src">each period's first block against the one before</span></h3>
945
+ <div id="mnAdjustments"></div>
946
+ <a class="viewmore" href="#mining" data-expand="adjustments">View more »</a>
947
+ </div>
948
+ </div>
949
+ <!-- our own: the block being built and the attribution behind the pools -->
950
+ <div class="mnextra">
951
+ <div class="card">
952
+ <h3>Packages in the block being built <span class="sp"></span><span class="src">ancestor graph from getrawmempool · depends</span></h3>
953
+ <div id="mnPackages"></div>
954
+ </div>
955
+ <div class="card">
956
+ <h3>Feerate landscape <span class="sp"></span><span class="src">the transactions selected for the next block</span></h3>
957
+ <canvas class="chart" id="mnFeeLandscape"></canvas>
958
+ <div class="note tiny faint">Buckets are sat/vB. Green means the bucket pays well above the block's own median, red below — the same shape a miner is looking at when it decides what to include.</div>
959
+ </div>
960
+ <div class="card">
961
+ <h3>Pools in this window <span class="sp"></span><span class="src">coinbase tags, grouped by curated label</span></h3>
962
+ <div id="mnPools"></div>
963
+ </div>
964
+ <div class="card">
965
+ <h3>Attribution <span class="sp"></span><span class="src">what we read and from where</span></h3>
966
+ <div id="mnCoverage"></div>
967
+ </div>
968
+ <details class="card mndetailed">
742
969
  <summary><h3>Detailed <span class="sp"></span><span class="src">what this node can and cannot answer</span></h3></summary>
743
970
  <div class="kv">
744
971
  <dt>fee deployment by miner</dt><dd>above, per mined block</dd>
@@ -756,7 +983,6 @@
756
983
  view above is built from <span class="mono">getrawmempool … depends</span>, which is RPC.</dd>
757
984
  </div>
758
985
  </details>
759
- </div>
760
986
  </div>
761
987
  </div>
762
988
  </section>
@@ -780,36 +1006,22 @@
780
1006
  <!-- ==================================================== NODE & RPC -->
781
1007
  <section class="page" data-page="node">
782
1008
  <div class="grid">
783
- <div class="card w6">
1009
+ <!-- TWO STACKS (2026-09-15, "same treatment on the node & rpc tab"): the cards are of very
1010
+ different heights -- RPC etiquette is fourteen figures and a chart, Poll cadence five
1011
+ rows -- and as grid rows they left holes beside the short ones. Each stack packs its
1012
+ own cards; the etiquette figures run in two columns. -->
1013
+ <div class="ovcols ndcols">
1014
+ <div class="ovcol">
1015
+ <div class="card">
784
1016
  <h3>RPC etiquette <span class="sp"></span><span class="src">what this app asks of the node</span></h3>
785
- <dl class="kv" id="ndRpc"></dl>
1017
+ <dl class="kv two" id="ndRpc"></dl>
786
1018
  <canvas class="chart short" id="ndLatChart"></canvas>
787
1019
  <div class="note tiny faint">The node's RPC server services one connection at a time on a single thread. This app keeps one request in flight, spaces them, batches multiple methods per connection, and drops a poll that could not answer in time to be true.</div>
788
1020
  </div>
789
- <div class="card w6">
790
- <h3>Poll cadence <span class="sp"></span><span class="src">configured vs actual</span></h3>
791
- <table class="t" id="ndCadence"><thead><tr><th>tier</th><th class="r">configured</th><th class="r">actual</th><th class="r">last run</th></tr></thead><tbody></tbody></table>
792
- <div class="note tiny faint" id="ndCadenceNote"></div>
793
- </div>
794
-
795
- <div class="card w6">
1021
+ <div class="card">
796
1022
  <h3>Data quality <span class="sp"></span><span class="src">known gaps, stated</span></h3>
797
1023
  <div id="ndQuality"></div>
798
1024
  </div>
799
- <div class="card w3">
800
- <h3>Monitor self-telemetry <span class="sp"></span><span class="src">this process</span></h3>
801
- <dl class="kv" id="ndSelf"></dl>
802
- <canvas class="chart short" id="ndSelfChart"></canvas>
803
- </div>
804
- <div class="card w3">
805
- <h3>Log tail <span class="sp"></span><span class="src">follow state</span></h3>
806
- <dl class="kv" id="ndTail"></dl>
807
- </div>
808
-
809
- <div class="card w6">
810
- <h3>Panel sources <span class="sp"></span><span class="src">provenance</span></h3>
811
- <div id="ndSources"></div>
812
- </div>
813
1025
  <!-- THE NODE CONNECTION (operator, 2026-09-12: "Still left to do is a config connection in
814
1026
  the web settings. We have no way for users to configure a connection to their rpc
815
1027
  backend"). Test first, save second: the save button is disabled until a test has
@@ -818,7 +1030,7 @@
818
1030
  No credential fields, deliberately: authentication is the node's own .cookie, found
819
1031
  from the data directory (server/config.js resolveCookie). Asking for an RPC password on
820
1032
  a page that is open access by default is not something to add quietly. -->
821
- <div class="card w6">
1033
+ <div class="card">
822
1034
  <h3>Node connection <span class="sp"></span><span class="src">where this monitor looks</span></h3>
823
1035
  <!-- the placeholder deliberately is NOT a URL: web-contract.test.js refuses an http(s)
824
1036
  literal outside an <a href>, because in shipped markup that reads as a resource load
@@ -836,7 +1048,29 @@
836
1048
  <div class="note tiny" id="cnOut"></div>
837
1049
  <div class="note tiny faint">Authentication uses the node's <span class="mono">.cookie</span> from the data directory; no password is asked for or kept here. Saving writes this monitor's own configuration file and takes effect on restart.</div>
838
1050
  </div>
839
- <div class="card w6">
1051
+ </div>
1052
+ <div class="ovcol">
1053
+ <div class="card">
1054
+ <h3>Poll cadence <span class="sp"></span><span class="src">configured vs actual</span></h3>
1055
+ <table class="t" id="ndCadence"><thead><tr><th>tier</th><th class="r">configured</th><th class="r">actual</th><th class="r">last run</th></tr></thead><tbody></tbody></table>
1056
+ <div class="note tiny faint" id="ndCadenceNote"></div>
1057
+ </div>
1058
+ <div class="ovpair">
1059
+ <div class="card">
1060
+ <h3>Monitor self-telemetry <span class="sp"></span><span class="src">this process</span></h3>
1061
+ <dl class="kv" id="ndSelf"></dl>
1062
+ <canvas class="chart short" id="ndSelfChart"></canvas>
1063
+ </div>
1064
+ <div class="card">
1065
+ <h3>Log tail <span class="sp"></span><span class="src">follow state</span></h3>
1066
+ <dl class="kv" id="ndTail"></dl>
1067
+ </div>
1068
+ </div>
1069
+ <div class="card">
1070
+ <h3>Panel sources <span class="sp"></span><span class="src">provenance</span></h3>
1071
+ <div id="ndSources"></div>
1072
+ </div>
1073
+ <div class="card">
840
1074
  <h3>RPC console <span class="sp"></span><span class="src">read-only allowlist</span></h3>
841
1075
  <div class="rowform">
842
1076
  <input class="f grow minw-180" id="rpcMethod" placeholder="method, e.g. getblockchaininfo">
@@ -846,13 +1080,20 @@
846
1080
  <div class="note tiny faint" id="rpcNote">Mutating, wallet- and peer-control methods are refused by this app regardless of what the node would do.</div>
847
1081
  <pre class="mono tiny output-box" id="rpcOut"></pre>
848
1082
  </div>
1083
+ </div>
1084
+ </div>
849
1085
  </div>
850
1086
  </section>
851
1087
 
852
1088
  <!-- ========================================================== ADMIN -->
853
1089
  <section class="page" data-page="admin">
854
1090
  <div class="grid">
855
- <div class="card w6">
1091
+ <!-- TWO STACKS (2026-09-15, "same treatment on the admin page"): Users beside Node actions left
1092
+ a hole under the short Users card; each stack packs its own. The two password fields
1093
+ share a row. -->
1094
+ <div class="ovcols adcols">
1095
+ <div class="ovcol">
1096
+ <div class="card">
856
1097
  <h3>Users <span class="sp"></span><span class="src">scrypt-hashed, never shown</span></h3>
857
1098
  <div class="scroll"><table class="t" id="adUsers"><thead><tr><th>user</th><th>role</th><th>created</th><th>last login</th><th></th></tr></thead><tbody></tbody></table></div>
858
1099
  <div class="hr"></div>
@@ -863,25 +1104,28 @@
863
1104
  </div>
864
1105
  <div class="note tiny" id="adGen"></div>
865
1106
  </div>
866
-
867
- <div class="card w6">
868
- <h3>Node actions <span class="sp"></span><span class="src">off unless enabled</span></h3>
869
- <div class="note tiny" id="adActions"></div>
870
- </div>
871
-
872
- <div class="card w6">
1107
+ <div class="card">
873
1108
  <h3>Audit log <span class="sp"></span><span class="src">credentials use, without credentials</span></h3>
874
1109
  <div class="scroll lg"><table class="t" id="adAudit"><thead><tr><th>when</th><th>type</th><th>who</th><th>detail</th></tr></thead><tbody></tbody></table></div>
875
1110
  <div class="note tiny faint mt-6" id="adAuditSize"></div>
876
1111
  </div>
877
-
878
- <div class="card w6">
1112
+ </div>
1113
+ <div class="ovcol">
1114
+ <div class="card">
1115
+ <h3>Node actions <span class="sp"></span><span class="src">off unless enabled</span></h3>
1116
+ <div class="note tiny" id="adActions"></div>
1117
+ </div>
1118
+ <div class="card">
879
1119
  <h3>Change own password <span class="sp"></span></h3>
880
- <div class="field"><label>current password</label><input class="f w-full" type="password" id="pwCurrent" autocomplete="current-password"></div>
881
- <div class="field"><label>new password</label><input class="f w-full" type="password" id="pwNew" autocomplete="new-password"></div>
1120
+ <div class="ovpair">
1121
+ <div class="field"><label>current password</label><input class="f w-full" type="password" id="pwCurrent" autocomplete="current-password"></div>
1122
+ <div class="field"><label>new password</label><input class="f w-full" type="password" id="pwNew" autocomplete="new-password"></div>
1123
+ </div>
882
1124
  <button class="btn primary" id="btnPw">change password</button>
883
1125
  <div class="note tiny faint mt-6">Changing it revokes every session, including this one.</div>
884
1126
  </div>
1127
+ </div>
1128
+ </div>
885
1129
  </div>
886
1130
  </section>
887
1131
  </main>