灵符lingfu

LingSys

↓ 0 downloads

Your machine's memory, GPU, motherboard and network, drawn and played in the Ling design system: LingSys, a small build for USB liquid-cooler displays, LingNet for IPv4/IPv6, DNS and packets, and a LingOS edition. 64-bit and 32-bit Windows builds included.

Keywords system-monitor, visualization, network, ipv6, gpu, lcd, lingos, ling
lingfu install LingSys
LicenseMIT
AuthorsLing Lin <taellinglin@gmail.com>
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (48)

hatchsys.ling
# ══════════════════════════════════════════════════════════════════════════════
# HatchSYS: the machine's memory, GPU, motherboard and network, drawn and played
# in the Ling design system.
#
# GENERATED FILE: edit the parts in src/ and run `python tools/build.py`.
# (One file on purpose: the Ling JIT does not see top-level bindings of `use`d modules.)
#
# Line work only: circles, squares, Daemon tiles, Chesilin rings, crosshatch.
#   circle  = something comes in (a page read, a packet received, a frame drawn)
#   square  = something goes out (a page written, a packet sent, a copy)
# The same numbers drive an additive synth: seven channels x four partials plus four
# shimmer partials, 32 audio_tone slots in all, and a short chime for every pulse.
#
# Sections: MAIN (system overview) | MEMORY | GPU | BOARD | NETWORK
# Keys: 0 main, 1-4 section, LEFT/RIGHT section, TAB or SPACE next view, UP/DOWN pick GPU,
#       A auto-cycle, H hud, M mute, ESC back (quit on MAIN), Q quit. The bottom menu is clickable.
# Run:  ling run hatchsys.ling  (run.ps1 starts the samplers for live data first)
# Args: full (borderless fullscreen) | sim (ignore the .snap files) | shot (save a screenshot per view, quit)
# ══════════════════════════════════════════════════════════════════════════════

bind TAU = 6.283185307179586
bind HUE = [357.0, 28.0, 46.0, 140.0, 215.0, 247.0, 285.0]
bind CAT_NAME = ["KERNEL", "STANDBY", "DATA", "MODIFIED", "MAPPED", "COMPRESSED", "FREE"]
bind CAT_LET = "ALBCKFM"
bind ANG1 = [0.785, 0.52, 1.05, 0.0, 2.09, 1.31, 0.26]
bind ORDER = [0, 2, 4, 5, 3, 1, 6]
bind RATIO = [1.0, 1.5, 2.0, 2.25, 3.0, 3.75, 4.5]
bind READ_W = [0.15, 0.9, 0.5, 0.0, 0.8, 0.2, 0.0]
bind WRITE_W = [0.1, 0.0, 0.7, 1.0, 0.2, 0.5, 0.0]

# sections and their views
bind SEC_NAME = ["MAIN", "MEMORY", "GPU", "BOARD", "NETWORK"]
bind SEC_HUE = [2, 4, 3, 1, 6]
bind SEC_LET = "HMGBN"
bind SUB_NAME = [["OVERVIEW"], ["RINGS", "TILES", "ORBITS", "WEAVE"], ["ENGINES", "VRAM", "DIALS", "FLEET"], ["LAYOUT", "CORES", "SIGNALS"], ["FLOW", "LINKS", "TRACES"]]

# the seven channels of every section: ring names, lane names, pulse categories (same order as the Ling hues)
bind CH_GPU = ["3D", "COMPUTE", "COPY", "DECODE", "ENCODE", "VRAM", "POWER"]
bind CH_BRD = ["CORES", "MEMBUS", "PCIE", "CHIPSET", "STORAGE", "NETWORK", "THERMAL"]
bind CH_NET = ["RX", "TX", "PKT IN", "PKT OUT", "TCP", "UDP", "RETRANS"]
bind CH_MAIN = ["MEMORY", "GPU", "CPU", "NETWORK", "VRAM", "DISK", "HEAT"]
bind CH_ALL = [CAT_NAME, CH_MAIN, CH_GPU, CH_BRD, CH_NET]
bind IDM = [0, 1, 2, 3, 4, 5, 6]
bind CH_LET_GPU = "3CPDEVW"
bind CH_LET_BRD = "CMPSDNT"
bind CH_LET_NET = "RTIOUDX"
bind CH_LET_MAIN = "MGCNVDH"

# state list Z, built once per frame and handed to the views and the synth
bind Z_T = 0
bind Z_ROT = 1
bind Z_HS = 2
bind Z_SAT = 3
bind Z_W = 4
bind Z_H = 5
bind Z_CATS = 6
bind Z_TOTAL = 7
bind Z_ACT = 8
bind Z_RD = 9
bind Z_WR = 10
bind Z_FLT = 11
bind Z_EV = 12
bind Z_NAME = 13
bind Z_WS = 14
bind Z_PRIV = 15
bind Z_IOR = 16
bind Z_IOW = 17
bind Z_HIST = 18
bind Z_USED = 19
bind Z_ENERGY = 20
bind Z_HIL = 21
bind Z_LIVE = 22
bind Z_FH = 23
bind Z_D = 24
bind Z_STAT = 25
bind Z_MX = 26
bind Z_MY = 27
bind Z_SEC = 28
bind Z_VIEW = 29
bind Z_FRAC = 30
bind Z_LAB = 31
bind Z_ALERT = 32

# section data pack D: the smoothed readings of all four sections plus the history strips
bind D_GPU = 0
bind D_BRD = 1
bind D_NET = 2
bind D_HS = 3
bind D_MEM = 4
bind D_LIVE = 5

# GPU pack: [n, names, drivers, vecs, engs, procs, sel]; one vec per GPU
bind GV_TEMP = 0
bind GV_FAN = 1
bind GV_POWER = 2
bind GV_PLIM = 3
bind GV_CLK = 4
bind GV_CLKMAX = 5
bind GV_MCLK = 6
bind GV_MCLKMAX = 7
bind GV_UTIL = 8
bind GV_MUTIL = 9
bind GV_VUSED = 10
bind GV_VTOTAL = 11
bind GV_PGEN = 12
bind GV_PW = 13
bind GV_THP = 14
bind GV_THT = 15
bind GV_LEN = 16

# board pack: [info, bv, cutil, cfreq, dimms, disks, dio, sens]
bind BV_UTIL = 0
bind BV_MHZ = 1
bind BV_PMAX = 2
bind BV_TEMP = 3
bind BV_INTR = 4
bind BV_DPC = 5
bind BV_CTX = 6
bind BV_SYSC = 7
bind BV_PROCS = 8
bind BV_THR = 9
bind BV_UP = 10
bind BV_DRD = 11
bind BV_DWR = 12
bind BV_DBUSY = 13
bind BV_DQ = 14
bind BV_LEN = 15

# net pack: [adapters, nv, nodes, procs]
bind NV_RX = 0
bind NV_TX = 1
bind NV_PRX = 2
bind NV_PTX = 3
bind NV_BW = 4
bind NV_EST = 5
bind NV_RETX = 6
bind NV_UDP = 7
bind NV_RTT = 8
bind NV_LISTEN = 9
bind NV_TW = 10
bind NV_CW = 11
bind NV_SYN = 12
bind NV_LOOP = 13
bind NV_FAIL = 14
bind NV_LEN = 15

# history strips: 120 samples, half a second apart, each already scaled to 0..1
bind HS_N = 120
bind HS_GPU = 0
bind HS_BRD = 32
bind HS_NET = 40
bind HS_MEM = 48
bind HS_COUNT = 50

# ---- BEGIN GLYPHS ----
# generated by tools/gen_glyphs.py from the Ling design system bundle; do not edit by hand
bind DAEMON_CH = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
bind DAEMON = [
[212,699,[[0,611,6,577,26,548,54,529,88,523,122,529,150,548,170,577,176,611,170,645,150,674,122,693,88,699,54,693,26,674,6,645],[212,0,133,368,65,368,88,0]]],
[729,751,[[393,1,294,2,235,205,293,1],[172,420,206,304,173,420],[76,751,144,518,77,751],[634,0,535,1,500,120,534,0],[317,750,385,518,318,750],[487,508,387,508,410,430,510,430],[247,508,147,508,169,430,269,430],[147,508,0,508,0,430,169,430],[387,508,247,508,269,430,410,430],[672,430,672,508,487,508,510,430],[550,293,450,293,472,215,572,215],[309,293,209,293,232,215,332,215],[209,293,48,293,48,215,232,215],[450,293,309,293,332,215,472,215],[720,215,720,293,550,293,572,215],[535,1,634,1,575,205,729,205,729,303,547,303,513,420,681,420,681,518,485,518,418,750,318,750,387,508,487,508,417,750,318,750,418,750,485,518,681,518,681,420,513,420,547,303,729,303,729,205,575,205,634,1,572,215,472,215,500,120],[447,304,450,293,550,293,510,430,410,430,413,420],[294,2,393,2,332,215,232,215,234,205],[172,420,209,293,309,293,269,430,169,430],[77,751,147,508,247,508,176,751]]],
[500,749,[[400,283,394,192,370,136,351,119,292,103,210,103,151,119,131,136,116,160,101,248,102,333,116,401,151,437,177,446,292,450,351,437,384,401,398,333],[0,286,7,188,16,149,28,115,63,62,85,42,140,14,250,0,356,14,411,40,433,59,470,113,482,146,495,208,500,282,493,380,484,417,473,449,438,498,416,516,361,540,251,551,140,540,110,530,62,498,43,476,16,417,7,380,0,287],[175,279,183,340,190,351,214,363,272,365,301,358,318,340,324,305,318,217,311,206,288,193,230,191,200,198,183,217,176,253],[125,283,128,235,139,198,156,172,180,154,250,140,320,154,344,172,361,198,372,235,375,283,368,348,358,371,326,402,279,414,220,414,174,402,142,371,127,319],[175,285,183,345,190,355,214,364,301,361,318,345,324,311,318,222,311,210,288,195,230,192,200,201,183,222,176,259],[125,283,139,199,156,172,181,155,212,144,251,141,289,144,321,155,344,172,361,199,372,236,375,283,368,348,358,371,327,402,280,414,221,414,174,402,143,371,127,319],[1,30,79,30,79,749,1,749]]],
[569,751,[[50,139,57,77,74,58,88,54,125,51,176,58,193,77,199,113,193,201,186,212,162,224,104,225,74,219,57,201,51,165],[0,144,3,96,14,59,31,32,55,15,87,4,125,1,163,4,195,15,219,32,236,59,247,96,250,144,248,180,233,232,201,263,154,275,95,275,49,263,17,232,2,180],[50,146,57,205,64,215,88,224,176,221,193,205,199,172,193,82,176,61,162,55,104,52,74,61,57,82,51,120],[0,144,3,96,14,59,31,32,55,15,87,4,125,1,163,4,195,15,219,32,236,59,247,96,250,144,248,180,233,232,201,263,154,275,95,275,49,263,17,232,2,180],[369,614,376,552,406,529,465,527,494,534,512,552,518,588,516,660,512,676,494,694,444,701,394,694,376,676,370,640],[319,619,322,571,333,534,350,508,374,490,444,476,482,479,514,490,538,508,555,534,566,571,569,619,562,684,552,707,520,738,473,750,414,750,368,738,336,707,321,655],[369,621,376,680,406,700,494,696,512,680,518,647,512,557,504,544,482,530,423,527,394,536,376,557,370,595],[319,619,322,571,333,534,350,508,374,490,444,476,482,479,514,490,538,508,555,534,566,571,569,619,562,684,552,707,520,738,473,750,414,750,368,738,336,707,321,655],[343,0,443,0,226,750,126,750]]],
[428,583,[[341,582,259,583,18,0,94,0,341,583],[302,283,78,283,80,458,428,280,428,370,0,583,0,208,221,205,220,82,80,88,40,0,303,0]]],
[208,164,[[208,0,193,43,149,107,98,149,68,164,0,164,41,113,70,63,84,0]]],
[445,806,[[405,0,445,2,445,89,387,89,301,104,233,134,194,164,157,200,120,259,93,349,88,403,94,464,112,527,126,558,165,615,218,663,285,698,380,718,445,719,445,806,355,805,284,790,202,761,134,718,73,652,46,605,20,537,0,437,0,368,20,266,38,212,86,134,143,82,209,42,275,18,374,1]]],
[445,806,[[71,1,170,18,236,42,302,82,359,134,394,186,425,266,445,368,445,437,425,537,399,605,372,652,311,718,243,761,162,790,90,805,0,806,0,719,66,718,160,698,228,663,280,615,319,558,333,527,356,433,357,379,337,290,304,220,252,164,212,134,144,104,58,89,0,89,0,2,40,0]]],
[334,334,[[334,138,334,196,0,196,0,138],[138,0,196,0,196,334,138,334],[264,28,306,70,70,306,28,264],[28,70,70,28,306,264,264,306]]],
[445,445,[[445,183,445,261,0,261,0,183],[184,0,262,0,262,445,184,445]]],
[176,266,[[88,0,123,6,151,26,170,54,176,103,165,154,145,190,68,266,0,266,38,220,66,174,37,161,16,140,0,88,6,54,26,26,54,6]]],
[445,78,[[445,0,445,78,0,78,0,0]]],
[176,176,[[0,88,6,54,26,26,54,6,88,0,122,6,150,26,170,54,176,88,170,122,150,150,122,170,88,176,54,170,26,150,6,122]]],
[317,750,[[0,750,217,0,317,0,100,750]]],
[500,745,[[400,370,394,265,370,180,351,147,325,122,292,106,251,101,210,106,177,122,151,147,116,219,101,333,107,476,130,564,150,597,176,623,209,640,250,645,291,640,324,623,350,597,370,564,387,508,399,407],[0,373,4,293,16,222,28,180,63,106,85,75,140,28,173,12,250,0,289,2,353,20,405,52,446,96,475,162,484,201,492,242,500,369,496,450,484,521,457,603,438,638,416,669,360,716,290,742,250,745,209,742,139,716,84,669,62,638,43,603,16,521,4,450,0,374]]],
[456,749,[[23,381,16,585,0,749,68,749,147,381],[356,169,355,112,345,81,281,100,230,106,202,105,113,81,104,96,100,136,102,224,104,238,113,248,179,216,230,209,281,216,345,248,353,238,356,201],[0,172,1,120,12,54,29,24,52,8,83,1,229,5,344,0,390,6,410,15,445,52,454,73,456,106,451,256,435,302,406,320,230,309,72,322,35,314,12,283,1,220,0,173]]],
[456,749,[[23,381,16,585,0,749,68,749,147,381],[356,169,355,112,345,81,281,100,230,106,202,105,113,81,104,96,100,136,102,224,104,238,113,248,179,216,230,209,281,216,345,248,353,238,356,201],[0,172,1,120,12,54,29,24,52,8,83,1,229,5,344,0,390,6,410,15,445,52,454,73,456,106,451,256,435,302,406,320,230,309,72,322,35,314,12,283,1,220,0,173],[177,381,170,585,154,749,222,749,301,381]]],
[456,749,[[23,381,16,585,0,749,68,749,147,381],[331,381,324,585,308,749,376,749,455,381],[356,169,355,112,345,81,281,100,230,106,202,105,113,81,104,96,100,136,102,224,104,238,113,248,179,216,230,209,281,216,345,248,353,238,356,201],[0,172,1,120,12,54,29,24,52,8,83,1,229,5,344,0,390,6,410,15,445,52,454,73,456,106,451,256,435,302,406,320,230,309,72,322,35,314,12,283,1,220,0,173],[177,381,170,585,154,749,222,749,301,381]]],
[455,742,[[23,374,16,578,0,742,68,742,147,374],[0,192,455,192,455,292,0,292],[230,0,286,10,331,38,348,58,368,108,369,191,267,193,266,127,246,105,229,102,212,105,193,128,191,194,90,191,91,117,110,67,147,23,213,1]]],
[455,742,[[23,374,16,578,0,742,68,742,147,374],[0,192,455,192,455,292,0,292],[230,0,286,10,331,38,348,58,368,108,369,191,267,193,266,127,246,105,229,102,212,105,193,128,191,194,90,191,91,117,110,67,147,23,213,1],[177,374,170,578,154,742,222,742,301,374]]],
[455,742,[[23,374,16,578,0,742,68,742,147,374],[0,192,455,192,455,292,0,292],[230,0,286,10,331,38,348,58,368,108,369,191,267,193,266,127,246,105,229,102,212,105,193,128,191,194,90,191,91,117,110,67,147,23,213,1],[331,374,324,578,308,742,376,742,455,374],[177,374,170,578,154,742,222,742,301,374]]],
[455,750,[[23,382,16,586,0,750,68,750,147,382],[0,200,455,200,455,300,0,300],[0,0,455,0,455,100,0,100]]],
[455,750,[[23,382,16,586,0,750,68,750,147,382],[0,200,455,200,455,300,0,300],[0,0,455,0,455,100,0,100],[177,382,170,586,154,750,222,750,301,382]]],
[455,751,[[23,383,16,587,0,751,68,751,147,383],[0,201,455,201,455,301,0,301],[0,1,455,1,455,101,0,101,0,0],[331,383,324,587,308,751,376,751,455,383],[177,383,170,587,154,751,222,751,301,383]]],
[176,399,[[0,311,6,277,26,248,54,229,88,223,122,229,150,248,170,277,176,311,170,345,150,374,122,393,88,399,54,393,26,374,6,345],[0,88,6,54,26,26,54,6,88,0,122,6,150,26,170,54,176,88,170,122,150,150,122,170,88,176,54,170,26,150,6,122]]],
[176,491,[[0,88,6,54,26,26,54,6,88,0,122,6,150,26,170,54,176,88,170,122,150,150,122,170,88,176,54,170,26,150,6,122],[88,225,123,232,151,251,170,280,176,328,165,378,145,415,68,491,0,491,38,445,66,399,37,386,16,364,0,313,6,279,26,250,54,231]]],
[448,804,[[361,2,448,2,448,82,424,191,410,232,364,311,309,364,246,403,327,457,394,542,436,660,447,727,447,804,360,804,356,715,329,617,299,569,234,504,188,478,140,460,88,450,0,448,0,359,82,358,164,338,226,309,296,242,328,188,350,132,358,90,361,0]]],
[455,301,[[0,201,455,201,455,301,0,301],[0,1,455,1,455,101,0,101,0,0]]],
[448,801,[[87,49,98,130,119,186,151,239,195,286,221,306,283,335,365,355,448,356,448,445,359,447,307,458,237,486,191,521,149,567,119,615,92,713,88,801,1,801,1,725,8,672,47,553,79,501,120,455,202,401,158,376,100,328,51,256,23,189,0,80,0,0,87,0]]],
[226,701,[[0,613,6,579,26,551,54,532,88,525,122,532,150,551,170,579,176,613,170,647,150,676,122,695,88,701,54,695,26,676,6,647],[0,132,0,1,49,0,125,9,166,25,196,47,215,74,226,140,213,210,182,286,133,370,65,370,113,245,120,191,118,171,105,144,80,128,43,126]]],
[500,551,[[102,327,112,391,130,423,150,438,194,449,297,450,351,438,386,429,494,380,481,430,463,469,430,507,364,539,293,551,202,551,143,541,107,529,72,507,41,476,17,425,1,327],[178,327,192,354,222,366,297,363,310,354,323,327,466,327,432,357,378,390,351,403,295,416,221,416,184,409,156,389,137,360,127,327],[1,327,7,192,28,116,45,86,83,43,112,25,222,0,292,1,350,12,400,32,438,64,467,105,485,147,500,231,500,283,466,327,323,327,325,257,308,206,270,192,222,194,196,206,178,238,178,327,127,327,127,252,139,201,168,164,217,144,284,145,336,166,361,196,368,217,375,305,398,279,391,183,377,150,355,123,285,103,219,103,152,120,124,147,111,182,101,241,102,327]]],
[500,750,[[166,750,0,0,100,0,250,650,401,0,500,0,333,750]]],
[500,750,[[100,407,400,407,400,97,0,97,0,0,500,0,500,503,200,503,200,750,100,750]]],
[500,750,[[200,653,200,0,300,0,300,653,500,653,500,749,0,750,0,654]]],
[500,750,[[100,318,100,0,200,0,200,221,500,221,500,750,0,750,0,653,400,653,400,318]]],
[500,745,[[0,447,0,303,500,0,500,134,100,377,498,613,498,745]]],
[500,750,[[0,0,500,0,500,100,100,100,100,650,500,650,500,750,0,750,0,726]]],
[500,750,[[0,0,100,0,100,648,400,648,400,0,500,0,500,750,0,750]]],
[500,750,[[0,0,500,0,500,750,0,750,0,650,400,650,400,100,0,100]]],
[500,750,[[166,0,333,0,500,750,400,750,250,100,100,750,0,750]]],
[500,750,[[0,0,500,0,500,100,100,99,100,750,0,750]]],
[500,750,[[100,100,99,750,0,750,0,0,500,0,500,750,400,750,400,100]]],
[500,750,[[400,650,100,650,100,750,0,750,0,0,500,0,500,750,400,750],[400,550,400,100,101,100,100,550]]],
[500,751,[[368,750,0,750,0,650,365,650,400,441,400,425,0,425,0,325,400,325,400,100,0,100,0,0,500,0,500,441,450,751]]],
[500,750,[[350,750,394,425,0,425,0,325,400,325,400,100,0,100,0,0,500,0,500,441,450,750]]],
[500,750,[[500,425,454,421,416,429,385,447,340,503,324,540,306,618,300,750,200,750,189,583,168,520,152,493,107,452,42,429,0,425,0,325,77,307,133,277,161,252,197,194,212,130,210,62,200,0,300,0,314,125,303,203,276,271,230,326,200,348,125,375,166,391,205,418,235,457,250,505,284,406,310,372,340,346,374,330,412,322,500,325,500,375]]],
[500,749,[[415,350,100,350,100,650,500,650,500,749,1,749,0,250,315,250,315,0,415,0,415,340]]],
[500,750,[[100,325,500,325,500,425,100,425,100,750,0,750,0,0,100,0]]],
[500,750,[[300,650,450,650,450,750,50,750,50,650,200,650,200,100,0,100,0,0,500,0,500,100,300,100]]],
[500,750,[[0,0,500,0,500,100,100,100,100,325,500,325,500,750,0,750,0,650,400,650,400,425,0,425]]],
[500,750,[[300,100,300,750,200,750,200,100,0,100,0,0,500,0,500,100]]],
[500,750,[[500,325,500,425,0,750,0,650,400,373,0,100,0,0]]],
[500,750,[[100,100,500,100,500,200,350,200,350,400,250,400,250,200,100,200,100,750,0,750,0,0,100,0]]],
[500,750,[[100,425,100,675,0,675,0,75,100,75,100,326,400,326,400,0,500,0,500,750,400,750,400,425]]],
[500,750,[[0,0,100,0,100,650,200,650,200,0,500,0,500,750,400,750,400,100,300,100,300,750,0,750,0,18]]],
[500,750,[[100,100,100,650,500,650,500,750,0,750,0,0,500,0,500,100,350,100,350,400,250,400,250,100]]],
[500,750,[[500,0,500,750,400,750,400,100,0,100,0,0]]],
[200,783,[[0,0,200,0,200,98,98,98,98,686,200,686,200,783,0,783]]],
[200,783,[[200,0,200,783,0,783,0,686,102,686,102,98,0,98,0,0]]],
[804,448,[[802,361,802,448,722,448,613,424,572,410,493,364,440,309,401,246,347,327,262,394,144,436,77,447,0,447,0,360,89,356,187,329,235,299,300,234,326,188,344,140,354,88,356,0,445,0,446,82,466,164,495,226,562,296,616,328,672,350,714,358,804,361]]],
[672,96,[[672,22,672,96,0,95,0,0,672,0]]],
[211,164,[[124,0,126,38,150,100,187,146,211,164,143,164,105,141,50,88,0,0]]],
[503,499,[[298,499,210,499,0,0,104,0,254,375,404,0,503,0]]],
[500,500,[[194,266,400,266,400,95,0,95,0,0,500,0,500,363,200,363,200,500,100,500,100,266]]],
[500,496,[[300,399,500,399,500,496,0,496,0,399,200,398,200,0,300,0]]],
[500,500,[[208,230,111,230,111,0,211,0,211,130,500,130,500,500,0,500,0,400,400,400,400,230]]],
[499,503,[[0,298,0,210,499,0,499,104,124,254,499,404,499,503]]],
[500,500,[[0,0,500,0,500,100,97,100,97,400,495,400,495,500,0,500]]],
[500,500,[[0,500,0,0,99,0,99,402,403,402,403,0,500,0,499,500]]],
[500,500,[[500,500,0,500,0,400,400,400,400,100,0,100,0,0,500,0]]],
[503,499,[[205,0,293,0,503,499,399,499,249,124,99,499,0,499]]],
[500,500,[[0,0,500,0,500,100,99,101,100,500,0,500]]],
[500,500,[[500,0,500,500,400,500,402,96,100,100,100,500,0,500,0,0]]],
[500,499,[[400,399,100,400,100,499,0,499,0,0,500,0,500,499,400,499],[400,299,400,99,100,99,100,299]]],
[503,500,[[400,200,400,100,0,100,0,0,503,0,500,500,0,500,0,400,400,400,400,300,0,300,0,200]]],
[500,500,[[400,500,400,300,0,300,0,200,400,200,400,100,0,100,0,0,500,0,500,500]]],
[500,500,[[0,291,0,191,84,184,150,151,190,100,203,43,205,0,305,0,300,96,279,158,240,210,213,228,174,241,208,255,231,274,255,320,276,274,294,252,348,212,409,194,500,191,500,291,402,301,370,315,346,334,316,384,308,416,305,500,205,500,201,418,182,366,132,319,102,304,40,292]]],
[500,500,[[317,230,100,230,100,399,500,400,500,500,0,500,0,129,315,130,315,0,415,0,415,230]]],
[500,500,[[100,300,100,500,0,500,0,0,100,0,100,200,500,200,500,300]]],
[503,500,[[301,100,301,400,503,400,503,500,1,500,1,400,201,400,201,100,0,100,0,0,501,0,501,100]]],
[500,500,[[0,1,500,0,500,100,105,100,107,200,500,200,500,500,0,500,0,405,400,405,400,300,0,300]]],
[500,500,[[200,100,0,100,0,0,500,0,500,100,300,100,300,500,200,500]]],
[499,503,[[499,205,499,293,0,503,0,399,375,249,0,99,0,0]]],
[499,500,[[99,100,499,100,499,200,349,200,349,350,249,350,249,200,99,200,99,500,0,500,0,0,99,0]]],
[500,500,[[400,300,100,300,100,500,0,500,0,0,100,0,100,200,400,200,400,0,500,0,500,500,400,500]]],
[500,500,[[0,500,0,0,100,0,100,400,200,400,200,0,500,0,500,500,400,500,400,100,300,100,300,500]]],
[500,500,[[250,100,100,100,100,400,500,400,500,500,0,500,0,0,500,0,500,100,350,100,350,300,250,300]]],
[500,500,[[500,0,500,500,400,500,400,100,0,100,0,0]]],
[340,806,[[158,0,245,0,245,74,236,143,212,218,180,284,148,326,238,356,340,359,340,446,244,450,148,480,186,534,212,586,240,688,245,731,245,805,157,806,150,690,127,620,95,566,1,469,48,430,95,403,48,376,0,337,89,249,117,206,148,122,158,51]]],
[100,750,[[100,750,0,750,0,0,100,0]]],
[340,808,[[182,53,192,124,223,207,251,251,340,339,292,378,245,405,292,432,339,471,245,568,213,622,189,698,183,808,95,808,96,714,112,638,144,552,192,482,96,452,0,448,0,360,86,359,192,328,160,286,128,220,104,145,95,76,95,0,182,1]]],
[409,161,[[126,0,160,6,161,0,230,0,234,59,246,76,283,89,309,83,332,65,337,0,409,0,409,48,392,104,355,141,326,156,299,161,179,161,175,102,162,86,124,72,90,86,76,102,72,161,0,161,1,103,20,54,59,15,113,0]]]
]
bind CHESILIN_CH = "!,.0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
bind CHESILIN = [
[97,844,[[47,0,97,0,62,218,46,250,0,250,34,42],[47,595,97,595,64,806,46,842,0,844,34,637]]],
[432,844,[[424,0,432,12,432,92,422,195,399,306,376,370,345,428,304,476,279,495,220,518,96,514,50,494,16,455,0,393,3,87,16,27,28,4,39,2,44,14,40,306,47,354,70,385,97,403,202,420,276,408,302,394,356,328,382,239,395,58,417,2],[54,590,336,590,321,751,310,798,299,830,286,844,24,844,30,714,42,632]]],
[317,844,[[30,0,317,0,294,169,276,239,262,254,0,254,2,168,26,10],[30,591,317,591,294,759,276,829,262,844,0,844,2,758,26,601]]],
[362,664,[[362,0,361,68,341,93,225,93,225,567,362,567,361,640,341,664,0,664,2,609,22,571,133,571,133,93,0,93,2,38,22,0]]],
[264,659,[[264,0,264,638,214,659,174,659,174,94,0,94,2,39,22,0]]],
[263,662,[[263,0,263,623,242,662,0,662,2,602,27,566,173,566,173,21,225,5]]],
[262,658,[[262,0,262,636,220,658,177,658,177,372,0,372,0,323,27,274,170,275,170,21,233,4]]],
[362,663,[[195,0,235,0,235,565,362,565,361,638,340,663,0,663,1,607,22,570,144,570,144,21,184,1]]],
[362,659,[[362,0,361,70,340,94,223,94,223,637,173,659,132,659,132,93,0,93,1,38,22,0]]],
[368,664,[[27,0,368,0,368,642,314,662,277,658,277,96,6,96,0,70,16,18]]],
[366,663,[[116,0,366,0,366,69,350,98,95,98,95,642,47,663,0,663,0,27,26,14,108,0]]],
[364,663,[[51,0,91,0,91,568,364,568,362,638,342,663,0,663,0,22,40,2]]],
[367,663,[[367,0,367,624,346,663,0,663,2,603,27,567,277,567,277,22,338,4]]],
[432,844,[[425,0,432,6,430,120,399,308,345,430,304,478,251,511,184,522,92,512,49,494,20,461,0,383,2,104,9,46,30,4,40,3,45,15,41,315,52,361,72,388,119,409,182,420,244,418,285,403,323,376,363,315,382,238,393,65,418,2],[166,595,216,595,186,795,168,840,120,844,153,637]]],
[78,321,[[45,0,78,0,78,276,61,321,42,321,42,102,7,103,0,91,8,38,32,3]]],
[290,1006,[[189,531,142,542,110,590,93,650,91,743,106,806,132,864,180,906,216,919,214,531],[271,0,287,7,290,985,281,994,234,1006,218,1004,215,989,187,997,121,986,63,944,38,910,8,830,0,725,14,644,34,588,53,560,131,490,188,462,214,458,212,24,228,10,266,0],[189,531,142,542,110,590,93,650,91,743,106,806,132,864,180,906,216,919,214,531],[271,0,287,7,290,985,281,994,234,1006,218,1004,215,989,187,997,121,986,63,944,38,910,8,830,0,725,14,644,34,588,53,560,131,490,188,462,214,458,212,24,228,10,266,0]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[289,1006,[[215,75,154,89,125,112,103,160,89,233,94,322,120,397,151,440,213,472],[274,0,289,5,286,982,268,996,231,1006,211,999,213,548,168,549,127,540,87,518,51,484,14,409,0,333,0,278,18,178,69,94,144,34,270,0],[215,75,154,89,125,112,103,160,89,233,94,322,120,397,151,440,213,472],[274,0,289,5,286,982,268,996,231,1006,211,999,213,548,168,549,127,540,87,518,51,484,14,409,0,333,0,278,18,178,69,94,144,34,270,0]]],
[109,270,[[54,0,95,30,108,60,108,102,79,207,55,270,18,235,1,206,45,26]]],
[393,1006,[[78,534,76,931,169,909,235,877,284,824,298,790,303,750,284,670,238,611,176,569,81,534],[64,0,80,4,77,457,138,459,211,479,317,544,319,24,326,14,354,4,393,5,393,706,389,748,356,824,332,857,268,914,158,974,14,1006,0,999,5,21,59,0],[78,534,76,931,169,909,235,877,284,824,298,790,303,750,284,670,238,611,176,569,81,534],[64,0,80,4,77,457,138,459,211,479,317,544,319,24,326,14,354,4,393,5,393,706,389,748,356,824,332,857,268,914,158,974,14,1006,0,999,5,21,59,0]]],
[392,1005,[[75,84,77,474,152,468,221,440,285,376,303,308,292,242,257,182,197,132,105,91],[59,0,72,2,75,17,99,8,157,14,264,58,333,112,368,160,384,198,392,229,392,984,375,996,335,1005,318,1001,315,427,226,495,157,526,77,548,79,980,44,1001,5,1000,0,22,53,0],[75,84,77,474,152,468,221,440,285,376,303,308,292,242,257,182,197,132,105,91],[59,0,72,2,75,17,99,8,157,14,264,58,333,112,368,160,384,198,392,229,392,984,375,996,335,1005,318,1001,315,427,226,495,157,526,77,548,79,980,44,1001,5,1000,0,22,53,0]]],
[288,997,[[272,0,286,4,288,505,258,522,145,541,112,586,91,666,93,750,109,820,125,853,174,904,209,919,250,924,259,933,251,962,194,996,151,994,95,972,38,910,8,830,0,719,22,617,52,561,130,490,188,462,214,458,211,24,266,1]]],
[180,82,[[0,0,143,0,180,55,180,82,54,82,22,58,0,23]]],
[291,1006,[[78,535,75,931,134,918,174,882,201,773,196,687,171,612,141,570,80,535],[64,0,80,4,77,457,163,467,221,504,266,568,280,606,291,678,282,800,240,889,172,956,87,993,14,1006,0,1000,5,21,59,1],[78,535,75,931,134,918,174,882,201,773,196,687,171,612,141,570,80,535],[64,0,80,4,77,457,163,467,221,504,266,568,280,606,291,678,282,800,240,889,172,956,87,993,14,1006,0,1000,5,21,59,1]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[487,1009,[[187,77,160,81,139,94,110,133,92,207,90,266,100,334,129,402,162,441,207,463,209,77,192,77],[282,548,280,934,333,929,352,916,388,848,397,784,396,723,370,629,355,601,300,555],[268,0,284,5,282,472,345,472,383,488,442,543,476,615,487,748,468,842,421,923,347,982,294,1002,242,1009,205,999,207,540,155,541,127,533,70,496,44,466,10,393,0,265,28,151,43,121,90,65,178,12,266,0],[187,77,160,81,139,94,110,133,92,207,90,266,100,334,129,402,162,441,207,463,209,77,192,77],[282,548,280,934,333,929,352,916,388,848,397,784,396,723,370,629,355,601,300,555],[268,0,284,5,282,472,345,472,383,488,442,543,476,615,487,748,468,842,421,923,347,982,294,1002,242,1009,205,999,207,540,155,541,127,533,70,496,44,466,10,393,0,265,28,151,43,121,90,65,178,12,266,0]]],
[333,1005,[[238,530,202,534,146,564,110,614,91,702,100,768,128,828,180,880,260,918,258,530,241,530],[60,0,75,3,77,559,148,501,222,467,295,455,332,466,333,982,316,996,280,1005,262,1002,259,992,217,996,174,986,116,957,57,904,8,812,0,748,1,23,19,9,54,0],[238,530,202,534,146,564,110,614,91,702,100,768,128,828,180,880,260,918,258,530,241,530],[60,0,75,3,77,559,148,501,222,467,295,455,332,466,333,982,316,996,280,1005,262,1002,259,992,217,996,174,986,116,957,57,904,8,812,0,748,1,23,19,9,54,0]]],
[288,997,[[103,0,148,6,186,22,243,77,277,149,288,272,285,312,264,383,215,460,140,518,74,538,76,974,30,996,2,994,0,495,27,477,143,456,180,406,198,315,190,218,164,150,116,96,81,79,31,68,38,34,95,1]]],
[181,228,[[29,0,142,97,176,143,180,177,164,228,52,138,7,88,2,45,20,6]]],
[392,1006,[[318,74,226,96,159,128,110,180,95,215,90,257,104,322,144,386,214,436,316,472],[377,0,392,5,388,981,372,995,329,1005,314,1000,316,547,240,544,183,527,126,500,76,461,75,983,38,1002,1,1002,0,299,4,257,37,182,62,148,126,90,236,31,366,1],[318,74,226,96,159,128,110,180,95,215,90,257,104,322,144,386,214,436,316,472],[377,0,392,5,388,981,372,995,329,1005,314,1000,316,547,240,544,183,527,126,500,76,461,75,983,38,1002,1,1002,0,299,4,257,37,182,62,148,126,90,236,31,366,1]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[289,997,[[63,0,77,4,75,458,137,460,173,472,229,514,265,574,278,613,289,673,289,728,269,830,216,916,140,975,81,995,51,997,31,990,40,956,82,928,146,910,175,876,190,835,199,771,192,678,168,608,147,578,120,552,87,536,28,532,0,520,3,24,57,0]]],
[332,1006,[[74,88,76,474,147,464,194,433,226,384,243,310,228,221,200,170,155,124,76,88],[59,0,70,2,74,13,98,8,160,17,216,44,263,82,292,117,327,194,332,984,318,995,275,1006,258,1002,255,446,187,503,149,523,74,546,29,549,2,542,0,22,52,0],[74,88,76,474,147,464,194,433,226,384,243,310,228,221,200,170,155,124,76,88],[59,0,70,2,74,13,98,8,160,17,216,44,263,82,292,117,327,194,332,984,318,995,275,1006,258,1002,255,446,187,503,149,523,74,546,29,549,2,542,0,22,52,0]]],
[291,1005,[[76,86,76,475,136,470,155,456,182,413,200,341,200,261,184,195,153,134,108,98],[59,0,72,4,76,17,106,8,151,14,188,30,246,85,280,157,291,288,287,326,265,395,216,472,168,514,110,541,77,547,80,981,62,996,25,1005,5,999,0,24,11,12,55,0],[76,86,76,475,136,470,155,456,182,413,200,341,200,261,184,195,153,134,108,98],[59,0,72,4,76,17,106,8,151,14,188,30,246,85,280,157,291,288,287,326,265,395,216,472,168,514,110,541,77,547,80,981,62,996,25,1005,5,999,0,24,11,12,55,0]]],
[202,364,[[143,0,202,12,202,30,145,56,145,336,92,363,75,364,47,347,10,312,0,288,19,258,38,256,50,270,50,28,120,2]]],
[392,1006,[[77,510,77,579,166,510,392,510,392,985,374,998,335,1006,321,1002,318,989,288,998,258,996,161,966,73,906,28,850,3,784,0,748,2,510],[317,531,242,538,152,581,105,637,90,707,95,748,110,786,159,846,220,887,319,921],[2,510,1,24,18,10,54,0,75,4,77,510],[392,510,166,510,242,476,317,458,315,25,342,6,379,0,389,8],[317,531,242,538,152,581,105,637,90,707,95,748,110,786,159,846,220,887,319,921],[60,0,75,4,77,579,136,529,196,494,317,458,315,25,342,6,388,4,392,985,374,998,335,1006,321,1002,318,989,288,998,258,996,194,980,100,929,28,850,3,784,1,24,18,10,54,0]]],
[333,1006,[[259,78,186,93,148,118,106,174,91,233,96,306,128,382,171,428,257,470],[320,0,333,4,332,522,321,533,277,546,200,546,127,520,77,482,75,984,58,997,22,1006,1,999,0,301,11,225,42,158,114,77,209,26,315,0],[259,78,186,93,148,118,106,174,91,233,96,306,128,382,171,428,257,470],[320,0,333,4,332,522,321,533,277,546,200,546,127,520,77,482,75,984,58,997,22,1006,1,999,0,301,11,225,42,158,114,77,209,26,315,0]]],
[487,1009,[[277,81,280,473,335,462,360,441,387,381,396,317,389,217,371,166,341,120,280,81],[205,539,149,549,114,588,96,640,90,722,96,789,122,860,166,910,207,929],[262,0,334,6,371,18,427,60,463,120,476,159,487,219,487,274,474,354,436,435,371,502,313,531,279,539,282,983,255,1000,195,1009,156,1007,102,987,44,932,11,860,0,736,12,656,50,576,114,510,205,471,203,24,219,10,256,0],[277,81,280,473,335,462,360,441,387,381,396,317,389,217,371,166,341,120,280,81],[205,539,149,549,114,588,96,640,90,722,96,789,122,860,166,910,207,929],[262,0,334,6,371,18,427,60,463,120,476,159,487,219,487,274,474,354,436,435,371,502,313,531,279,539,282,983,255,1000,195,1009,156,1007,102,987,44,932,11,860,0,736,12,656,50,576,114,510,205,471,203,24,219,10,256,0]]],
[487,1008,[[278,81,280,464,339,452,360,435,385,385,397,320,394,238,375,175,344,123,295,87],[263,0,340,7,380,22,440,75,476,146,487,278,484,317,456,397,390,478,307,524,233,544,153,554,126,574,106,607,90,695,91,743,98,795,117,848,151,895,178,916,225,932,270,935,277,948,255,985,192,1008,109,990,58,950,19,881,0,791,0,736,12,658,28,617,69,556,142,500,205,480,203,23,220,10,257,0],[278,81,280,464,339,452,360,435,385,385,397,320,394,238,375,175,344,123,295,87],[263,0,340,7,380,22,440,75,476,146,487,278,484,317,456,397,390,478,307,524,233,544,153,554,126,574,106,607,90,695,91,743,98,795,117,848,151,895,178,916,225,932,270,935,277,948,255,985,192,1008,109,990,58,950,19,881,0,791,0,736,12,658,28,617,69,556,142,500,205,480,203,23,220,10,257,0]]],
[480,1009,[[199,544,135,560,106,592,91,632,83,748,110,850,150,901,202,927],[287,0,333,5,372,20,433,73,454,107,477,183,480,280,466,356,449,396,407,456,334,510,274,529,276,986,230,1008,161,1006,112,993,47,944,0,842,0,678,33,592,100,522,182,480,328,453,355,432,374,400,390,293,384,228,361,156,314,98,280,80,208,72,203,60,225,23,278,1],[199,544,135,560,106,592,91,632,83,748,110,850,150,901,202,927],[287,0,333,5,372,20,433,73,454,107,477,183,480,280,466,356,449,396,407,456,334,510,274,529,276,986,230,1008,161,1006,112,993,47,944,0,842,0,678,33,592,100,522,182,480,328,453,355,432,374,400,390,293,384,228,361,156,314,98,280,80,208,72,203,60,225,23,278,1]]],
[78,321,[[45,0,78,0,78,276,61,321,42,321,42,102,7,103,0,91,8,38,32,3]]],
[290,1006,[[189,531,142,542,110,590,93,650,91,743,106,806,132,864,180,906,216,919,214,531],[271,0,287,7,290,985,281,994,234,1006,218,1004,215,989,187,997,121,986,63,944,38,910,8,830,0,725,14,644,34,588,53,560,131,490,188,462,214,458,212,24,228,10,266,0],[189,531,142,542,110,590,93,650,91,743,106,806,132,864,180,906,216,919,214,531],[271,0,287,7,290,985,281,994,234,1006,218,1004,215,989,187,997,121,986,63,944,38,910,8,830,0,725,14,644,34,588,53,560,131,490,188,462,214,458,212,24,228,10,266,0]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[289,1006,[[215,75,154,89,125,112,103,160,89,233,94,322,120,397,151,440,213,472],[274,0,289,5,286,982,268,996,231,1006,211,999,213,548,168,549,127,540,87,518,51,484,14,409,0,333,0,278,18,178,69,94,144,34,270,0],[215,75,154,89,125,112,103,160,89,233,94,322,120,397,151,440,213,472],[274,0,289,5,286,982,268,996,231,1006,211,999,213,548,168,549,127,540,87,518,51,484,14,409,0,333,0,278,18,178,69,94,144,34,270,0]]],
[109,270,[[54,0,95,30,108,60,108,102,79,207,55,270,18,235,1,206,45,26]]],
[395,1006,[[78,534,76,931,169,909,235,877,284,824,298,790,303,750,284,670,238,611,176,569,81,534],[64,0,80,4,77,457,138,459,211,479,317,544,319,24,326,14,355,4,395,5,394,690,389,748,376,787,332,857,302,887,232,938,158,974,14,1006,0,999,5,21,59,0],[78,534,76,931,169,909,235,877,284,824,298,790,303,750,284,670,238,611,176,569,81,534],[64,0,80,4,77,457,138,459,211,479,317,544,319,24,326,14,355,4,395,5,394,690,389,748,376,787,332,857,302,887,232,938,158,974,14,1006,0,999,5,21,59,0]]],
[392,1005,[[75,84,77,474,152,468,221,440,285,376,303,308,292,242,257,182,197,132,105,91],[59,0,72,2,75,17,99,8,157,14,264,58,333,112,368,160,384,198,392,229,392,984,375,996,335,1005,318,1001,315,427,226,495,157,526,77,548,79,980,44,1001,5,1000,0,22,53,0],[75,84,77,474,152,468,221,440,285,376,303,308,292,242,257,182,197,132,105,91],[59,0,72,2,75,17,99,8,157,14,264,58,333,112,368,160,384,198,392,229,392,984,375,996,335,1005,318,1001,315,427,226,495,157,526,77,548,79,980,44,1001,5,1000,0,22,53,0]]],
[288,997,[[272,0,286,4,288,505,258,522,145,541,112,586,91,666,93,750,109,820,125,853,174,904,209,919,250,924,259,933,251,962,194,996,151,994,95,972,38,910,8,830,0,719,22,617,52,561,130,490,188,462,214,458,211,24,266,1]]],
[180,82,[[0,0,143,0,180,55,180,82,54,82,22,58,0,23]]],
[291,1006,[[78,535,75,931,134,918,174,882,201,773,196,687,171,612,141,570,80,535],[64,0,80,4,77,457,163,467,221,504,266,568,280,606,291,678,282,800,240,889,172,956,87,993,14,1006,0,1000,5,21,59,1],[78,535,75,931,134,918,174,882,201,773,196,687,171,612,141,570,80,535],[64,0,80,4,77,457,163,467,221,504,266,568,280,606,291,678,282,800,240,889,172,956,87,993,14,1006,0,1000,5,21,59,1]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[487,1009,[[187,77,160,81,139,94,110,133,92,207,90,266,100,334,129,402,162,441,207,463,209,77,192,77],[282,548,280,934,333,929,352,916,388,848,397,784,396,723,370,629,355,601,300,555],[268,0,284,5,282,472,345,472,383,488,442,543,476,615,487,748,468,842,421,923,347,982,294,1002,242,1009,205,999,207,540,155,541,127,533,70,496,44,466,10,393,0,265,28,151,43,121,90,65,178,12,266,0],[187,77,160,81,139,94,110,133,92,207,90,266,100,334,129,402,162,441,207,463,209,77,192,77],[282,548,280,934,333,929,352,916,388,848,397,784,396,723,370,629,355,601,300,555],[268,0,284,5,282,472,345,472,383,488,442,543,476,615,487,748,468,842,421,923,347,982,294,1002,242,1009,205,999,207,540,155,541,127,533,70,496,44,466,10,393,0,265,28,151,43,121,90,65,178,12,266,0]]],
[333,1005,[[238,530,202,534,146,564,110,614,91,702,100,768,128,828,180,880,260,918,258,530,241,530],[60,0,75,3,77,559,148,501,222,467,295,455,332,466,333,982,316,996,280,1005,262,1002,259,992,217,996,174,986,116,957,57,904,8,812,0,748,1,23,19,9,54,0],[238,530,202,534,146,564,110,614,91,702,100,768,128,828,180,880,260,918,258,530,241,530],[60,0,75,3,77,559,148,501,222,467,295,455,332,466,333,982,316,996,280,1005,262,1002,259,992,217,996,174,986,116,957,57,904,8,812,0,748,1,23,19,9,54,0]]],
[288,997,[[103,0,148,6,186,22,243,77,277,149,288,272,285,312,264,383,215,460,140,518,74,538,76,974,30,996,2,994,0,495,27,477,143,456,180,406,198,315,190,218,164,150,116,96,81,79,31,68,38,34,95,1]]],
[181,228,[[29,0,142,97,176,143,180,177,164,228,52,138,7,88,2,45,20,6]]],
[392,1006,[[318,74,226,96,159,128,110,180,95,215,90,257,104,322,144,386,214,436,316,472],[377,0,392,5,388,981,372,995,329,1005,314,1000,316,547,240,544,183,527,126,500,76,461,75,983,38,1002,1,1002,0,299,4,257,37,182,62,148,126,90,236,31,366,1],[318,74,226,96,159,128,110,180,95,215,90,257,104,322,144,386,214,436,316,472],[377,0,392,5,388,981,372,995,329,1005,314,1000,316,547,240,544,183,527,126,500,76,461,75,983,38,1002,1,1002,0,299,4,257,37,182,62,148,126,90,236,31,366,1]]],
[290,1005,[[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0],[77,314,76,709,140,696,164,674,190,621,200,551,197,479,179,406,154,363,106,324],[63,0,78,4,78,237,138,239,175,252,231,295,251,322,280,393,290,445,290,517,286,556,264,625,212,701,136,757,74,775,74,981,59,995,15,1005,0,998,4,23,19,10,58,0]]],
[289,997,[[63,0,77,4,75,458,137,460,173,472,229,514,265,574,278,613,289,673,289,728,269,830,216,916,140,975,81,995,51,997,31,990,40,956,82,928,146,910,175,876,190,835,199,771,192,678,168,608,147,578,120,552,87,536,28,532,0,520,3,24,57,0]]],
[332,1006,[[74,88,76,474,147,464,194,433,226,384,243,310,228,221,200,170,155,124,76,88],[59,0,70,2,74,13,98,8,160,17,216,44,263,82,292,117,327,194,332,984,318,995,275,1006,258,1002,255,446,187,503,149,523,74,546,29,549,2,542,0,22,52,0],[74,88,76,474,147,464,194,433,226,384,243,310,228,221,200,170,155,124,76,88],[59,0,70,2,74,13,98,8,160,17,216,44,263,82,292,117,327,194,332,984,318,995,275,1006,258,1002,255,446,187,503,149,523,74,546,29,549,2,542,0,22,52,0]]],
[291,1005,[[76,86,76,475,136,470,155,456,182,413,200,341,200,261,184,195,153,134,108,98],[59,0,72,4,76,17,106,8,151,14,188,30,246,85,280,157,291,288,287,326,265,395,216,472,168,514,110,541,77,547,80,981,62,996,25,1005,5,999,0,24,11,12,55,0],[76,86,76,475,136,470,155,456,182,413,200,341,200,261,184,195,153,134,108,98],[59,0,72,4,76,17,106,8,151,14,188,30,246,85,280,157,291,288,287,326,265,395,216,472,168,514,110,541,77,547,80,981,62,996,25,1005,5,999,0,24,11,12,55,0]]],
[202,364,[[143,0,202,12,202,30,145,56,145,336,92,363,75,364,47,347,10,312,0,288,19,258,38,256,50,270,50,28,120,2]]],
[392,1006,[[77,510,77,579,166,510,392,510,392,985,374,998,335,1006,321,1002,318,989,288,998,258,996,161,966,73,906,28,850,3,784,0,748,2,510],[317,531,242,538,152,581,105,637,90,707,95,748,110,786,159,846,220,887,319,921],[2,510,1,24,18,10,54,0,75,4,77,510],[166,510,242,476,317,458,315,25,332,10,369,0,389,8,392,510],[317,531,242,538,152,581,105,637,90,707,95,748,110,786,159,846,220,887,319,921],[60,0,75,4,77,579,136,529,196,494,317,458,315,25,342,6,388,4,392,985,374,998,335,1006,321,1002,318,989,288,998,258,996,194,980,100,929,28,850,3,784,1,24,18,10,54,0]]],
[333,1006,[[259,78,186,93,148,118,106,174,91,233,96,306,128,382,171,428,257,470],[320,0,333,4,332,522,321,533,277,546,200,546,127,520,77,482,75,984,58,997,22,1006,1,999,0,301,11,225,42,158,114,77,209,26,315,0],[259,78,186,93,148,118,106,174,91,233,96,306,128,382,171,428,257,470],[320,0,333,4,332,522,321,533,277,546,200,546,127,520,77,482,75,984,58,997,22,1006,1,999,0,301,11,225,42,158,114,77,209,26,315,0]]],
[487,1009,[[277,81,280,473,335,462,360,441,387,381,396,317,389,217,371,166,341,120,280,81],[205,539,149,549,114,588,96,640,90,722,96,789,122,860,166,910,207,929],[262,0,334,6,371,18,427,60,463,120,476,159,487,219,487,274,474,354,436,435,371,502,313,531,279,539,282,983,255,1000,195,1009,156,1007,102,987,44,932,11,860,0,736,12,656,50,576,114,510,205,471,203,24,219,10,256,0],[277,81,280,473,335,462,360,441,387,381,396,317,389,217,371,166,341,120,280,81],[205,539,149,549,114,588,96,640,90,722,96,789,122,860,166,910,207,929],[262,0,334,6,371,18,427,60,463,120,476,159,487,219,487,274,474,354,436,435,371,502,313,531,279,539,282,983,255,1000,195,1009,156,1007,102,987,44,932,11,860,0,736,12,656,50,576,114,510,205,471,203,24,219,10,256,0]]],
[487,1008,[[278,81,280,464,339,452,360,435,385,385,397,320,394,238,375,175,344,123,295,87],[263,0,340,7,380,22,440,75,476,146,487,278,484,317,456,397,390,478,307,524,233,544,153,554,126,574,106,607,90,695,91,743,98,795,117,848,151,895,178,916,225,932,270,935,277,948,255,985,192,1008,109,990,58,950,19,881,0,791,0,736,12,658,28,617,69,556,142,500,205,480,203,23,220,10,257,0],[278,81,280,464,339,452,360,435,385,385,397,320,394,238,375,175,344,123,295,87],[263,0,340,7,380,22,440,75,476,146,487,278,484,317,456,397,390,478,307,524,233,544,153,554,126,574,106,607,90,695,91,743,98,795,117,848,151,895,178,916,225,932,270,935,277,948,255,985,192,1008,109,990,58,950,19,881,0,791,0,736,12,658,28,617,69,556,142,500,205,480,203,23,220,10,257,0]]],
[480,1009,[[199,544,135,560,106,592,91,632,83,748,110,850,150,901,202,927],[287,0,333,5,372,20,433,73,454,107,477,183,480,280,466,356,449,396,407,456,334,510,274,529,276,986,230,1008,161,1006,112,993,47,944,0,842,0,678,33,592,100,522,182,480,328,453,355,432,374,400,390,293,384,228,361,156,314,98,280,80,208,72,203,60,225,23,278,1],[199,544,135,560,106,592,91,632,83,748,110,850,150,901,202,927],[287,0,333,5,372,20,433,73,454,107,477,183,480,280,466,356,449,396,407,456,334,510,274,529,276,986,230,1008,161,1006,112,993,47,944,0,842,0,678,33,592,100,522,182,480,328,453,355,432,374,400,390,293,384,228,361,156,314,98,280,80,208,72,203,60,225,23,278,1]]]
]
# ---- END GLYPHS ----

# ───────────────────────────── small helpers ─────────────────────────────────
fn mix(a, b, k) { return a + (b - a) * k }

fn pos_mod(a, m) {
    bind r = a % m
    if r < 0.0 { bind r = r + m }
    return r
}

# set the pen to Ling hue k; shift is the global hue drift in degrees
fn col(k, sat, lit, shift) {
    set_color_hsl(pos_mod(HUE[k] + shift, 360.0), clamp(sat, 0.0, 100.0), clamp(lit, 0.0, 100.0))
}

fn ink(lit) { set_color_hsl(214.0, 14.0, clamp(lit, 0.0, 100.0)) }

fn px(cx, r, a) { return cx + r * sin(a) }
fn py(cy, r, a) { return cy - r * cos(a) }

fn pline(cx, cy, r0, a0, r1, a1) {
    draw_line(px(cx, r0, a0), py(cy, r0, a0), px(cx, r1, a1), py(cy, r1, a1))
}

# square of half-size h centred on (x, y), turned by a
fn square(x, y, h, a) {
    bind c = cos(a)
    bind s = sin(a)
    bind x0 = x - h * c + h * s
    bind y0 = y - h * s - h * c
    bind x1 = x + h * c + h * s
    bind y1 = y + h * s - h * c
    bind x2 = x + h * c - h * s
    bind y2 = y + h * s + h * c
    bind x3 = x - h * c - h * s
    bind y3 = y - h * s + h * c
    draw_line(x0, y0, x1, y1)
    draw_line(x1, y1, x2, y2)
    draw_line(x2, y2, x3, y3)
    draw_line(x3, y3, x0, y0)
}

# hatch inside a circle: parallel chords at angle a, spacing sp
fn hatch_disc(cx, cy, r, a, sp) {
    bind nx = cos(a + 1.5708)
    bind ny = sin(a + 1.5708)
    bind tx = cos(a)
    bind ty = sin(a)
    bind d = 0.0 - r + sp * 0.5
    while d < r {
        bind h = sqrt(max(r * r - d * d, 0.0))
        draw_line(cx + nx * d - tx * h, cy + ny * d - ty * h, cx + nx * d + tx * h, cy + ny * d + ty * h)
        bind d = d + sp
    }
}

# hatch inside an axis-aligned square [x, x+s] x [y, y+s], lines at 45 degrees
fn hatch_box(x, y, s, sp) {
    bind d = sp
    while d < 2.0 * s {
        bind ax = x
        bind ay = y + d
        bind bx = x + d
        bind by = y
        if d > s {
            bind ax = x + d - s
            bind ay = y + s
            bind bx = x + s
            bind by = y + d - s
        }
        draw_line(ax, ay, bx, by)
        bind d = d + sp
    }
}

fn fmt_mb(v) {
    if v >= 1024.0 { return num_str(round(v / 102.4) / 10.0) + "G" }
    return num_str(round(v)) + "M"
}

fn fmt_rate(v) {
    if v >= 10000.0 { return num_str(round(v / 1000.0)) + "K" }
    if v >= 1000.0 { return num_str(round(v / 100.0) / 10.0) + "K" }
    return num_str(round(v))
}

# ───────────────────────────── glyph drawing ─────────────────────────────────
# Outlines come from the Ling bundle. Contours are closed, y runs down.

# Daemon glyph fitted into a bw x bh box centred on (x, y), turned by a
fn daemon_glyph(ch, x, y, bw, bh, a) {
    bind i = str_find(DAEMON_CH, ch)
    if i < 0 { return 0 }
    bind g = DAEMON[i]
    bind sc = min(bw / g[0], bh / g[1])
    bind gw = g[0] * sc
    bind gh = g[1] * sc
    bind c = cos(a)
    bind s = sin(a)
    bind polys = g[2]
    bind p = 0
    while p < len(polys) {
        bind pts = polys[p]
        bind n = len(pts) / 2
        bind j = 0
        while j < n {
            bind k = (j + 1) % n
            bind ux = pts[j * 2] * sc - gw / 2.0
            bind uy = pts[j * 2 + 1] * sc - gh / 2.0
            bind vx = pts[k * 2] * sc - gw / 2.0
            bind vy = pts[k * 2 + 1] * sc - gh / 2.0
            draw_line(x + ux * c - uy * s, y + ux * s + uy * c, x + vx * c - vy * s, y + vx * s + vy * c)
            bind j = j + 1
        }
        bind p = p + 1
    }
    return 1
}

# a run of Daemon letters, h tall, left-aligned at (x, y) where y is the vertical centre
fn daemon_text(str, x, y, h) {
    bind n = str_len(str)
    bind i = 0
    while i < n {
        bind ch = str_slice(str, i, 1)
        if ch != " " { daemon_glyph(ch, x + i * h * 0.74 + h * 0.3, y, h * 0.62, h, 0.0) }
        bind i = i + 1
    }
}

fn text_w(str, h) { return str_len(str) * h * 0.74 }

# Chesilin glyph bent round a ring: radius rm, centred on angle ac (radians, clockwise from up)
fn chesilin_glyph(ch, cx, cy, rm, ac, bw, bh) {
    bind i = str_find(CHESILIN_CH, ch)
    if i < 0 { return 0 }
    bind g = CHESILIN[i]
    bind sc = min(bw / g[0], bh / g[1])
    bind gw = g[0] * sc
    bind gh = g[1] * sc
    bind polys = g[2]
    bind p = 0
    while p < len(polys) {
        bind pts = polys[p]
        bind n = len(pts) / 2
        bind j = 0
        while j < n {
            bind k = (j + 1) % n
            bind ux = pts[j * 2] * sc - gw / 2.0
            bind uy = pts[j * 2 + 1] * sc - gh / 2.0
            bind vx = pts[k * 2] * sc - gw / 2.0
            bind vy = pts[k * 2 + 1] * sc - gh / 2.0
            bind m = 1 + floor(abs(vx - ux) / (rm * 0.05))
            bind q = 0
            while q < m {
                bind f0 = q / m
                bind f1 = (q + 1) / m
                pline(cx, cy, rm - mix(uy, vy, f0), ac + mix(ux, vx, f0) / rm, rm - mix(uy, vy, f1), ac + mix(ux, vx, f1) / rm)
                bind q = q + 1
            }
            bind j = j + 1
        }
        bind p = p + 1
    }
    return 1
}

# ring text: the string repeats round the whole ring in n cells, turned by spin
fn ring_text(str, cx, cy, r0, r1, spin, lit, k, sat, hs) {
    bind band = r1 - r0
    bind rm = (r0 + r1) / 2.0
    bind sl = str_len(str)
    bind n = floor(TAU * rm / (band * 0.62))
    bind da = TAU / n
    col(k, sat, lit, hs)
    bind i = 0
    while i < n {
        bind ch = str_slice(str, i % sl, 1)
        if ch != " " { chesilin_glyph(ch, cx, cy, rm, spin + (i + 0.5) * da, rm * da * 0.72, band * 0.62) }
        bind i = i + 1
    }
}

# a Daemon tile: frame, corner brackets, letter, optional hatch
fn tile(ch, x, y, s, lit, k, sat, hs, fillk) {
    col(k, sat, lit, hs)
    draw_line(x, y, x + s, y)
    draw_line(x + s, y, x + s, y + s)
    draw_line(x + s, y + s, x, y + s)
    draw_line(x, y + s, x, y)
    bind a = s * 0.055
    bind b = s * 0.21
    draw_line(x + a, y + a + b, x + a, y + a)
    draw_line(x + a, y + a, x + a + b, y + a)
    draw_line(x + s - a - b, y + a, x + s - a, y + a)
    draw_line(x + s - a, y + a, x + s - a, y + a + b)
    draw_line(x + s - a, y + s - a - b, x + s - a, y + s - a)
    draw_line(x + s - a, y + s - a, x + s - a - b, y + s - a)
    draw_line(x + a + b, y + s - a, x + a, y + s - a)
    draw_line(x + a, y + s - a, x + a, y + s - a - b)
    if fillk > 0.0 {
        col(k, sat * 0.8, lit * 0.45, hs)
        hatch_box(x + a, y + a, s - 2.0 * a, max(2.5, s * 0.07) / fillk)
    }
    col(k, sat, min(lit + 16.0, 92.0), hs)
    daemon_glyph(ch, x + s / 2.0, y + s / 2.0, s * 0.6, s * 0.6, 0.0)
}

# ───────────────────────────── generic snapshot helpers ──────────────────────
# The *.snap files are written by sampler.ps1 (memory) and sampler_hw.ps1 (gpu, board, net).
# One record per line, fields split by '|'. A row is a list of strings.

fn nf(row, i) {
    if i < len(row) { return to_number(row[i]) }
    return 0.0
}

fn sf(row, i) {
    if i < len(row) { return row[i] }
    return ""
}

fn nums(row, from) {
    bind out = []
    bind i = from
    while i < len(row) {
        bind out = list_push(out, to_number(row[i]))
        bind i = i + 1
    }
    return out
}

fn zeros(n) {
    bind out = []
    bind i = 0
    while i < n {
        bind out = list_push(out, 0.0)
        bind i = i + 1
    }
    return out
}

fn pad_to(l, n) {
    bind out = l
    while len(out) < n {
        bind out = list_push(out, 0.0)
    }
    return out
}

fn snap_rows(text) {
    bind lines = split(text, "\n")
    bind out = []
    bind i = 0
    while i < len(lines) {
        bind ln = trim(lines[i])
        if str_len(ln) > 2 { bind out = list_push(out, split(ln, "|")) }
        bind i = i + 1
    }
    return out
}

# the sampler's wall-clock stamp: if it stops changing, the sampler has stopped
fn snap_time(rows) {
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        if r[0] == "t" { return nf(r, 1) }
        bind i = i + 1
    }
    return 0.0
}

# glide a list of numbers (or a list of such lists) toward its target
fn glide(cur, tgt, k) {
    if len(cur) != len(tgt) { return tgt }
    bind out = []
    bind i = 0
    while i < len(cur) {
        bind out = list_push(out, mix(cur[i], tgt[i], k))
        bind i = i + 1
    }
    return out
}

fn glide_ll(cur, tgt, k) {
    if len(cur) != len(tgt) { return tgt }
    bind out = []
    bind i = 0
    while i < len(cur) {
        bind out = list_push(out, glide(cur[i], tgt[i], k))
        bind i = i + 1
    }
    return out
}

# log scale to 0..1: full is log10 of the value that fills the strip
fn lg(x, full) { return clamp(log10(1.0 + max(x, 0.0)) / full, 0.0, 1.0) }

fn fmt_bytes(v) {
    if v >= 1000000000.0 { return num_str(round(v / 100000000.0) / 10.0) + "G" }
    if v >= 1000000.0 { return num_str(round(v / 100000.0) / 10.0) + "M" }
    if v >= 1000.0 { return num_str(round(v / 1000.0)) + "K" }
    return num_str(round(v))
}

fn fmt_up(s) {
    bind d = floor(s / 86400.0)
    bind h = floor((s - d * 86400.0) / 3600.0)
    bind m = floor((s - d * 86400.0 - h * 3600.0) / 60.0)
    if d > 0 { return num_str(d) + "D " + num_str(h) + "H" }
    return num_str(h) + "H " + num_str(m) + "M"
}

fn pct(x) { return num_str(round(x)) }

fn short_gpu(name) {
    bind s = str_replace(name, "NVIDIA ", "")
    bind s = str_replace(s, "GEFORCE ", "")
    bind s = str_replace(s, "AMD ", "")
    bind s = str_replace(s, "RADEON ", "")
    bind s = str_replace(s, "INTEL(R) ", "")
    return str_slice(s, 0, 14)
}

# ───────────────────────────── history strips ────────────────────────────────
fn hs_new() {
    bind hs = []
    bind i = 0
    while i < HS_COUNT {
        bind hs = list_push(hs, zeros(HS_N))
        bind i = i + 1
    }
    return hs
}

fn hs_push(hs, id, v) {
    bind old = hs[id]
    bind nw = []
    bind i = 1
    while i < len(old) {
        bind nw = list_push(nw, old[i])
        bind i = i + 1
    }
    bind nw = list_push(nw, clamp(v, 0.0, 1.0))
    return list_set(hs, id, nw)
}

# ───────────────────────────── GPU ───────────────────────────────────────────
# pack GP: [good, n, names, drvs, vecs, engs, procs, sel]
bind GP_N = 1
bind GP_NAME = 2
bind GP_DRV = 3
bind GP_VEC = 4
bind GP_ENG = 5
bind GP_PROC = 6
bind GP_SEL = 7

fn parse_gpu(text) {
    bind rows = snap_rows(text)
    bind n = 0
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        if r[0] == "n" { bind n = max(n, floor(nf(r, 1)) + 1) }
        bind i = i + 1
    }
    bind n = min(n, 8)
    bind names = []
    bind drvs = []
    bind vecs = []
    bind engs = []
    bind procs = []
    bind g = 0
    while g < n {
        bind names = list_push(names, "GPU")
        bind drvs = list_push(drvs, "")
        bind vecs = list_push(vecs, zeros(GV_LEN))
        bind engs = list_push(engs, zeros(6))
        bind procs = list_push(procs, [[], [], [], []])
        bind g = g + 1
    }
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        bind tag = r[0]
        bind g = floor(nf(r, 1))
        if g >= 0 {
            if g < n {
                if tag == "n" {
                    bind names = list_set(names, g, str_upper(sf(r, 2)))
                    bind drvs = list_set(drvs, g, sf(r, 3))
                }
                if tag == "v" { bind vecs = list_set(vecs, g, pad_to(nums(r, 2), GV_LEN)) }
                if tag == "e" { bind engs = list_set(engs, g, pad_to(nums(r, 2), 6)) }
                if tag == "a" {
                    bind pg = procs[g]
                    bind pg = [list_push(pg[0], str_upper(sf(r, 2))), list_push(pg[1], nf(r, 4)), list_push(pg[2], nf(r, 5)), list_push(pg[3], nf(r, 6))]
                    bind procs = list_set(procs, g, pg)
                }
            }
        }
        bind i = i + 1
    }
    bind good = 0
    if n > 0 { bind good = 1 }
    return [good, n, names, drvs, vecs, engs, procs, 0, snap_time(rows)]
}

bind SIM_NAMES = ["chrome", "code", "firefox", "explorer", "python", "node", "slack", "spotify", "ling", "audiodg",
                  "svchost", "dwm", "teams", "steam", "obs", "docker", "mysqld", "discord", "winword", "excel"]

fn sim_gpu(t) {
    bind names = []
    bind drvs = []
    bind vecs = []
    bind engs = []
    bind procs = []
    bind g = 0
    while g < 4 {
        bind u = 22.0 + 20.0 * sin(t * 0.31 + g * 2.0) + 55.0 * pow(abs(sin(t * 0.07 + g * 1.3)), 4.0)
        bind u = clamp(u, 1.0, 100.0)
        bind vu = 9000.0 + 5000.0 * sin(t * 0.05 + g) + 40.0 * u
        bind names = list_push(names, "SIM GPU " + num_str(g))
        bind drvs = list_push(drvs, "0.0")
        bind vecs = list_push(vecs, [36.0 + 0.4 * u, 28.0 + 0.55 * u, 18.0 + 4.1 * u, 450.0, 210.0 + 28.0 * u, 3105.0, 405.0 + 100.0 * u, 10501.0, u, u * 0.4, vu, 24564.0, 4.0, 16.0, 0.0, 0.0])
        bind engs = list_push(engs, [u * 0.95, u * 0.3 * (1.0 + sin(t * 0.4)), 12.0 * pow(abs(sin(t * 0.21 + g)), 3.0), 6.0 * pow(abs(sin(t * 0.13)), 5.0), 25.0 * pow(abs(sin(t * 0.09 + g)), 6.0), 3.0])
        bind pn = []
        bind pd = []
        bind ps = []
        bind pu = []
        bind q = 0
        while q < 8 {
            bind pn = list_push(pn, str_upper(SIM_NAMES[(q + g * 5) % 20]))
            bind pd = list_push(pd, vu * 0.5 / (1.0 + q * 0.9))
            bind ps = list_push(ps, 30.0 / (1.0 + q))
            bind pu = list_push(pu, u * 0.7 / (1.0 + q))
            bind q = q + 1
        }
        bind procs = list_push(procs, [pn, pd, ps, pu])
        bind g = g + 1
    }
    return [1, 4, names, drvs, vecs, engs, procs, 0, 0.0]
}

# ───────────────────────────── board ─────────────────────────────────────────
# pack BP: [good, info, bv, cutil, cfreq, dimms, disks, dio, sens, t]
bind BP_INFO = 1
bind BP_BV = 2
bind BP_CU = 3
bind BP_CF = 4
bind BP_DIMM = 5
bind BP_DISK = 6
bind BP_DIO = 7
bind BP_SENS = 8
# info: [maker, product, bios vendor, bios version, bios date, cpu, cores, threads, max MHz, sockets, dimm slots, max GB]
bind BI_MAKER = 0
bind BI_PROD = 1
bind BI_BIOSV = 2
bind BI_BIOSVER = 3
bind BI_BIOSDATE = 4
bind BI_CPU = 5
bind BI_CORES = 6
bind BI_THR = 7
bind BI_MHZ = 8
bind BI_SOCK = 9
bind BI_SLOTS = 10
bind BI_MAXGB = 11

fn parse_board(text) {
    bind rows = snap_rows(text)
    bind info = ["", "", "", "", "", "", 0, 0, 0, 1, 0, 0]
    bind bv = []
    bind nc = 0
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        if r[0] == "k" { bind nc = max(nc, floor(nf(r, 1)) + 1) }
        bind i = i + 1
    }
    bind nc = min(nc, 128)
    bind cu = zeros(nc)
    bind cf = zeros(nc)
    bind dimms = []
    bind disks = []
    bind dio = []
    bind sens = []
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        bind tag = r[0]
        if tag == "b" {
            bind info = list_set(info, BI_MAKER, str_upper(sf(r, 1)))
            bind info = list_set(info, BI_PROD, str_upper(sf(r, 2)))
        }
        if tag == "i" {
            bind info = list_set(info, BI_BIOSV, str_upper(sf(r, 1)))
            bind info = list_set(info, BI_BIOSVER, sf(r, 2))
            bind info = list_set(info, BI_BIOSDATE, sf(r, 3))
        }
        if tag == "c" {
            bind info = list_set(info, BI_CPU, str_upper(sf(r, 1)))
            bind info = list_set(info, BI_CORES, nf(r, 2))
            bind info = list_set(info, BI_THR, nf(r, 3))
            bind info = list_set(info, BI_MHZ, nf(r, 4))
            bind info = list_set(info, BI_SOCK, nf(r, 5))
        }
        if tag == "y" {
            bind info = list_set(info, BI_SLOTS, nf(r, 1))
            bind info = list_set(info, BI_MAXGB, nf(r, 2))
        }
        if tag == "m" { bind dimms = list_push(dimms, [str_upper(sf(r, 1)), nf(r, 2), nf(r, 3)]) }
        if tag == "d" { bind disks = list_push(disks, [str_upper(sf(r, 1)), nf(r, 2)]) }
        if tag == "l" { bind bv = pad_to(nums(r, 1), BV_LEN) }
        if tag == "k" {
            bind c = floor(nf(r, 1))
            if c >= 0 {
                if c < nc {
                    bind cu = list_set(cu, c, nf(r, 2))
                    bind cf = list_set(cf, c, nf(r, 3))
                }
            }
        }
        if tag == "s" { bind dio = list_push(dio, [str_upper(sf(r, 1)), nf(r, 2), nf(r, 3), nf(r, 4)]) }
        if tag == "h" { bind sens = list_push(sens, [sf(r, 1), str_upper(sf(r, 2)), nf(r, 3)]) }
        bind i = i + 1
    }
    bind good = 0
    if len(bv) >= BV_LEN {
        if nc > 0 { bind good = 1 }
    }
    return [good, info, bv, cu, cf, dimms, disks, dio, sens, snap_time(rows)]
}

fn sim_board(t) {
    bind u = clamp(30.0 + 25.0 * sin(t * 0.3) + 35.0 * pow(abs(sin(t * 0.11)), 5.0), 2.0, 100.0)
    bind info = ["SIMCORP", "HATCH SIM Z999", "SIM BIOS", "1.0", "2030-01-01", "SIM CORE 16", 8, 16, 4800, 1, 4, 128]
    bind bv = [u, 2800.0 + 2000.0 * u / 100.0, 100.0, 32.0 + 28.0 * u / 100.0, 18000.0 + 160000.0 * u / 100.0, 2000.0 + 30000.0 * pow(abs(sin(t * 0.4)), 4.0),
               90000.0 + 600000.0 * u / 100.0, 800000.0 + 6000000.0 * u / 100.0, 420.0, 5200.0, 86400.0 * 3.0 + t,
               1000000.0 * pow(abs(sin(t * 0.23)), 6.0) * 300.0, 1000000.0 * pow(abs(sin(t * 0.19 + 1.0)), 6.0) * 200.0,
               30.0 * pow(abs(sin(t * 0.2)), 3.0), 1.0]
    bind cu = []
    bind cf = []
    bind i = 0
    while i < 16 {
        bind ci = clamp(u + 45.0 * sin(t * 0.5 + i * 0.9) - 15.0, 0.0, 100.0)
        bind cu = list_push(cu, ci)
        bind cf = list_push(cf, 1200.0 + 3600.0 * ci / 100.0)
        bind i = i + 1
    }
    bind dimms = [["DIMM_A1", 16.0, 5600.0], ["DIMM_B1", 16.0, 5600.0]]
    bind disks = [["SIM NVME 1000", 1000.0], ["SIM SATA 2000", 2000.0]]
    bind dio = [["0 C:", bv[BV_DRD], bv[BV_DWR], bv[BV_DBUSY]], ["1 D:", 0.0, 0.0, 0.0]]
    return [1, info, bv, cu, cf, dimms, disks, dio, [], 0.0]
}

# ───────────────────────────── network ───────────────────────────────────────
# pack NP: [good, adapters, nv, nodes, procs, t]; adapter [name, rx, tx, bits/s, pkts in, pkts out, errors];
# node [hash 0..1, port, state 0 est / 1 syn, process]; proc [name, connections]
bind NP_AD = 1
bind NP_NV = 2
bind NP_NODE = 3
bind NP_PROC = 4

# remote port -> one of the seven classes (= the seven Ling hues)
fn port_class(p) {
    if p == 443 { return 0 }
    if p == 80 { return 1 }
    if p == 8080 { return 1 }
    if p == 8000 { return 1 }
    if p == 53 { return 2 }
    if p == 5353 { return 2 }
    if p == 22 { return 3 }
    if p == 3389 { return 3 }
    if p == 5900 { return 3 }
    if p == 25 { return 4 }
    if p == 110 { return 4 }
    if p == 143 { return 4 }
    if p == 465 { return 4 }
    if p == 587 { return 4 }
    if p == 993 { return 4 }
    if p == 995 { return 4 }
    if p >= 1024 { return 5 }
    return 6
}

bind PORT_NAME = ["HTTPS", "HTTP", "DNS", "REMOTE", "MAIL", "HIGH", "OTHER"]

fn parse_net(text) {
    bind rows = snap_rows(text)
    bind ads = []
    bind nodes = []
    bind procs = []
    bind kk = [0.0, 0.0, 0.0, 0.0, 0.0]
    bind cc = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
    bind rtt = 0.0 - 1.0
    bind i = 0
    while i < len(rows) {
        bind r = rows[i]
        bind tag = r[0]
        if tag == "i" { bind ads = list_push(ads, [sf(r, 1), nf(r, 2), nf(r, 3), nf(r, 4), nf(r, 5), nf(r, 6), nf(r, 7)]) }
        if tag == "k" { bind kk = pad_to(nums(r, 1), 5) }
        if tag == "r" { bind rtt = nf(r, 1) }
        if tag == "c" { bind cc = pad_to(nums(r, 1), 7) }
        if tag == "x" { bind procs = list_push(procs, [str_upper(sf(r, 1)), nf(r, 3)]) }
        if tag == "d" {
            bind st = 0
            if sf(r, 3) != "ESTABLISHED" { bind st = 1 }
            bind nodes = list_push(nodes, [nf(r, 1), nf(r, 2), st, str_upper(sf(r, 4))])
        }
        bind i = i + 1
    }
    bind rx = 0.0
    bind tx = 0.0
    bind prx = 0.0
    bind ptx = 0.0
    bind bw = 0.0
    bind a = 0
    while a < len(ads) {
        bind ad = ads[a]
        bind rx = rx + ad[1]
        bind tx = tx + ad[2]
        bind prx = prx + ad[4]
        bind ptx = ptx + ad[5]
        if a == 0 { bind bw = ad[3] }
        bind a = a + 1
    }
    bind nv = [rx, tx, prx, ptx, bw, kk[0], kk[1], kk[4], rtt, cc[1], cc[2], cc[3], cc[4], cc[6], kk[2]]
    bind good = 0
    if len(ads) > 0 { bind good = 1 }
    return [good, ads, nv, nodes, procs, snap_time(rows)]
}

fn sim_net(t) {
    bind b1 = pow(abs(sin(t * 0.21)), 5.0)
    bind b2 = pow(abs(sin(t * 0.33 + 1.0)), 6.0)
    bind rx = 40000.0 + 90000.0 * (1.0 + sin(t * 0.5)) + 40000000.0 * b1
    bind tx = 8000.0 + 20000.0 * (1.0 + sin(t * 0.4)) + 6000000.0 * b2
    bind ads = [["SIM ETHERNET", rx, tx, 1000000000.0, rx / 1200.0, tx / 900.0, 0.0]]
    bind nodes = []
    bind ports = [443, 443, 443, 80, 53, 22, 8333, 25565, 443, 993, 443, 3389, 8080, 51820]
    bind i = 0
    while i < 28 {
        bind nodes = list_push(nodes, [fract(i * 0.6180339 + 0.1), ports[i % 14], 0, str_upper(SIM_NAMES[(i * 7) % 20])])
        bind i = i + 1
    }
    bind procs = []
    bind i = 0
    while i < 8 {
        bind procs = list_push(procs, [str_upper(SIM_NAMES[i]), 12.0 - i])
        bind i = i + 1
    }
    bind nv = [rx, tx, rx / 1200.0, tx / 900.0, 1000000000.0, 90.0 + 20.0 * sin(t * 0.2), 2.0 + 30.0 * b2, 15.0 + 60.0 * b1, 8.0 + 10.0 * b2, 12.0, 6.0, 2.0, 0.0, 40.0, 3.0]
    return [1, ads, nv, nodes, procs, 0.0]
}

# ───────────────────────────── history sampling ──────────────────────────────
# push one 0..1 sample into every strip; GP, BP, NP are the smoothed packs
fn hs_sample(hs, GP, BP, NP, memUsed, memFlt) {
    bind g = 0
    while g < min(GP[GP_N], 8) {
        bind v = GP[GP_VEC][g]
        bind b = HS_GPU + g * 4
        bind hs = hs_push(hs, b, v[GV_UTIL] / 100.0)
        bind hs = hs_push(hs, b + 1, v[GV_TEMP] / 100.0)
        bind hs = hs_push(hs, b + 2, v[GV_POWER] / max(v[GV_PLIM], 1.0))
        bind hs = hs_push(hs, b + 3, v[GV_VUSED] / max(v[GV_VTOTAL], 1.0))
        bind g = g + 1
    }
    bind bv = BP[BP_BV]
    bind hs = hs_push(hs, HS_BRD, bv[BV_UTIL] / 100.0)
    bind hs = hs_push(hs, HS_BRD + 1, bv[BV_TEMP] / 100.0)
    bind hs = hs_push(hs, HS_BRD + 2, lg(bv[BV_INTR], 6.0))
    bind hs = hs_push(hs, HS_BRD + 3, lg(bv[BV_DPC], 5.0))
    bind hs = hs_push(hs, HS_BRD + 4, lg(bv[BV_CTX], 6.5))
    bind hs = hs_push(hs, HS_BRD + 5, lg(bv[BV_DRD], 9.0))
    bind hs = hs_push(hs, HS_BRD + 6, lg(bv[BV_DWR], 9.0))
    bind nv = NP[NP_NV]
    bind hs = hs_push(hs, HS_NET, lg(nv[NV_RX], 9.0))
    bind hs = hs_push(hs, HS_NET + 1, lg(nv[NV_TX], 9.0))
    bind hs = hs_push(hs, HS_NET + 2, lg(nv[NV_PRX], 6.0))
    bind hs = hs_push(hs, HS_NET + 3, lg(nv[NV_PTX], 6.0))
    bind hs = hs_push(hs, HS_NET + 4, lg(nv[NV_EST], 3.5))
    bind hs = hs_push(hs, HS_NET + 5, lg(nv[NV_UDP], 4.0))
    bind rt = 0.0
    if nv[NV_RTT] > 0.0 { bind rt = nv[NV_RTT] / 500.0 }
    bind hs = hs_push(hs, HS_NET + 6, rt)
    bind hs = hs_push(hs, HS_NET + 7, lg(nv[NV_RETX], 3.0))
    bind hs = hs_push(hs, HS_MEM, memUsed)
    bind hs = hs_push(hs, HS_MEM + 1, lg(memFlt, 6.0))
    return hs
}

# ───────────────────────────── data ──────────────────────────────────────────
# mem.snap, see sampler.ps1. Returns [good, g, r, private_all, names, ws, priv, ior, iow]
fn parse_snap(text) {
    bind g = []
    bind r = []
    bind pall = 0.0
    bind names = []
    bind ws = []
    bind priv = []
    bind ior = []
    bind iow = []
    bind lines = split(text, "\n")
    bind i = 0
    while i < len(lines) {
        bind f = split(trim(lines[i]), "|")
        bind tag = f[0]
        if tag == "g" {
            bind g = []
            bind j = 1
            while j < len(f) {
                bind g = list_push(g, to_number(f[j]))
                bind j = j + 1
            }
        }
        if tag == "r" {
            bind r = []
            bind j = 1
            while j < len(f) {
                bind r = list_push(r, to_number(f[j]))
                bind j = j + 1
            }
        }
        if tag == "t" { bind ts = to_number(f[1]) }
        if tag == "s" { bind pall = to_number(f[1]) }
        if tag == "p" {
            if len(f) >= 8 {
                bind names = list_push(names, f[1])
                bind ws = list_push(ws, to_number(f[3]))
                bind priv = list_push(priv, to_number(f[4]))
                bind ior = list_push(ior, to_number(f[5]))
                bind iow = list_push(iow, to_number(f[6]))
            }
        }
        bind i = i + 1
    }
    bind good = 0
    if len(g) >= 11 {
        if len(r) >= 8 { bind good = 1 }
    }
    return [good, g, r, pall, names, ws, priv, ior, iow, ts]
}

# the seven category sizes in MB, from the sampler's counters
fn categories(g, pall) {
    bind total = g[0]
    bind avail = g[1]
    bind free = g[2]
    bind standby = g[3]
    bind modified = g[4]
    bind kernel = g[5] + g[6]
    bind comp = g[7]
    bind inuse = max(total - avail - modified, 0.0)
    bind data = clamp(pall - comp, 0.0, max(inuse - kernel - comp, 0.0))
    bind mapped = max(inuse - kernel - comp - data, 0.0)
    return [kernel, standby, data, modified, mapped, comp, free]
}

# a made-up machine for when there is no sampler: 16 GB, breathing, with bursts of paging
fn sim_cats(t) {
    bind total = 16384.0
    bind kernel = 900.0 + 150.0 * sin(t * 0.13)
    bind standby = 4200.0 + 1600.0 * sin(t * 0.05 + 1.0)
    bind data = 5200.0 + 2200.0 * sin(t * 0.031 + 2.0) + 600.0 * sin(t * 0.21)
    bind modified = 130.0 + 110.0 * (sin(t * 0.4) + 1.0)
    bind comp = 700.0 + 300.0 * sin(t * 0.07)
    bind mapped = 1500.0 + 500.0 * sin(t * 0.09 + 3.0)
    bind free = max(total - kernel - standby - data - modified - comp - mapped, 200.0)
    return [kernel, standby, data, modified, mapped, comp, free]
}

fn sim_rates(t) {
    bind b1 = pow(abs(sin(t * 0.37)), 6.0)
    bind b2 = pow(abs(sin(t * 0.23 + 1.0)), 8.0)
    bind b3 = pow(abs(sin(t * 0.51 + 2.0)), 5.0)
    return [3000.0 + 2500.0 * sin(t * 0.7) + 40000.0 * b3, 40.0 + 4000.0 * b1, 20.0 + 3000.0 * b2]
}


# weighted pick: w is a list of weights, u in [0,1)
fn pick(w, u) {
    bind sum = 0.0
    bind i = 0
    while i < len(w) {
        bind sum = sum + w[i]
        bind i = i + 1
    }
    bind v = u * sum
    bind i = 0
    while i < len(w) - 1 {
        if v < w[i] { return i }
        bind v = v - w[i]
        bind i = i + 1
    }
    return len(w) - 1
}

# Hilbert curve table for a 32 x 32 page map: flat [x0, y0, x1, y1, ...]
fn hilbert_table(n) {
    bind out = []
    bind d = 0
    while d < n * n {
        bind t = d
        bind x = 0
        bind y = 0
        bind s = 1
        while s < n {
            bind rx = floor(t / 2) % 2
            bind ry = (t % 2 + rx) % 2
            if ry == 0 {
                if rx == 1 {
                    bind x = s - 1 - x
                    bind y = s - 1 - y
                }
                bind tmp = x
                bind x = y
                bind y = tmp
            }
            bind x = x + s * rx
            bind y = y + s * ry
            bind t = floor(t / 4)
            bind s = s * 2
        }
        bind out = list_push(out, x)
        bind out = list_push(out, y)
        bind d = d + 1
    }
    return out
}

# ───────────────────────────── pulses ────────────────────────────────────────
# EV is flat, six numbers per pulse: kind (0 read circle, 1 write square), category,
# born, seed, magnitude, process index.

fn ev_add(ev, kind, cat, born, seed, mag, proc) {
    bind ev = list_push(ev, kind)
    bind ev = list_push(ev, cat)
    bind ev = list_push(ev, born)
    bind ev = list_push(ev, seed)
    bind ev = list_push(ev, mag)
    bind ev = list_push(ev, proc)
    return ev
}

fn ev_cull(ev, t, life) {
    bind out = []
    bind i = 0
    while i < len(ev) {
        if t - ev[i + 2] < life {
            bind j = 0
            while j < 6 {
                bind out = list_push(out, ev[i + j])
                bind j = j + 1
            }
        }
        bind i = i + 6
    }
    return out
}

# ───────────────────────────── view 1: RINGS ─────────────────────────────────
fn ring_geom(c, R) {
    bind r1 = R * (1.0 - c * 0.1)
    bind band = R * 0.082
    return [r1, r1 - band, band]
}

fn ring_spin(Z, c) {
    bind dir = 1.0
    if c % 2 == 1 { bind dir = 0.0 - 1.0 }
    return Z[Z_ROT] * dir * (0.35 + 0.12 * c) + Z[Z_T] * dir * 0.03 * (c + 1)
}

fn view_rings(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W, H) * 0.5 - 16.0
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind c = 0
    while c < 7 {
        bind gm = ring_geom(c, R)
        bind r1 = gm[0]
        bind r0 = gm[1]
        bind band = gm[2]
        bind frac = cats[c] / total
        bind spin = ring_spin(Z, c)
        bind e = en[c]
        # the two rails
        col(c, sat * 0.7, 26.0 + 14.0 * e, hs)
        draw_circle(cx, cy, r1)
        draw_circle(cx, cy, r0)
        # crosshatch over the occupied sector, a second angle once the ring is busy
        bind sweep = max(frac * TAU, 0.03)
        bind da = 0.0215
        bind n = floor(sweep / da)
        bind k = 0
        col(c, sat, 36.0 + 26.0 * e, hs)
        while k < n {
            bind a = spin + k * da
            pline(cx, cy, r0, a, r1, a + 0.05 + 0.05 * e)
            bind k = k + 1
        }
        if e > 0.35 {
            bind k = 0
            col(c, sat, 30.0 + 22.0 * e, hs)
            while k < n {
                bind a = spin + k * da
                pline(cx, cy, r0, a + 0.05, r1, a)
                bind k = k + 2
            }
        }
        col(c, sat, 62.0 + 28.0 * e, hs)
        pline(cx, cy, r0 - 4.0, spin, r1 + 4.0, spin)
        pline(cx, cy, r0 - 4.0, spin + sweep, r1 + 4.0, spin + sweep)
        # ring text: name, size, share
        bind label = CAT_NAME[c] + " " + fmt_mb(cats[c]) + " " + num_str(round(frac * 1000.0) / 10.0) + "% "
        ring_text(label, cx, cy, r0 + band * 0.06, r1 - band * 0.06, spin * 0.6 + 1.0, 74.0 + 20.0 * e, c, sat, hs)
        bind c = c + 1
    }
    # hub: used share in Daemon, ticks turning
    bind hr = R * 0.3 - 6.0
    ink(34.0)
    draw_circle(cx, cy, hr)
    bind k = 0
    while k < 60 {
        bind a = k * TAU / 60.0 + Z[Z_ROT] * 0.5
        bind tl = 5.0
        if k % 5 == 0 { bind tl = 11.0 }
        pline(cx, cy, hr - tl, a, hr, a)
        bind k = k + 1
    }
    bind used = num_str(round(Z[Z_USED] * 100.0)) + "%"
    col(2, sat, 78.0, hs)
    daemon_text(used, cx - text_w(used, hr * 0.5) / 2.0, cy - hr * 0.08, hr * 0.5)
    ink(55.0)
    daemon_text("USED", cx - text_w("USED", hr * 0.2) / 2.0, cy + hr * 0.4, hr * 0.2)
    # pulses ride their ring
    bind ev = Z[Z_EV]
    bind i = 0
    while i < len(ev) {
        bind age = t - ev[i + 2]
        bind p = age / 1.8
        bind cc = ev[i + 1]
        bind gm = ring_geom(cc, R)
        bind rm = (gm[0] + gm[1]) / 2.0
        bind a = ring_spin(Z, cc) + ev[i + 3] * TAU
        bind x = px(cx, rm, a)
        bind y = py(cy, rm, a)
        col(cc, sat, (1.0 - p) * (55.0 + 40.0 * ev[i + 4]), hs)
        if ev[i] == 0 {
            draw_circle(x, y, gm[2] * (0.25 + 1.5 * p))
            draw_circle(x, y, gm[2] * (0.1 + 0.9 * p))
        } else {
            square(x, y, gm[2] * (1.1 - 0.8 * p), a + p * 2.0)
            square(x, y, gm[2] * (0.6 - 0.4 * p), a - p * 2.0)
        }
        bind i = i + 6
    }
}

# ───────────────────────────── view 2: TILES ─────────────────────────────────
fn cell_cat(i, cum) {
    bind k = 0
    while k < 6 {
        if i < cum[k] { return ORDER[k] }
        bind k = k + 1
    }
    return ORDER[6]
}

fn view_tiles(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind hil = Z[Z_HIL]
    bind S = min(H * 0.76, W * 0.5)
    bind cs = S / 32.0
    bind mx = W * 0.36
    bind my = H * 0.5
    bind tilt = 0.09 * sin(t * 0.07) + 0.07 * sin(Z[Z_ROT] * 0.8)
    bind ca = cos(tilt)
    bind sa = sin(tilt)
    # cumulative cell counts along the curve, in ORDER
    bind cum = []
    bind run = 0.0
    bind k = 0
    while k < 7 {
        bind run = run + cats[ORDER[k]] / total * 1024.0
        bind cum = list_push(cum, round(run))
        bind k = k + 1
    }
    bind i = 0
    while i < 1024 {
        bind c = cell_cat(i, cum)
        bind gx = hil[i * 2] - 15.5
        bind gy = hil[i * 2 + 1] - 15.5
        bind x = mx + (gx * ca - gy * sa) * cs
        bind y = my + (gx * sa + gy * ca) * cs
        bind hv = fract(sin(i * 12.9898) * 43758.5453)
        bind hv = fract(hv + 0.18 * sin(t * 0.6 + i * 0.37) + 1.0)
        bind e = en[c]
        bind h = cs * 0.46
        col(c, sat * 0.8, 20.0 + 16.0 * hv + 14.0 * e, hs + (hv - 0.5) * 10.0)
        square(x, y, h, tilt)
        if c != 6 {
            bind nl = 1 + floor(hv * 3.0 + e * 2.0)
            bind m = 0
            col(c, sat, 30.0 + 26.0 * hv + 22.0 * e, hs + (hv - 0.5) * 10.0)
            while m < nl {
                bind u = (m + 0.5) / nl
                bind s0 = (u * 2.0 - 1.0) * h
                bind an = tilt + ANG1[c]
                bind dx = cos(an) * h * 0.9
                bind dy = sin(an) * h * 0.9
                bind ox = (0.0 - sin(an)) * s0
                bind oy = cos(an) * s0
                draw_line(x + ox - dx, y + oy - dy, x + ox + dx, y + oy + dy)
                bind m = m + 1
            }
        } else {
            col(6, sat * 0.5, 18.0, hs)
            draw_pixel(x, y)
        }
        bind i = i + 1
    }
    ink(30.0)
    square(mx, my, S * 0.5 + cs * 0.9, tilt)
    # pulses: read = circles leaving a cell, write = squares closing on it
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind age = t - ev[j + 2]
        bind p = age / 1.8
        bind c = ev[j + 1]
        bind kk = 0
        while kk < 7 {
            if ORDER[kk] == c {
                bind lo = 0
                if kk > 0 { bind lo = cum[kk - 1] }
                bind span = max(cum[kk] - lo, 1.0)
                bind idx = min(floor(lo + ev[j + 3] * span), 1023)
                bind gx = hil[idx * 2] - 15.5
                bind gy = hil[idx * 2 + 1] - 15.5
                bind x = mx + (gx * ca - gy * sa) * cs
                bind y = my + (gx * sa + gy * ca) * cs
                col(c, sat, (1.0 - p) * (60.0 + 35.0 * ev[j + 4]), hs)
                if ev[j] == 0 {
                    draw_circle(x, y, cs * (0.5 + 3.5 * p))
                    draw_circle(x, y, cs * (0.3 + 1.6 * p))
                } else {
                    square(x, y, cs * (2.6 - 2.1 * p), tilt + p * 3.0)
                    square(x, y, cs * 0.5, tilt)
                    if p < 0.35 { draw_disc(x, y, cs * 0.35) }
                }
            }
            bind kk = kk + 1
        }
        bind j = j + 6
    }
    # legend: seven Daemon tiles
    bind lx = W * 0.68
    bind ts = min(H * 0.095, 62.0)
    bind c = 0
    while c < 7 {
        bind ly = H * 0.1 + c * (ts + H * 0.03)
        bind e = en[c]
        tile(str_slice(CAT_LET, c, 1), lx, ly, ts, 52.0 + 30.0 * e, c, sat, hs, 0.6 + e)
        col(c, sat, 68.0, hs)
        daemon_text(CAT_NAME[c], lx + ts + 14.0, ly + ts * 0.3, ts * 0.3)
        ink(62.0)
        daemon_text(fmt_mb(cats[c]) + " " + num_str(round(cats[c] / total * 1000.0) / 10.0) + "%", lx + ts + 14.0, ly + ts * 0.68, ts * 0.26)
        bind bw = W * 0.28
        bind bx = lx + ts + 14.0
        bind by = ly + ts * 0.95
        col(c, sat * 0.8, 40.0, hs)
        draw_line(bx, by, bx + bw, by)
        bind fw = bw * cats[c] / total
        col(c, sat, 60.0 + 20.0 * e, hs)
        bind u = 0.0
        while u < fw {
            draw_line(bx + u, by, bx + u + 4.0, by - 5.0)
            bind u = u + 3.0
        }
        bind c = c + 1
    }
}

# ───────────────────────────── view 3: ORBITS ────────────────────────────────
fn name_hue(name) {
    bind h = 0
    bind i = 0
    while i < str_len(name) {
        bind h = (h * 31 + str_find(DAEMON_CH, str_slice(name, i, 1)) + 1) % 7919
        bind i = i + 1
    }
    return h % 7
}

fn view_orbits(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W, H) * 0.5 - 20.0
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind names = Z[Z_NAME]
    bind ws = Z[Z_WS]
    bind pv = Z[Z_PRIV]
    bind ior = Z[Z_IOR]
    bind iow = Z[Z_IOW]
    bind np = min(min(len(names), len(ior)), 22)
    bind i = 0
    while i < np {
        ink(14.0)
        draw_circle(cx, cy, R * (0.24 + 0.74 * i / max(np - 1, 1)))
        bind i = i + 1
    }
    # the sun: memory in use
    bind used = num_str(round(Z[Z_USED] * 100.0)) + "%"
    col(2, sat, 70.0, hs)
    draw_circle(cx, cy, R * 0.12)
    draw_circle(cx, cy, R * 0.1)
    daemon_text(used, cx - text_w(used, R * 0.09) / 2.0, cy, R * 0.09)
    bind k = 0
    while k < 36 {
        bind a = k * TAU / 36.0 - Z[Z_ROT] * 0.7
        pline(cx, cy, R * 0.12, a, R * 0.12 + 5.0 + 4.0 * (k % 3), a)
        bind k = k + 1
    }
    # bodies: circle = working set, square = private bytes
    bind pos = []
    bind i = 0
    while i < np {
        bind orr = R * (0.24 + 0.74 * i / max(np - 1, 1))
        bind dir = 1.0
        if i % 2 == 1 { bind dir = 0.0 - 1.0 }
        bind io = clamp(log10(1.0 + ior[i] + iow[i]) / 4.0, 0.0, 1.0)
        bind a = i * 2.399 + Z[Z_ROT] * dir * (0.8 / sqrt(1.0 + i)) * (1.0 + 2.0 * io)
        bind x = px(cx, orr, a)
        bind y = py(cy, orr, a)
        bind pos = list_push(pos, x)
        bind pos = list_push(pos, y)
        bind k = name_hue(names[i])
        bind r = clamp(4.0 + sqrt(ws[i]) * 0.3, 5.0, R * 0.11)
        col(k, sat, 48.0 + 24.0 * io, hs)
        draw_circle(x, y, r)
        draw_circle(x, y, r * 0.86)
        col(k, sat, 32.0 + 22.0 * io, hs)
        hatch_disc(x, y, r * 0.84, ANG1[k] + Z[Z_ROT] * 0.5, max(2.6, r * 0.12))
        bind sh = r * 0.7 * sqrt(clamp(pv[i] / max(ws[i], 1.0), 0.05, 1.0))
        col(k, sat, 78.0, hs)
        square(x, y, sh, t * 0.6 * dir + i)
        if r > 15.0 {
            col(k, sat, 88.0, hs)
            daemon_glyph(str_upper(str_slice(names[i], 0, 1)), x, y, r * 0.5, r * 0.6, 0.0)
        }
        if r > 11.0 {
            bind nm = str_upper(str_slice(names[i], 0, 10))
            bind m = str_len(nm)
            bind rm2 = r + 9.0
            bind da = 11.0 / rm2
            bind q = 0
            col(k, sat * 0.9, 70.0, hs)
            while q < m {
                bind ch = str_slice(nm, q, 1)
                if ch != " " { chesilin_glyph(ch, x, y, rm2, (q - (m - 1) / 2.0) * da, 9.0, 8.0) }
                bind q = q + 1
            }
        }
        bind i = i + 1
    }
    # pulses leave their process
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind pidx = ev[j + 5]
        if pidx < np {
            bind p = (t - ev[j + 2]) / 1.8
            bind x = pos[pidx * 2]
            bind y = pos[pidx * 2 + 1]
            bind k = name_hue(names[pidx])
            bind r = clamp(4.0 + sqrt(ws[pidx]) * 0.3, 5.0, R * 0.11)
            col(k, sat, (1.0 - p) * (60.0 + 38.0 * ev[j + 4]), hs)
            if ev[j] == 0 {
                draw_circle(x, y, r * (1.0 + 1.1 * p))
                draw_circle(x, y, r * (1.0 + 0.5 * p))
            } else {
                square(x, y, r * (1.9 - 0.8 * p), p * 2.5)
                square(x, y, r * (1.4 - 0.6 * p), 0.0 - p * 2.5)
            }
        }
        bind j = j + 6
    }
}

# ───────────────────────────── view 4: WEAVE ─────────────────────────────────
# Seven strings, one per category, each a sine at that category's partial ratio and
# loudness: the picture of the additive synth. The bottom trace is their sum. Notes
# (every read and write) are born at the playhead and scroll left.

fn lane_y(c, H) { return H * 0.14 + c * H * 0.095 }

fn view_weave(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind x0 = W * 0.12
    bind x1 = W * 0.9
    bind xph = W * 0.86
    bind sw = 8.0
    bind amp = H * 0.042
    bind c = 0
    while c < 7 {
        bind y = lane_y(c, H)
        bind e = en[c]
        bind a = amp * (0.12 + 0.88 * sqrt(cats[c] / total)) * (0.7 + 0.5 * e)
        col(c, sat * 0.6, 14.0, hs)
        draw_line(x0, y, x1, y)
        col(c, sat, 40.0 + 36.0 * e, hs)
        bind x = x0
        bind prevy = y + a * sin(0.0 - t * RATIO[c] * 1.3)
        while x < x1 {
            bind u = (x + sw - x0) / (x1 - x0)
            bind ny = y + a * sin(u * TAU * RATIO[c] * 2.0 - t * RATIO[c] * 1.3)
            draw_line(x, prevy, x + sw, ny)
            bind prevy = ny
            bind x = x + sw
        }
        tile(str_slice(CAT_LET, c, 1), W * 0.03, y - H * 0.035, H * 0.07, 40.0 + 40.0 * e, c, sat, hs, 0.3 + e)
        col(c, sat, 40.0 + 40.0 * e, hs)
        bind dr = H * 0.033
        draw_circle(W * 0.095, y, dr)
        pline(W * 0.095, y, 0.0, 0.0, dr, t * RATIO[c] * 0.6 + c)
        bind c = c + 1
    }
    # the sum of the seven strings
    bind ys = H * 0.86
    ink(18.0)
    draw_line(x0, ys, x1, ys)
    col(2, sat * 0.4, 86.0, hs)
    bind x = x0
    bind prevy = ys
    bind first = 1
    while x < x1 {
        bind u = (x - x0) / (x1 - x0)
        bind s = 0.0
        bind k = 0
        while k < 7 {
            bind s = s + sqrt(cats[k] / total) * sin(u * TAU * RATIO[k] * 2.0 - t * RATIO[k] * 1.3)
            bind k = k + 1
        }
        bind ny = ys + s * H * 0.045
        if first == 0 { draw_line(x - 3.0, prevy, x, ny) }
        bind prevy = ny
        bind first = 0
        bind x = x + 3.0
    }
    ink(70.0)
    draw_line(xph, H * 0.05, xph, H * 0.95)
    draw_line(xph + 6.0, H * 0.05, xph + 6.0, H * 0.95)
    # notes: four numbers each, kind, category, born, magnitude
    bind hist = Z[Z_HIST]
    bind i = 0
    while i < len(hist) {
        bind age = t - hist[i + 2]
        bind x = xph - age * (xph - x0) / 14.0
        bind c = hist[i + 1]
        bind y = lane_y(c, H)
        bind r = H * 0.011 + H * 0.014 * hist[i + 3]
        bind lit = 78.0 - age * 4.0
        if age < 0.4 { bind lit = 96.0 }
        col(c, sat, lit, hs)
        if hist[i] == 0 {
            draw_circle(x, y, r)
            if age < 0.9 { draw_circle(x, y, r * (1.0 + age * 3.0)) }
        } else {
            square(x, y, r, age * 0.4)
            if age < 0.9 { square(x, y, r * (1.0 + age * 2.0), age * 2.0) }
        }
        bind i = i + 4
    }
}

# ───────────────────────────── synth ─────────────────────────────────────────
# 28 category partials (7 categories x 4 harmonics) + 4 shimmer partials = slots 0..31.
fn synth(Z, f0, level) {
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind rot = Z[Z_ROT]
    bind wr = log10(1.0 + Z[Z_WR]) / 4.0
    bind c = 0
    while c < 7 {
        bind w = 0.12 + 0.88 * sqrt(cats[c] / total)
        if c == 6 { bind w = w * 0.5 }
        bind ang = c * TAU / 7.0 + rot * 0.25
        bind h = 1
        while h <= 4 {
            bind a = level * 0.05 * w * pow(0.62, h - 1) * (0.55 + 0.9 * en[c])
            audio_tone(c * 4 + h - 1, 4.0 * cos(ang), 0.0, 4.0 * sin(ang) + 6.0, wr * 2.5, f0 * RATIO[c] * h, a, 0.09 + en[c] * 2.2 + h * 0.03, 0.0015 + 0.012 * en[c])
            bind h = h + 1
        }
        bind c = c + 1
    }
    # shimmer: upper partials that open with reads, writes, faults and overall activity
    bind sh = [Z[Z_RD], Z[Z_WR], Z[Z_FLT], Z[Z_ACT] * 1000.0]
    bind s = 0
    while s < 4 {
        bind lv = clamp(log10(1.0 + sh[s]) / 4.5, 0.0, 1.0)
        bind ang = s * TAU / 4.0 + rot * 0.6
        audio_tone(28 + s, 5.0 * cos(ang), 1.0, 5.0 * sin(ang) + 6.0, 1.0 + lv * 2.0, f0 * (12.0 + 4.0 * s + 0.25 * s * s), level * 0.012 * lv, 0.3 + lv * 5.0, 0.004 + 0.01 * lv)
        bind s = s + 1
    }
}

# one chime for a pulse. With pluck = 1 there is no ring: a noise click on the attack, then a short
# plucked note (triangle for in, square for out, so an out pulse is a harder tick).
fn chime(kind, cat, mag, f0, seed, pluck) {
    bind oct = 4.0
    if seed > 0.5 { bind oct = 8.0 }
    if pluck == 1 {
        bind ang = seed * TAU
        bind px_ = 4.0 * cos(ang)
        bind pz_ = 4.0 * sin(ang) + 6.0
        audio_sfx(px_, 0.0, pz_, 1.0 + mag, 3000.0 + 2500.0 * seed, 0.05 * (0.4 + 0.6 * mag), 0.012, "noise")
        bind wv = "tri"
        bind dd = 0.11
        if kind == 1 {
            bind wv = "square"
            bind dd = 0.045
        }
        audio_sfx(px_, 0.0, pz_, 1.0 + mag, f0 * RATIO[cat] * oct * 2.0, 0.045 * (0.4 + 0.6 * mag), dd, wv)
        return 0
    }
    bind wave = "sine"
    bind dur = 1.1
    if kind == 1 {
        bind wave = "tri"
        bind dur = 0.6
    }
    bind ang = seed * TAU
    audio_sfx(4.0 * cos(ang), 0.0, 4.0 * sin(ang) + 6.0, 1.0 + mag, f0 * RATIO[cat] * oct, 0.07 * (0.4 + 0.6 * mag), dur, wave)
}

# ───────────────────────────── shared views ──────────────────────────────────
# Used by every section: a stack of Chesilin rings (any number of rings) and a scrolling
# seven-lane scope. The Memory views in 40_memory.ling keep their own copies of this.

bind D_HT = 6

fn fit_h(str, maxw, h) { return min(h, maxw / (max(str_len(str), 1) * 0.74)) }

fn ring_geom2(c, R, nr) {
    bind step = 0.7 / nr
    bind r1 = R * (1.0 - c * step)
    bind band = R * step * 0.82
    return [r1, r1 - band, band]
}

# nr rings, one per channel: fr[c] is how much of the ring is crosshatched, labels[c] runs round it.
# The hub shows `big` (Daemon) and `small`. Pulses (Z_EV, category = ring) ride the rings.
fn ring_stack(Z, nr, hm, labels, fr, big, small, bigk) {
    ring_stack_at(Z, Z[Z_W] / 2.0, Z[Z_H] / 2.0, min(Z[Z_W], Z[Z_H]) * 0.5 - 16.0, nr, hm, labels, fr, big, small, bigk)
}

# the same, centred on (cx, cy) with outer radius R
fn ring_stack_at(Z, cx, cy, R, nr, hm, labels, fr, big, small, bigk) {
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind en = Z[Z_ENERGY]
    bind c = 0
    while c < nr {
        bind gm = ring_geom2(c, R, nr)
        bind r1 = gm[0]
        bind r0 = gm[1]
        bind band = gm[2]
        bind frac = clamp(fr[c], 0.0, 1.0)
        bind spin = ring_spin(Z, c)
        bind e = en[c]
        col(hm[c], sat * 0.7, 26.0 + 14.0 * e, hs)
        draw_circle(cx, cy, r1)
        draw_circle(cx, cy, r0)
        bind sweep = max(frac * TAU, 0.03)
        bind da = 0.0215
        bind n = floor(sweep / da)
        bind k = 0
        col(hm[c], sat, 36.0 + 26.0 * e, hs)
        while k < n {
            bind a = spin + k * da
            pline(cx, cy, r0, a, r1, a + 0.05 + 0.05 * e)
            bind k = k + 1
        }
        if e > 0.35 {
            bind k = 0
            col(hm[c], sat, 30.0 + 22.0 * e, hs)
            while k < n {
                bind a = spin + k * da
                pline(cx, cy, r0, a + 0.05, r1, a)
                bind k = k + 2
            }
        }
        col(hm[c], sat, 62.0 + 28.0 * e, hs)
        pline(cx, cy, r0 - 4.0, spin, r1 + 4.0, spin)
        pline(cx, cy, r0 - 4.0, spin + sweep, r1 + 4.0, spin + sweep)
        ring_text(labels[c], cx, cy, r0 + band * 0.06, r1 - band * 0.06, spin * 0.6 + 1.0, 74.0 + 20.0 * e, hm[c], sat, hs)
        bind c = c + 1
    }
    bind hr = R * 0.3 - 6.0
    ink(34.0)
    draw_circle(cx, cy, hr)
    bind k = 0
    while k < 60 {
        bind a = k * TAU / 60.0 + Z[Z_ROT] * 0.5
        bind tl = 5.0
        if k % 5 == 0 { bind tl = 11.0 }
        pline(cx, cy, hr - tl, a, hr, a)
        bind k = k + 1
    }
    bind bh = fit_h(big, hr * 1.5, hr * 0.5)
    col(bigk, sat, 78.0, hs)
    daemon_text(big, cx - text_w(big, bh) / 2.0, cy - hr * 0.08, bh)
    bind sh = fit_h(small, hr * 1.5, hr * 0.2)
    ink(55.0)
    daemon_text(small, cx - text_w(small, sh) / 2.0, cy + hr * 0.4, sh)
    bind ev = Z[Z_EV]
    bind i = 0
    while i < len(ev) {
        bind cc = ev[i + 1]
        if cc < nr {
            bind age = t - ev[i + 2]
            bind p = age / 1.8
            bind gm = ring_geom2(cc, R, nr)
            bind rm = (gm[0] + gm[1]) / 2.0
            bind a = ring_spin(Z, cc) + ev[i + 3] * TAU
            bind x = px(cx, rm, a)
            bind y = py(cy, rm, a)
            col(hm[cc], sat, (1.0 - p) * (55.0 + 40.0 * ev[i + 4]), hs)
            if ev[i] == 0 {
                draw_circle(x, y, gm[2] * (0.25 + 1.5 * p))
                draw_circle(x, y, gm[2] * (0.1 + 0.9 * p))
            } else {
                square(x, y, gm[2] * (1.1 - 0.8 * p), a + p * 2.0)
                square(x, y, gm[2] * (0.6 - 0.4 * p), a - p * 2.0)
            }
        }
        bind i = i + 6
    }
}

# seven lanes, each a Daemon tile, a name, a current value and the last minute as a trace.
# ids are history strip numbers (hs_push), vals are the current readings as text.
# Notes (every pulse) are born at the playhead and scroll left at the trace's speed.
fn scope(Z, titles, letters, ids, vals) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind en = Z[Z_ENERGY]
    bind D = Z[Z_D]
    bind hst = D[D_HS]
    bind x0 = W * 0.22
    bind xph = W * 0.9
    bind dx = (xph - x0) / (HS_N - 1.0)
    bind sh = clamp((t - D[D_HT]) / 0.5, 0.0, 1.0) * dx
    bind amp = H * 0.05
    bind ts = H * 0.075
    bind c = 0
    while c < 7 {
        bind y = H * 0.15 + c * H * 0.118
        bind e = en[c]
        bind yb = y + amp
        bind ser = hst[ids[c]]
        col(c, sat * 0.6, 14.0, hs)
        draw_line(x0, yb, xph, yb)
        draw_line(x0, yb - amp * 2.0, xph, yb - amp * 2.0)
        bind k = 0
        bind ox = x0 - sh
        bind oy = yb - ser[0] * amp * 2.0
        while k < HS_N - 1 {
            bind nx = x0 + (k + 1) * dx - sh
            bind ny = yb - ser[k + 1] * amp * 2.0
            if ox >= x0 - 0.5 {
                col(c, sat * 0.8, 20.0 + 18.0 * ser[k + 1], hs)
                draw_line(nx, yb, nx, ny)
                col(c, sat, 44.0 + 36.0 * e, hs)
                draw_line(ox, oy, nx, ny)
            }
            bind ox = nx
            bind oy = ny
            bind k = k + 1
        }
        col(c, sat, 60.0 + 30.0 * e, hs)
        draw_line(ox, oy, xph, oy)
        tile(str_slice(letters, c, 1), W * 0.02, y - ts * 0.5, ts, 40.0 + 40.0 * e, c, sat, hs, 0.3 + e)
        col(c, sat, 68.0 + 20.0 * e, hs)
        daemon_text(titles[c], W * 0.02 + ts + 12.0, y - ts * 0.14, ts * 0.3)
        ink(62.0)
        daemon_text(vals[c], W * 0.02 + ts + 12.0, y + ts * 0.3, ts * 0.3)
        bind c = c + 1
    }
    ink(70.0)
    draw_line(xph, H * 0.04, xph, H * 0.97)
    draw_line(xph + 6.0, H * 0.04, xph + 6.0, H * 0.97)
    bind hist = Z[Z_HIST]
    bind i = 0
    while i < len(hist) {
        bind age = t - hist[i + 2]
        bind x = xph - age * dx * 2.0
        bind c = hist[i + 1]
        if c < 7 {
            if x > x0 {
                bind y = H * 0.15 + c * H * 0.118
                bind r = H * 0.009 + H * 0.012 * hist[i + 3]
                bind lit = 78.0 - age * 4.0
                if age < 0.4 { bind lit = 96.0 }
                col(c, sat, lit, hs)
                if hist[i] == 0 {
                    draw_circle(x, y, r)
                    if age < 0.9 { draw_circle(x, y, r * (1.0 + age * 3.0)) }
                } else {
                    square(x, y, r, age * 0.4)
                    if age < 0.9 { square(x, y, r * (1.0 + age * 2.0), age * 2.0) }
                }
            }
        }
        bind i = i + 4
    }
}

# the last n samples of a strip in a w x h box with its top-left at (x, y)
fn spark(x, y, w, h, ser, n, k, sat, lit, hs) {
    bind s0 = len(ser) - n
    bind dx = w / (n - 1.0)
    col(k, sat * 0.6, 18.0, hs)
    draw_line(x, y + h, x + w, y + h)
    bind i = 0
    while i < n - 1 {
        bind a = ser[s0 + i]
        bind b = ser[s0 + i + 1]
        col(k, sat * 0.8, 22.0 + 14.0 * b, hs)
        draw_line(x + (i + 1) * dx, y + h, x + (i + 1) * dx, y + h - b * h)
        col(k, sat, lit, hs)
        draw_line(x + i * dx, y + h - a * h, x + (i + 1) * dx, y + h - b * h)
        bind i = i + 1
    }
}

# a hatched bar: rail, then diagonal strokes for the filled part
fn hbar(x, y, w, frac, k, sat, lit, hs) {
    col(k, sat * 0.8, 38.0, hs)
    draw_line(x, y, x + w, y)
    draw_line(x, y - 6.0, x, y)
    draw_line(x + w, y - 6.0, x + w, y)
    bind fw = w * clamp(frac, 0.0, 1.0)
    col(k, sat, lit, hs)
    bind u = 0.0
    while u < fw {
        draw_line(x + u, y, x + u + 4.0, y - 6.0)
        bind u = u + 3.0
    }
}

fn bline(x0, y0, S, u0, v0, u1, v1) {
    draw_line(x0 + u0 * S, y0 + v0 * S, x0 + u1 * S, y0 + v1 * S)
}

# an axis-aligned rectangle in board units
fn brect(x0, y0, S, u0, v0, u1, v1) {
    bline(x0, y0, S, u0, v0, u1, v0)
    bline(x0, y0, S, u1, v0, u1, v1)
    bline(x0, y0, S, u1, v1, u0, v1)
    bline(x0, y0, S, u0, v1, u0, v0)
}

# diagonal hatch inside a board-unit rectangle; spacing in pixels
fn bhatch(x0, y0, S, u0, v0, u1, v1, sp) {
    bind w = (u1 - u0) * S
    bind h = (v1 - v0) * S
    bind xa = x0 + u0 * S
    bind ya = y0 + v0 * S
    bind d = sp
    while d < w + h {
        bind ax = xa
        bind ay = ya + d
        if d > h {
            bind ax = xa + d - h
            bind ay = ya + h
        }
        bind bx = xa + d
        bind by = ya
        if d > w {
            bind bx = xa + w
            bind by = ya + d - w
        }
        draw_line(ax, ay, bx, by)
        bind d = d + sp
    }
}

# ───────────────────────────── GPU section ───────────────────────────────────
# Channels (rings, lanes, pulse categories): 3D, COMPUTE, COPY, DECODE, ENCODE, VRAM, POWER.
# Data: gpu.snap from sampler_hw.ps1 (nvidia-smi for the card, Windows GPU counters for engines and processes).

fn gpu_sel_vec(Z) {
    bind gp = Z[Z_D][D_GPU]
    return gp[GP_VEC][gp[GP_SEL]]
}

# ---- view 1: ENGINES, seven rings
fn view_gpu_engines(Z) {
    bind gp = Z[Z_D][D_GPU]
    bind g = gp[GP_SEL]
    bind v = gp[GP_VEC][g]
    bind e = gp[GP_ENG][g]
    bind labels = ["3D " + pct(e[0]) + " ", "COMPUTE " + pct(e[1]) + " ", "COPY " + pct(e[2]) + " ", "DECODE " + pct(e[3]) + " ",
                   "ENCODE " + pct(e[4]) + " ", "VRAM " + fmt_mb(v[GV_VUSED]) + " OF " + fmt_mb(v[GV_VTOTAL]) + " ",
                   "POWER " + pct(v[GV_POWER]) + "W OF " + pct(v[GV_PLIM]) + " "]
    ring_stack(Z, 7, IDM, labels, Z[Z_FRAC], pct(v[GV_UTIL]) + "%", short_gpu(gp[GP_NAME][g]) + " " + pct(v[GV_TEMP]) + "C", 3)
}

# ---- view 2: VRAM, a 32 x 32 page map along a Hilbert curve; each cell is 1/1024 of the card's memory
# The six biggest processes get a hue each, everything else (and what Windows does not attribute) is violet.
fn gpu_segments(pd, vused, vtotal) {
    bind all = 0.0
    bind i = 0
    while i < len(pd) {
        bind all = all + pd[i]
        bind i = i + 1
    }
    bind scale = 1.0
    if all > vused {
        if all > 0.0 { bind scale = vused / all }
    }
    bind sizes = []
    bind top = 0.0
    bind i = 0
    while i < 6 {
        bind s = 0.0
        if i < len(pd) { bind s = pd[i] * scale }
        bind sizes = list_push(sizes, s)
        bind top = top + s
        bind i = i + 1
    }
    bind sizes = list_push(sizes, max(vused - top, 0.0))
    bind cum = []
    bind run = 0.0
    bind i = 0
    while i < 7 {
        bind run = run + sizes[i] / max(vtotal, 1.0) * 1024.0
        bind cum = list_push(cum, min(round(run), 1024))
        bind i = i + 1
    }
    return [cum, sizes]
}

fn gcell(i, cum) {
    bind k = 0
    while k < 7 {
        if i < cum[k] { return k }
        bind k = k + 1
    }
    return 7
}

fn view_gpu_vram(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind hil = Z[Z_HIL]
    bind gp = Z[Z_D][D_GPU]
    bind g = gp[GP_SEL]
    bind v = gp[GP_VEC][g]
    bind pr = gp[GP_PROC][g]
    bind pn = pr[0]
    bind pu = pr[3]
    bind vt = max(v[GV_VTOTAL], v[GV_VUSED] * 1.02 + 1.0)
    bind seg = gpu_segments(pr[1], v[GV_VUSED], vt)
    bind cum = seg[0]
    bind sizes = seg[1]
    bind pe = []
    bind k = 0
    while k < 7 {
        bind u = v[GV_UTIL] / 100.0
        if k < 6 {
            if k < len(pu) { bind u = pu[k] / 40.0 } else { bind u = 0.0 }
        }
        bind pe = list_push(pe, clamp(u, 0.0, 1.0))
        bind k = k + 1
    }
    bind S = min(H * 0.76, W * 0.5)
    bind cs = S / 32.0
    bind mx = W * 0.36
    bind my = H * 0.5
    bind tilt = 0.09 * sin(t * 0.07) + 0.07 * sin(Z[Z_ROT] * 0.8)
    bind ca = cos(tilt)
    bind sa = sin(tilt)
    bind i = 0
    while i < 1024 {
        bind c = gcell(i, cum)
        bind gx = hil[i * 2] - 15.5
        bind gy = hil[i * 2 + 1] - 15.5
        bind x = mx + (gx * ca - gy * sa) * cs
        bind y = my + (gx * sa + gy * ca) * cs
        bind hv = fract(sin(i * 12.9898) * 43758.5453)
        bind hv = fract(hv + 0.18 * sin(t * 0.6 + i * 0.37) + 1.0)
        bind h = cs * 0.46
        if c == 7 {
            ink(9.0 + 6.0 * hv)
            square(x, y, h, tilt)
            ink(20.0)
            draw_pixel(x, y)
        } else {
            bind e = pe[c]
            col(c, sat * 0.8, 20.0 + 16.0 * hv + 14.0 * e, hs + (hv - 0.5) * 10.0)
            square(x, y, h, tilt)
            bind nl = 1 + floor(hv * 3.0 + e * 2.0)
            bind m = 0
            col(c, sat, 30.0 + 26.0 * hv + 22.0 * e, hs + (hv - 0.5) * 10.0)
            while m < nl {
                bind u = (m + 0.5) / nl
                bind s0 = (u * 2.0 - 1.0) * h
                bind an = tilt + ANG1[c]
                bind dx = cos(an) * h * 0.9
                bind dy = sin(an) * h * 0.9
                bind ox = (0.0 - sin(an)) * s0
                bind oy = cos(an) * s0
                draw_line(x + ox - dx, y + oy - dy, x + ox + dx, y + oy + dy)
                bind m = m + 1
            }
        }
        bind i = i + 1
    }
    ink(30.0)
    square(mx, my, S * 0.5 + cs * 0.9, tilt)
    # pulses leave the cells of the process that did the work
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind p = (t - ev[j + 2]) / 1.8
        bind sg = min(ev[j + 5], 6)
        bind lo = 0
        if sg > 0 { bind lo = cum[sg - 1] }
        bind span = max(cum[sg] - lo, 1.0)
        bind idx = min(floor(lo + ev[j + 3] * span), 1023)
        bind gx = hil[idx * 2] - 15.5
        bind gy = hil[idx * 2 + 1] - 15.5
        bind x = mx + (gx * ca - gy * sa) * cs
        bind y = my + (gx * sa + gy * ca) * cs
        col(sg, sat, (1.0 - p) * (60.0 + 35.0 * ev[j + 4]), hs)
        if ev[j] == 0 {
            draw_circle(x, y, cs * (0.5 + 3.5 * p))
            draw_circle(x, y, cs * (0.3 + 1.6 * p))
        } else {
            square(x, y, cs * (2.6 - 2.1 * p), tilt + p * 3.0)
            square(x, y, cs * 0.5, tilt)
            if p < 0.35 { draw_disc(x, y, cs * 0.35) }
        }
        bind j = j + 6
    }
    # legend
    bind lx = W * 0.68
    bind ts = min(H * 0.095, 62.0)
    bind c = 0
    while c < 7 {
        bind ly = H * 0.1 + c * (ts + H * 0.03)
        bind e = pe[c]
        bind nm = "OTHER"
        bind lt = "O"
        if c < 6 {
            bind nm = "-"
            bind lt = "-"
            if c < len(pn) {
                bind nm = pn[c]
                bind lt = str_slice(pn[c], 0, 1)
            }
        }
        tile(lt, lx, ly, ts, 52.0 + 30.0 * e, c, sat, hs, 0.6 + e)
        col(c, sat, 68.0, hs)
        daemon_text(str_slice(nm, 0, 16), lx + ts + 14.0, ly + ts * 0.3, ts * 0.3)
        ink(62.0)
        daemon_text(fmt_mb(sizes[c]) + " " + num_str(round(sizes[c] / vt * 1000.0) / 10.0) + "%", lx + ts + 14.0, ly + ts * 0.68, ts * 0.26)
        hbar(lx + ts + 14.0, ly + ts * 0.95, W * 0.28, sizes[c] / vt, c, sat, 60.0 + 20.0 * e, hs)
        bind c = c + 1
    }
    ink(55.0)
    daemon_text("FREE " + fmt_mb(max(vt - v[GV_VUSED], 0.0)) + " OF " + fmt_mb(vt), lx, H * 0.1 + 7.0 * (ts + H * 0.03) + ts * 0.2, ts * 0.26)
}

# ---- view 3: DIALS, four gauges and a minute of history under each
fn dial(cx, cy, r, frac, k, sat, hs, big, small, e, t) {
    bind a0 = 0.0 - 2.356
    bind sweep = 4.712
    bind f = clamp(frac, 0.0, 1.0)
    col(k, sat * 0.7, 26.0 + 14.0 * e, hs)
    draw_circle(cx, cy, r)
    draw_circle(cx, cy, r * 0.78)
    bind i = 0
    while i <= 40 {
        bind a = a0 + sweep * i / 40.0
        bind tl = 0.05
        if i % 5 == 0 { bind tl = 0.12 }
        if i / 40.0 <= f { col(k, sat, 62.0 + 28.0 * e, hs) } else { ink(22.0) }
        pline(cx, cy, r * (1.0 - tl), a, r * 1.02, a)
        bind i = i + 1
    }
    col(k, sat, 36.0 + 26.0 * e, hs)
    bind n = floor(sweep * f / 0.03)
    bind j = 0
    while j < n {
        bind a = a0 + j * 0.03
        pline(cx, cy, r * 0.8, a, r * 0.94, a + 0.07)
        bind j = j + 1
    }
    bind an = a0 + sweep * f
    col(k, sat, 80.0, hs)
    pline(cx, cy, r * 0.12, an, r * 0.74, an)
    square(px(cx, r * 0.74, an), py(cy, r * 0.74, an), r * 0.04, an + t * 0.5)
    draw_circle(cx, cy, r * 0.07)
    bind bh = fit_h(big, r * 1.0, r * 0.3)
    col(k, sat, 78.0, hs)
    daemon_text(big, cx - text_w(big, bh) / 2.0, cy + r * 0.34, bh)
    bind sh = fit_h(small, r * 1.0, r * 0.14)
    ink(55.0)
    daemon_text(small, cx - text_w(small, sh) / 2.0, cy + r * 0.62, sh)
}

fn view_gpu_dials(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind en = Z[Z_ENERGY]
    bind D = Z[Z_D]
    bind gp = D[D_GPU]
    bind g = gp[GP_SEL]
    bind v = gp[GP_VEC][g]
    bind hst = D[D_HS]
    bind r = min(W * 0.105, H * 0.2)
    bind cy = H * 0.3
    bind fr = [v[GV_UTIL] / 100.0, v[GV_TEMP] / 100.0, v[GV_POWER] / max(v[GV_PLIM], 1.0), v[GV_CLK] / max(v[GV_CLKMAX], 1.0)]
    bind bigs = [pct(v[GV_UTIL]) + "%", pct(v[GV_TEMP]) + "C", pct(v[GV_POWER]) + "W", pct(v[GV_CLK])]
    bind smalls = ["UTILISATION", "TEMPERATURE", "POWER OF " + pct(v[GV_PLIM]) + "W", "CORE MHZ OF " + pct(v[GV_CLKMAX])]
    bind hk = [3, 0, 2, 4]
    bind sid = [0, 1, 2, 3]
    bind d = 0
    while d < 4 {
        bind cx = W * (0.14 + 0.24 * d)
        bind e = clamp(fr[d] + 0.15, 0.0, 1.0)
        dial(cx, cy, r, fr[d], hk[d], sat, hs, bigs[d], smalls[d], e, t)
        bind ser = hst[HS_GPU + g * 4 + sid[d]]
        spark(cx - r, H * 0.57, r * 2.0, H * 0.17, ser, 100, hk[d], sat, 64.0, hs)
        ink(46.0)
        daemon_text("LAST 50 S", cx - r, H * 0.55, 11.0)
        bind d = d + 1
    }
    # the rest of the card's vitals
    bind thr = "NONE"
    if v[GV_THP] > 0.5 { bind thr = "POWER LIMIT" }
    if v[GV_THT] > 0.5 { bind thr = "THERMAL" }
    bind lines = ["MEMORY CLOCK " + pct(v[GV_MCLK]) + " OF " + pct(v[GV_MCLKMAX]) + " MHZ     FAN " + pct(v[GV_FAN]) + "%     MEMORY BUS " + pct(v[GV_MUTIL]) + "%",
                  "PCIE GEN " + pct(v[GV_PGEN]) + " X" + pct(v[GV_PW]) + "     THROTTLE " + thr + "     DRIVER " + gp[GP_DRV][g]]
    bind i = 0
    while i < 2 {
        ink(66.0 - 10.0 * i)
        daemon_text(lines[i], W * 0.04, H * 0.83 + i * H * 0.065, 14.0)
        bind i = i + 1
    }
}

# ---- view 4: FLEET, every GPU at once. Click a card (or press Up / Down) to pick the one the other views show.
# [x, y, w, h] of GPU i's card out of n
fn fleet_rect(i, n, W, H) {
    bind cols = min(n, 4)
    bind rows = ceil(n / cols)
    bind gap = 14.0
    bind cw = (W - 36.0 - (cols - 1) * gap) / cols
    bind ch = (H - 82.0 - (rows - 1) * gap) / rows
    return [18.0 + (i % cols) * (cw + gap), 70.0 + floor(i / cols) * (ch + gap), cw, ch]
}

fn fleet_hit(mx, my, W, H, n) {
    bind i = 0
    while i < n {
        bind r = fleet_rect(i, n, W, H)
        if inside(mx, my, r) == 1 { return i }
        bind i = i + 1
    }
    return 0 - 1
}

bind FLEET_HUE = [3, 4, 1, 6, 0, 2, 5, 3]

fn view_gpu_fleet(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind D = Z[Z_D]
    bind gp = D[D_GPU]
    bind hst = D[D_HS]
    bind n = gp[GP_N]
    bind sel = gp[GP_SEL]
    bind hov = fleet_hit(Z[Z_MX], Z[Z_MY], W, H, n)
    bind rows = ceil(n / min(n, 4))
    bind g = 0
    while g < n {
        bind v = gp[GP_VEC][g]
        bind r = fleet_rect(g, n, W, H)
        bind x = r[0]
        bind y = r[1]
        bind cw = r[2]
        bind ch = r[3]
        bind k = FLEET_HUE[g % 8]
        bind on = 0
        if g == sel { bind on = 1 }
        bind lit = 30.0 + 40.0 * on
        if g == hov { bind lit = lit + 14.0 }
        col(k, sat * 0.7, lit, hs)
        brect(x, y, 1.0, 0.0, 0.0, cw, ch)
        if on == 1 {
            bind a = 14.0
            col(k, sat, 84.0, hs)
            bline(x, y, 1.0, 0.0, a, 0.0, 0.0)
            bline(x, y, 1.0, 0.0, 0.0, a, 0.0)
            bline(x, y, 1.0, cw - a, 0.0, cw, 0.0)
            bline(x, y, 1.0, cw, 0.0, cw, a)
            bline(x, y, 1.0, cw, ch - a, cw, ch)
            bline(x, y, 1.0, cw, ch, cw - a, ch)
            bline(x, y, 1.0, a, ch, 0.0, ch)
            bline(x, y, 1.0, 0.0, ch, 0.0, ch - a)
        }
        tile(num_str(g), x + 10.0, y + 10.0, 38.0, 54.0 + 28.0 * on, k, sat, hs, 0.5 + on)
        col(k, sat, 78.0 + 12.0 * on, hs)
        daemon_text(str_slice(gp[GP_NAME][g], 0, 22), x + 58.0, y + 24.0, min(14.0, (cw - 70.0) / 16.0))
        ink(52.0)
        bind link = "GEN" + pct(v[GV_PGEN]) + " X" + pct(v[GV_PW])
        if v[GV_PW] < 1.0 { bind link = "NO NVIDIA DATA" }
        daemon_text(link, x + 58.0, y + 44.0, 10.0)
        bind u = clamp(v[GV_UTIL] / 100.0, 0.0, 1.0)
        bind dr = min(cw * 0.28, (ch - 52.0) * 0.26)
        bind dcy = y + 58.0 + dr
        dial(x + cw / 2.0, dcy, dr, u, k, sat, hs, pct(v[GV_UTIL]) + "%", "UTIL", clamp(u + 0.15, 0.0, 1.0), t)
        bind vt = max(v[GV_VTOTAL], v[GV_VUSED] * 1.02 + 1.0)
        bind by0 = dcy + dr + 24.0
        bind sp = 36.0
        if rows > 1 { bind sp = 30.0 }
        bind bw = cw - 24.0
        bind tx = pct(v[GV_TEMP]) + "C"
        if v[GV_FAN] > 0.5 { bind tx = tx + "  FAN " + pct(v[GV_FAN]) + "%" }
        bind lab = ["VRAM " + fmt_mb(v[GV_VUSED]) + " OF " + fmt_mb(vt), "POWER " + pct(v[GV_POWER]) + "W OF " + pct(v[GV_PLIM]), "TEMP " + tx]
        bind fr = [v[GV_VUSED] / vt, v[GV_POWER] / max(v[GV_PLIM], 1.0), v[GV_TEMP] / 100.0]
        bind bk = [5, 2, 0]
        bind q = 0
        while q < 3 {
            ink(62.0)
            daemon_text(lab[q], x + 12.0, by0 + q * sp, 11.0)
            hbar(x + 12.0, by0 + q * sp + 13.0, bw, fr[q], bk[q], sat, 58.0, hs)
            bind q = q + 1
        }
        if rows == 1 {
            ink(46.0)
            daemon_text("UTILISATION, LAST 50 S", x + 12.0, y + ch - 66.0, 10.0)
            spark(x + 12.0, y + ch - 56.0, bw, 44.0, hst[HS_GPU + g * 4], 100, k, sat, 64.0, hs)
        }
        bind g = g + 1
    }
}

# ───────────────────────────── BOARD section ─────────────────────────────────
# Channels (rings, lanes, pulse categories): CORES, MEMBUS, PCIE, CHIPSET, STORAGE, NETWORK, THERMAL.
# Data: board.snap from sampler_hw.ps1 (Windows performance counters and SMBIOS); GPUs and memory
# traffic come from the other samplers.

fn grid_cols(n) {
    bind c = floor(sqrt(n))
    if c * c < n { bind c = c + 1 }
    return max(c, 1)
}

# bus traces on the schematic, as flat [u, v, u, v, ...] in board units (0..1)
fn board_path(ch, variant) {
    if ch == 1 {
        if variant < 0.5 { return [0.435, 0.25, 0.62, 0.25] }
        return [0.435, 0.35, 0.62, 0.35]
    }
    if ch == 2 {
        if variant < 0.5 { return [0.33, 0.435, 0.33, 0.7] }
        return [0.37, 0.435, 0.37, 0.8]
    }
    if ch == 3 { return [0.4, 0.435, 0.4, 0.6, 0.68, 0.6] }
    if ch == 4 {
        if variant < 0.5 { return [0.18, 0.435, 0.18, 0.5] }
        return [0.84, 0.64, 0.93, 0.64]
    }
    if ch == 5 { return [0.76, 0.72, 0.76, 0.95, 0.035, 0.95, 0.035, 0.46] }
    return [0.3, 0.3, 0.3, 0.3]
}

# the point a fraction p of the way along a path: [u, v]
fn path_pt(path, p) {
    bind n = len(path) / 2 - 1
    bind total = 0.0
    bind i = 0
    while i < n {
        bind total = total + sqrt(pow(path[i * 2 + 2] - path[i * 2], 2.0) + pow(path[i * 2 + 3] - path[i * 2 + 1], 2.0))
        bind i = i + 1
    }
    bind target = clamp(p, 0.0, 1.0) * total
    bind i = 0
    while i < n {
        bind sl = sqrt(pow(path[i * 2 + 2] - path[i * 2], 2.0) + pow(path[i * 2 + 3] - path[i * 2 + 1], 2.0))
        if target <= sl {
            bind f = target / max(sl, 0.0001)
            return [mix(path[i * 2], path[i * 2 + 2], f), mix(path[i * 2 + 1], path[i * 2 + 3], f)]
        }
        bind target = target - sl
        bind i = i + 1
    }
    return [path[n * 2], path[n * 2 + 1]]
}

# ---- view 1: LAYOUT, a schematic of the board with live traffic on its buses
fn view_board_layout(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind en = Z[Z_ENERGY]
    bind D = Z[Z_D]
    bind bp = D[D_BRD]
    bind gp = D[D_GPU]
    bind info = bp[BP_INFO]
    bind bv = bp[BP_BV]
    bind cu = bp[BP_CU]
    bind S = min(H - 24.0, W * 0.6)
    bind x0 = 22.0
    bind y0 = (H - S) / 2.0 + 6.0
    # board outline, mounting holes
    ink(34.0)
    brect(x0, y0, S, 0.0, 0.0, 1.0, 1.0)
    ink(20.0)
    brect(x0, y0, S, 0.012, 0.012, 0.988, 0.988)
    bind hk = 0
    while hk < 6 {
        bind hu = 0.04
        if hk % 2 == 1 { bind hu = 0.96 }
        bind hv = 0.04 + floor(hk / 2) * 0.46
        ink(44.0)
        draw_circle(x0 + hu * S, y0 + hv * S, S * 0.014)
        draw_circle(x0 + hu * S, y0 + hv * S, S * 0.006)
        bind hk = hk + 1
    }
    # traces
    bind ch = 1
    while ch < 6 {
        bind vr = 0
        while vr < 2 {
            bind pth = board_path(ch, vr * 0.7)
            col(ch, sat * 0.6, 17.0 + 22.0 * en[ch], hs)
            bind q = 0
            while q < len(pth) / 2 - 1 {
                bline(x0, y0, S, pth[q * 2], pth[q * 2 + 1], pth[q * 2 + 2], pth[q * 2 + 3])
                bind q = q + 1
            }
            bind vr = vr + 1
        }
        bind ch = ch + 1
    }
    # rear I/O and VRM
    col(5, sat * 0.8, 38.0 + 24.0 * en[5], hs)
    brect(x0, y0, S, 0.0, 0.06, 0.075, 0.46)
    bind pq = 0
    while pq < 6 {
        bline(x0, y0, S, 0.012, 0.09 + pq * 0.058, 0.063, 0.09 + pq * 0.058)
        bind pq = pq + 1
    }
    col(0, sat * 0.8, 26.0 + 30.0 * en[0], hs)
    brect(x0, y0, S, 0.09, 0.06, 0.33, 0.115)
    brect(x0, y0, S, 0.09, 0.125, 0.135, 0.44)
    bhatch(x0, y0, S, 0.09, 0.06, 0.33, 0.115, 5.0 - 3.0 * en[0])
    bhatch(x0, y0, S, 0.09, 0.125, 0.135, 0.44, 5.0 - 3.0 * en[0])
    # CPU socket and the logical processors inside it
    bind ncores = max(len(cu), 1)
    bind cols = grid_cols(ncores)
    bind su = 0.3
    bind sv = 0.3
    bind sh_ = 0.135
    bind heat = en[6]
    col(0, sat, 40.0 + 30.0 * heat, hs)
    brect(x0, y0, S, su - sh_, sv - sh_, su + sh_, sv + sh_)
    bind bk = 0.02
    bind cx_ = x0 + su * S
    bind cy_ = y0 + sv * S
    col(0, sat * 0.8, 30.0 + 20.0 * heat, hs)
    brect(x0, y0, S, su - sh_ + 0.012, sv - sh_ + 0.012, su + sh_ - 0.012, sv + sh_ - 0.012)
    bind inner = (sh_ - 0.03) * 2.0
    bind cell = inner / cols
    bind i = 0
    while i < ncores {
        bind gx = i % cols
        bind gy = floor(i / cols)
        bind ccx = x0 + (su - sh_ + 0.03 + (gx + 0.5) * cell) * S
        bind ccy = y0 + (sv - sh_ + 0.03 + (gy + 0.5) * cell) * S
        bind u = cu[i] / 100.0
        bind hh = cell * S * 0.4
        col(0, sat, 22.0 + 66.0 * u, hs + 28.0 * u)
        square(ccx, ccy, hh, 0.0)
        if u > 0.2 { square(ccx, ccy, hh * u * 0.8, t * 0.5 + i) }
        bind i = i + 1
    }
    bind nm = str_slice(info[BI_CPU], 0, 28)
    ink(60.0)
    daemon_text(nm, cx_ - text_w(nm, 10.0) / 2.0, y0 + (sv + sh_ + 0.025) * S, 10.0)
    # memory slots
    bind slots = clamp(info[BI_SLOTS], 1.0, 8.0)
    bind dimms = bp[BP_DIMM]
    bind order = [1, 3, 0, 2, 5, 7, 4, 6]
    bind slot = 0
    while slot < slots {
        bind occ = 0 - 1
        bind oi = 0
        while oi < len(dimms) {
            if oi < 8 {
                if order[oi] == slot { bind occ = oi }
            }
            bind oi = oi + 1
        }
        bind u0 = 0.62 + slot * (0.34 / slots)
        bind u1 = u0 + 0.026
        col(1, sat * 0.8, 36.0 + 20.0 * en[1], hs)
        if occ < 0 { ink(24.0) }
        brect(x0, y0, S, u0, 0.06, u1, 0.46)
        if occ >= 0 {
            bind dm = dimms[occ]
            col(1, sat, 28.0 + 30.0 * en[1], hs)
            bhatch(x0, y0, S, u0, 0.06, u1, 0.46, 6.0 - 3.0 * en[1])
            bind ctext = fmt_mb(dm[1] * 1024.0)
            col(1, sat, 70.0, hs)
            daemon_text(ctext, x0 + u0 * S + (u1 - u0) * S / 2.0 - text_w(ctext, 9.0) / 2.0, y0 + 0.485 * S, 9.0)
            bind sp = num_str(round(dm[2]))
            ink(52.0)
            daemon_text(sp, x0 + u0 * S + (u1 - u0) * S / 2.0 - text_w(sp, 8.0) / 2.0, y0 + 0.515 * S, 8.0)
        }
        bind slot = slot + 1
    }
    # storage, chipset
    col(4, sat, 40.0 + 30.0 * en[4], hs)
    brect(x0, y0, S, 0.1, 0.5, 0.3, 0.54)
    bhatch(x0, y0, S, 0.1, 0.5, 0.3, 0.54, 5.0 - 3.0 * en[4])
    draw_circle(x0 + 0.315 * S, y0 + 0.52 * S, S * 0.007)
    bind sa = 0
    while sa < 5 {
        brect(x0, y0, S, 0.93, 0.5 + sa * 0.07, 0.985, 0.54 + sa * 0.07)
        bind sa = sa + 1
    }
    col(3, sat, 40.0 + 30.0 * en[3], hs)
    brect(x0, y0, S, 0.68, 0.56, 0.84, 0.72)
    bhatch(x0, y0, S, 0.68, 0.56, 0.84, 0.72, 6.0 - 3.0 * en[3])
    col(3, sat, 80.0, hs)
    daemon_text("PCH", x0 + 0.76 * S - text_w("PCH", 16.0) / 2.0, y0 + 0.64 * S, 16.0)
    # PCIe slots and the cards in them
    bind ngpu = gp[GP_N]
    bind sl = 0
    while sl < 3 {
        bind sv2 = 0.68 + sl * 0.1
        bind hit = 0
        if sl < ngpu { bind hit = 1 }
        col(2, sat * 0.8, 30.0 + 26.0 * en[2], hs)
        if hit == 0 { ink(22.0) }
        brect(x0, y0, S, 0.07, sv2 - 0.007, 0.5, sv2 + 0.007)
        bline(x0, y0, S, 0.4, sv2 - 0.007, 0.4, sv2 + 0.007)
        if hit == 1 {
            bind gv = gp[GP_VEC][sl]
            bind gu = gv[GV_UTIL] / 100.0
            col(sl % 7 + 2, sat, 40.0 + 40.0 * gu, hs)
            brect(x0, y0, S, 0.065, sv2 - 0.052, 0.53, sv2 - 0.012)
            bhatch(x0, y0, S, 0.065, sv2 - 0.052, 0.53, sv2 - 0.012, 7.0 - 5.0 * gu)
            bind gl = "GPU" + num_str(sl) + " " + short_gpu(gp[GP_NAME][sl]) + " " + pct(gv[GV_UTIL]) + "%"
            col(sl % 7 + 2, sat, 86.0, hs)
            daemon_text(gl, x0 + 0.08 * S, y0 + (sv2 - 0.032) * S, 11.0)
            ink(55.0)
            daemon_text("GEN" + pct(gv[GV_PGEN]) + " X" + pct(gv[GV_PW]), x0 + 0.54 * S, y0 + sv2 * S, 10.0)
        } else {
            ink(32.0)
            daemon_text("PCIE X16", x0 + 0.54 * S, y0 + sv2 * S, 10.0)
        }
        bind sl = sl + 1
    }
    # pulses: circles come in towards the CPU, squares go out from it
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind p = (t - ev[j + 2]) / 1.8
        bind c = ev[j + 1]
        bind a = 1.0 - p
        if c == 0 {
            bind pi_ = ev[j + 5]
            if pi_ < ncores {
                bind gx = pi_ % cols
                bind gy = floor(pi_ / cols)
                bind ccx = x0 + (su - sh_ + 0.03 + (gx + 0.5) * cell) * S
                bind ccy = y0 + (sv - sh_ + 0.03 + (gy + 0.5) * cell) * S
                col(0, sat, a * (60.0 + 38.0 * ev[j + 4]), hs)
                if ev[j] == 0 { draw_circle(ccx, ccy, cell * S * (0.4 + 0.9 * p)) } else { square(ccx, ccy, cell * S * (0.8 - 0.3 * p), p * 3.0) }
            }
        }
        if c == 6 {
            col(6, sat, a * (50.0 + 40.0 * ev[j + 4]), hs)
            draw_circle(cx_, cy_, S * (0.1 + 0.15 * p))
        }
        if c > 0 {
            if c < 6 {
                bind pth = board_path(c, ev[j + 3])
                bind q = p
                if ev[j] == 0 { bind q = 1.0 - p }
                bind pt = path_pt(pth, q)
                bind x = x0 + pt[0] * S
                bind y = y0 + pt[1] * S
                col(c, sat, 55.0 + 40.0 * ev[j + 4], hs)
                if ev[j] == 0 {
                    draw_circle(x, y, S * 0.012)
                    draw_circle(x, y, S * 0.006)
                } else {
                    square(x, y, S * 0.012, p * 3.0)
                }
            }
        }
        bind j = j + 6
    }
    # facts, right of the board
    bind px0 = x0 + S + 36.0
    bind pw = W - px0 - 18.0
    bind lh = 24.0
    bind ly = 40.0
    bind hl = min(14.0, pw / 46.0)
    tile("B", px0, ly - 18.0, 46.0, 66.0, 1, sat, hs, 0.6)
    col(1, sat, 80.0, hs)
    daemon_text(str_slice(info[BI_PROD], 0, 28), px0 + 62.0, ly + 6.0, min(18.0, pw / 32.0))
    ink(56.0)
    daemon_text(str_slice(info[BI_MAKER], 0, 30), px0 + 62.0, ly + 28.0, 11.0)
    bind ly = ly + 66.0
    bind facts = ["BIOS   " + str_slice(info[BI_BIOSV], 0, 20) + " " + info[BI_BIOSVER] + " " + info[BI_BIOSDATE],
                  "CPU    " + pct(info[BI_CORES]) + " CORES " + pct(info[BI_THR]) + " THREADS  " + num_str(round(bv[BV_MHZ] / 100.0) / 10.0) + " GHZ",
                  "MEMORY " + pct(len(dimms)) + " OF " + pct(info[BI_SLOTS]) + " SLOTS  MAX " + pct(info[BI_MAXGB]) + "G",
                  "ACPI   " + num_str(round(bv[BV_TEMP] * 10.0) / 10.0) + "C   UP " + fmt_up(bv[BV_UP]),
                  "LOAD   " + pct(bv[BV_UTIL]) + "%   PROCS " + pct(bv[BV_PROCS]) + "   THREADS " + fmt_rate(bv[BV_THR]),
                  "IRQ    " + fmt_rate(bv[BV_INTR]) + "/S   DPC " + fmt_rate(bv[BV_DPC]) + "/S   CTX " + fmt_rate(bv[BV_CTX]) + "/S"]
    bind i = 0
    while i < len(facts) {
        ink(64.0 - 3.0 * i)
        daemon_text(facts[i], px0, ly + i * lh, hl)
        bind i = i + 1
    }
    bind ly = ly + len(facts) * lh + 14.0
    col(4, sat, 70.0, hs)
    daemon_text("DISKS", px0, ly, 13.0)
    bind dio = bp[BP_DIO]
    bind d = 0
    while d < min(len(dio), 6) {
        bind dd = dio[d]
        bind dy = ly + 24.0 + d * 36.0
        ink(62.0)
        daemon_text(str_slice(dd[0], 0, 10) + "  R " + fmt_bytes(dd[1]) + "  W " + fmt_bytes(dd[2]), px0, dy, 12.0)
        hbar(px0, dy + 16.0, pw * 0.8, dd[3] / 100.0, 4, sat, 58.0 + 20.0 * dd[3] / 100.0, hs)
        ink(50.0)
        daemon_text(pct(dd[3]) + "%", px0 + pw * 0.8 + 8.0, dy + 12.0, 11.0)
        bind d = d + 1
    }
    # real sensors, only when a monitor that exposes WMI is running
    bind sens = bp[BP_SENS]
    if len(sens) > 0 {
        bind ly = ly + 24.0 + min(len(dio), 6) * 36.0 + 10.0
        col(6, sat, 70.0, hs)
        daemon_text("SENSORS", px0, ly, 13.0)
        bind s = 0
        while s < min(len(sens), 5) {
            bind sn = sens[s]
            ink(60.0)
            daemon_text(str_slice(sn[1], 0, 24) + " " + num_str(round(sn[2] * 10.0) / 10.0), px0, ly + 22.0 + s * 20.0, 12.0)
            bind s = s + 1
        }
    }
}

# ---- view 2: CORES, every logical processor as a body: circle = clock, square = load
fn view_board_cores(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W, H) * 0.5 - 20.0
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind bp = Z[Z_D][D_BRD]
    bind info = bp[BP_INFO]
    bind bv = bp[BP_BV]
    bind cu = bp[BP_CU]
    bind cf = bp[BP_CF]
    bind n = max(len(cu), 1)
    bind maxf = max(info[BI_MHZ] * 1.8, 1.0)
    bind i = 0
    while i < n {
        bind maxf = max(maxf, cf[i])
        bind i = i + 1
    }
    # Chesilin rim: the CPU, turning
    bind rim = str_slice(info[BI_CPU], 0, 30) + " " + pct(info[BI_CORES]) + " CORES " + pct(info[BI_THR]) + " THREADS "
    col(0, sat, 60.0, hs)
    draw_circle(cx, cy, R)
    draw_circle(cx, cy, R * 0.915)
    ring_text(rim, cx, cy, R * 0.925, R * 0.99, Z[Z_ROT] * 0.4, 66.0, 0, sat, hs)
    # hub
    bind hr = R * 0.2
    ink(34.0)
    draw_circle(cx, cy, hr)
    bind k = 0
    while k < 48 {
        bind a = k * TAU / 48.0 + Z[Z_ROT] * 0.5
        bind tl = 5.0
        if k % 4 == 0 { bind tl = 10.0 }
        pline(cx, cy, hr - tl, a, hr, a)
        bind k = k + 1
    }
    bind big = pct(bv[BV_UTIL]) + "%"
    col(0, sat, 78.0, hs)
    bind bh = fit_h(big, hr * 1.4, hr * 0.5)
    daemon_text(big, cx - text_w(big, bh) / 2.0, cy - hr * 0.1, bh)
    bind sm = num_str(round(bv[BV_MHZ] / 100.0) / 10.0) + " GHZ"
    ink(58.0)
    bind sh2 = fit_h(sm, hr * 1.4, hr * 0.2)
    daemon_text(sm, cx - text_w(sm, sh2) / 2.0, cy + hr * 0.38, sh2)
    # bodies
    bind two = 0
    if n > 36 { bind two = 1 }
    bind pos = []
    bind i = 0
    while i < n {
        bind rr = R * 0.74
        bind m = n
        bind idx = i
        if two == 1 {
            bind m = ceil(n / 2.0)
            if i < m {
                bind rr = R * 0.8
            } else {
                bind rr = R * 0.56
                bind m = n - m
                bind idx = i - ceil(n / 2.0)
            }
        }
        bind dir = 1.0
        if two == 1 {
            if i >= ceil(n / 2.0) { bind dir = 0.0 - 1.0 }
        }
        bind a = idx * TAU / m + Z[Z_ROT] * 0.12 * dir
        bind x = px(cx, rr, a)
        bind y = py(cy, rr, a)
        bind pos = list_push(pos, x)
        bind pos = list_push(pos, y)
        bind u = cu[i] / 100.0
        bind fq = clamp(cf[i] / maxf, 0.0, 1.0)
        bind k = floor(i * 7.0 / n)
        bind r = min(R * 0.052, TAU * rr / m * 0.4) * (0.55 + 0.45 * fq)
        ink(12.0 + 22.0 * u)
        pline(cx, cy, hr, a, rr - r, a)
        col(k, sat, 46.0 + 28.0 * u, hs)
        draw_circle(x, y, r)
        draw_circle(x, y, r * 0.86)
        col(k, sat, 28.0 + 26.0 * u, hs)
        hatch_disc(x, y, r * 0.84, ANG1[k] + Z[Z_ROT] * 0.5, max(2.2, r * 0.5 * (1.15 - u)))
        col(k, sat, 78.0, hs)
        square(x, y, r * 0.72 * sqrt(clamp(u, 0.04, 1.0)), t * 0.6 * dir + i)
        if r > 10.0 {
            col(k, sat, 90.0, hs)
            daemon_text(num_str(i), x - text_w(num_str(i), r * 0.55) / 2.0, y + r * 1.5, r * 0.55)
        }
        bind i = i + 1
    }
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind pidx = ev[j + 5]
        if pidx < n {
            bind p = (t - ev[j + 2]) / 1.8
            bind x = pos[pidx * 2]
            bind y = pos[pidx * 2 + 1]
            bind k = floor(pidx * 7.0 / n)
            bind r = R * 0.04
            col(k, sat, (1.0 - p) * (60.0 + 38.0 * ev[j + 4]), hs)
            if ev[j] == 0 {
                draw_circle(x, y, r * (1.0 + 1.1 * p))
                draw_circle(x, y, r * (1.0 + 0.5 * p))
            } else {
                square(x, y, r * (1.9 - 0.8 * p), p * 2.5)
                square(x, y, r * (1.4 - 0.6 * p), 0.0 - p * 2.5)
            }
        }
        bind j = j + 6
    }
}

# ---- view 3: SIGNALS, the last minute of seven vital signs
fn view_board_signals(Z) {
    bind bv = Z[Z_D][D_BRD][BP_BV]
    bind titles = ["CPU LOAD", "ACPI TEMP", "INTERRUPTS", "DPCS", "CONTEXT SW", "DISK READ", "DISK WRITE"]
    bind vals = [pct(bv[BV_UTIL]) + "%", num_str(round(bv[BV_TEMP] * 10.0) / 10.0) + "C", fmt_rate(bv[BV_INTR]) + "/S", fmt_rate(bv[BV_DPC]) + "/S",
                 fmt_rate(bv[BV_CTX]) + "/S", fmt_bytes(bv[BV_DRD]) + "/S", fmt_bytes(bv[BV_DWR]) + "/S"]
    bind ids = [HS_BRD, HS_BRD + 1, HS_BRD + 2, HS_BRD + 3, HS_BRD + 4, HS_BRD + 5, HS_BRD + 6]
    scope(Z, titles, "CTIDXRW", ids, vals)
}

# ───────────────────────────── NETWORK section ───────────────────────────────
# Channels (rings, lanes, pulse categories): RX, TX, PKT IN, PKT OUT, TCP, UDP, RETRANS.
# Data: net.snap from sampler_hw.ps1 (interface counters, TCP/UDP counters, netstat, one probe of your gateway).
# Remote addresses are never drawn as text: a connection is a dot whose place comes from a hash of the
# address and whose sector is its remote port (HTTPS, HTTP, DNS, REMOTE, MAIL, HIGH, OTHER).

# an arc of a circle, a0 to a1 (radians, clockwise from up)
fn arc(cx, cy, r, a0, a1) {
    bind n = floor(abs(a1 - a0) * r / 6.0) + 1
    bind i = 0
    while i < n {
        pline(cx, cy, r, mix(a0, a1, i / n), r, mix(a0, a1, (i + 1) / n))
        bind i = i + 1
    }
}

# Chesilin text laid along an arc, centred on angle ac, radius rm
fn arc_text(str, cx, cy, rm, ac, h) {
    bind m = str_len(str)
    bind da = h * 1.25 / rm
    bind q = 0
    while q < m {
        bind ch = str_slice(str, q, 1)
        if ch != " " { chesilin_glyph(ch, cx, cy, rm, ac + (q - (m - 1) / 2.0) * da, h * 1.05, h) }
        bind q = q + 1
    }
}

fn adapter_name(np) {
    bind ads = np[NP_AD]
    if len(ads) == 0 { return "NO ADAPTER" }
    bind ad = ads[0]
    bind s = str_upper(ad[0])
    bind s = str_replace(s, "INTEL[R] ", "")
    bind s = str_replace(s, "INTEL(R) ", "")
    return s
}

fn node_xy(nd, i, cx, cy, R) {
    bind c = port_class(nd[1])
    bind sec = TAU / 7.0
    bind a = c * sec + sec * (0.1 + 0.8 * fract(nd[0] * 3.7 + 0.31)) + (fract(i * 0.37) - 0.5) * 0.015
    bind r = R * (0.4 + 0.47 * fract(nd[0] * 17.3 + nd[1] * 0.0137))
    return [px(cx, r, a), py(cy, r, a), a, r]
}

# ---- view 1: FLOW, a constellation of live connections
fn view_net_flow(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W * 0.34, H * 0.5 - 20.0)
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind en = Z[Z_ENERGY]
    bind np = Z[Z_D][D_NET]
    bind nv = np[NP_NV]
    bind nodes = np[NP_NODE]
    bind sec = TAU / 7.0
    bind spin = Z[Z_ROT] * 0.02
    # sectors: rim arc, divider, name and count
    bind counts = [0, 0, 0, 0, 0, 0, 0]
    bind i = 0
    while i < len(nodes) {
        bind c = port_class(nodes[i][1])
        bind counts = list_set(counts, c, counts[c] + 1)
        bind i = i + 1
    }
    bind c = 0
    while c < 7 {
        bind e = en[c]
        bind a0 = c * sec + 0.02
        bind a1 = (c + 1) * sec - 0.02
        col(c, sat * 0.8, 36.0 + 24.0 * e, hs)
        arc(cx, cy, R, a0, a1)
        arc(cx, cy, R * 0.915, a0, a1)
        col(c, sat * 0.6, 16.0 + 10.0 * e, hs)
        pline(cx, cy, R * 0.17, c * sec, R * 0.915, c * sec)
        col(c, sat, 66.0 + 24.0 * e, hs)
        arc_text(PORT_NAME[c] + " " + num_str(counts[c]), cx, cy, R * 0.955, (c + 0.5) * sec, R * 0.05)
        bind c = c + 1
    }
    # spokes and dots
    bind pos = []
    bind i = 0
    while i < len(nodes) {
        bind nd = nodes[i]
        bind xy = node_xy(nd, i, cx, cy, R)
        bind pos = list_push(pos, xy[0])
        bind pos = list_push(pos, xy[1])
        bind k = port_class(nd[1])
        col(k, sat * 0.7, 14.0 + 8.0 * en[k], hs)
        pline(cx, cy, R * 0.17, xy[2], xy[3], xy[2])
        col(k, sat, 52.0 + 24.0 * en[k], hs)
        if nd[2] == 0 {
            draw_circle(xy[0], xy[1], 5.0)
            draw_circle(xy[0], xy[1], 2.0)
        } else {
            square(xy[0], xy[1], 4.0, t * 0.8 + i)
        }
        bind i = i + 1
    }
    # hub: this machine, rates in and out
    bind hr = R * 0.17
    col(2, sat, 70.0, hs)
    draw_circle(cx, cy, hr)
    square(cx, cy, hr * 0.7, Z[Z_ROT] * 0.5)
    square(cx, cy, hr * 0.7, Z[Z_ROT] * 0.5 + 0.7854)
    bind a1s = "IN " + fmt_bytes(nv[NV_RX])
    bind a2s = "OUT " + fmt_bytes(nv[NV_TX])
    bind hh = fit_h(a1s, hr * 1.5, hr * 0.3)
    col(0, sat, 80.0, hs)
    daemon_text(a1s, cx - text_w(a1s, hh) / 2.0, cy - hr * 0.2, hh)
    bind hh2 = fit_h(a2s, hr * 1.5, hr * 0.3)
    col(1, sat, 80.0, hs)
    daemon_text(a2s, cx - text_w(a2s, hh2) / 2.0, cy + hr * 0.22, hh2)
    # pulses: rx = circle travelling in, tx = square travelling out
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind ni = ev[j + 5]
        if ni < len(nodes) {
            bind p = (t - ev[j + 2]) / 1.8
            bind nd = nodes[ni]
            bind k = port_class(nd[1])
            bind xy = node_xy(nd, ni, cx, cy, R)
            bind q = p
            if ev[j] == 0 { bind q = 1.0 - p }
            bind hx = px(cx, hr, xy[2])
            bind hy = py(cy, hr, xy[2])
            bind x = mix(hx, xy[0], q)
            bind y = mix(hy, xy[1], q)
            col(k, sat, (1.0 - p * 0.6) * (60.0 + 38.0 * ev[j + 4]), hs)
            if ev[j] == 0 {
                draw_circle(x, y, 7.0)
                draw_circle(x, y, 3.0)
            } else {
                square(x, y, 6.0, p * 4.0)
            }
        }
        bind j = j + 6
    }
    # left: the link
    bind lx = 18.0
    bind ly = H * 0.18
    bind lh = 13.0
    bind rtt = nv[NV_RTT]
    bind rs = "---"
    if rtt > 0.0 { bind rs = num_str(round(rtt)) + "MS" }
    bind adn = str_slice(adapter_name(np), 0, 26)
    bind bw = nv[NV_BW]
    bind lines = [adn, "LINK " + fmt_bytes(bw) + "BIT", "IN  " + fmt_bytes(nv[NV_RX]) + "/S", "OUT " + fmt_bytes(nv[NV_TX]) + "/S",
                  "PKTS " + fmt_rate(nv[NV_PRX]) + " IN " + fmt_rate(nv[NV_PTX]) + " OUT", "GATEWAY " + rs, "RETRANS " + fmt_rate(nv[NV_RETX]) + "/S",
                  "TCP EST " + fmt_rate(nv[NV_EST]), "LISTEN " + fmt_rate(nv[NV_LISTEN]) + "  WAIT " + fmt_rate(nv[NV_TW]),
                  "CLOSE " + fmt_rate(nv[NV_CW]) + "  LOOP " + fmt_rate(nv[NV_LOOP])]
    tile("N", lx, ly - 54.0, 44.0, 66.0, 6, sat, hs, 0.6)
    bind i = 0
    while i < len(lines) {
        col(6, sat, 78.0 - 4.0 * i, hs)
        if i > 1 { ink(64.0 - 2.0 * i) }
        daemon_text(lines[i], lx, ly + 8.0 + i * 24.0, lh)
        bind i = i + 1
    }
    # right: who holds the connections
    bind rx0 = W - 18.0 - min(W * 0.22, 280.0)
    bind pw = min(W * 0.22, 280.0)
    col(6, sat, 70.0, hs)
    daemon_text("BUSIEST PROCESSES", rx0, H * 0.1, 13.0)
    bind pr = np[NP_PROC]
    bind top = 1.0
    if len(pr) > 0 { bind top = max(pr[0][1], 1.0) }
    bind i = 0
    while i < min(len(pr), 9) {
        bind pe = pr[i]
        bind yy = H * 0.1 + 32.0 + i * 44.0
        bind k = name_hue(pe[0])
        col(k, sat, 72.0, hs)
        daemon_text(str_slice(pe[0], 0, 14) + " " + num_str(pe[1]), rx0, yy, 12.0)
        hbar(rx0, yy + 16.0, pw, pe[1] / top, k, sat, 56.0, hs)
        bind i = i + 1
    }
}

# ---- view 2: LINKS, seven rings, one per channel
fn view_net_links(Z) {
    bind np = Z[Z_D][D_NET]
    bind nv = np[NP_NV]
    bind labels = ["RX " + fmt_bytes(nv[NV_RX]) + " PER S ", "TX " + fmt_bytes(nv[NV_TX]) + " PER S ", "PACKETS IN " + fmt_rate(nv[NV_PRX]) + " ",
                   "PACKETS OUT " + fmt_rate(nv[NV_PTX]) + " ", "TCP " + fmt_rate(nv[NV_EST]) + " LINKS ", "UDP " + fmt_rate(nv[NV_UDP]) + " PER S ",
                   "RETRANSMITS " + fmt_rate(nv[NV_RETX]) + " "]
    bind rtt = nv[NV_RTT]
    bind big = "---"
    if rtt > 0.0 { bind big = num_str(round(rtt)) + "MS" }
    ring_stack(Z, 7, IDM, labels, Z[Z_FRAC], big, str_slice(adapter_name(np), 0, 22), 6)
}

# ---- view 3: TRACES, the last minute of seven vital signs
fn view_net_traces(Z) {
    bind nv = Z[Z_D][D_NET][NP_NV]
    bind titles = ["RECEIVE", "TRANSMIT", "PACKETS IN", "PACKETS OUT", "TCP LINKS", "UDP", "GATEWAY"]
    bind rtt = nv[NV_RTT]
    bind rs = "---"
    if rtt > 0.0 { bind rs = num_str(round(rtt)) + "MS" }
    bind vals = [fmt_bytes(nv[NV_RX]) + "/S", fmt_bytes(nv[NV_TX]) + "/S", fmt_rate(nv[NV_PRX]) + "/S", fmt_rate(nv[NV_PTX]) + "/S",
                 fmt_rate(nv[NV_EST]), fmt_rate(nv[NV_UDP]) + "/S", rs]
    bind ids = [HS_NET, HS_NET + 1, HS_NET + 2, HS_NET + 3, HS_NET + 4, HS_NET + 5, HS_NET + 6]
    scope(Z, titles, "RTIOCUG", ids, vals)
}

# ───────────────────────────── MAIN screen ───────────────────────────────────
# Four rings in the middle (memory, GPU, CPU, network) and a card for each section on either side.
# A card shows that section's headline numbers; click it, or press its number, to open the section.

bind MAIN_HM = [4, 3, 1, 6]

fn card_rect(i, W, H) {
    bind cw = clamp(W * 0.225, 220.0, 340.0)
    bind top = 74.0
    bind gap = 20.0
    bind ch = (H - top - 14.0 - gap) / 2.0
    bind x = 18.0
    if i >= 2 { bind x = W - 18.0 - cw }
    bind y = top
    if i % 2 == 1 { bind y = top + ch + gap }
    return [x, y, cw, ch]
}

# which card, if any, is under (mx, my)
fn card_hit(mx, my, W, H) {
    bind i = 0
    while i < 4 {
        bind r = card_rect(i, W, H)
        if mx >= r[0] {
            if mx <= r[0] + r[2] {
                if my >= r[1] {
                    if my <= r[1] + r[3] { return i }
                }
            }
        }
        bind i = i + 1
    }
    return 0 - 1
}

fn live_tag(l) {
    if l == 1 { return "LIVE" }
    if l == 2 { return "STALE" }
    return "SIM"
}

fn card(Z, i, hov) {
    bind sat = Z[Z_SAT]
    bind hs = Z[Z_HS]
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind D = Z[Z_D]
    bind hst = D[D_HS]
    bind live = D[D_LIVE]
    bind sec = i + 1
    bind k = SEC_HUE[sec]
    bind r = card_rect(i, W, H)
    bind x = r[0]
    bind y = r[1]
    bind cw = r[2]
    bind ch = r[3]
    bind lit = 34.0 + 22.0 * hov
    col(k, sat * 0.7, lit, hs)
    brect(x, y, 1.0, 0.0, 0.0, cw, ch)
    bind a = 14.0
    col(k, sat, lit + 28.0, hs)
    bline(x, y, 1.0, 0.0, a, 0.0, 0.0)
    bline(x, y, 1.0, 0.0, 0.0, a, 0.0)
    bline(x, y, 1.0, cw - a, 0.0, cw, 0.0)
    bline(x, y, 1.0, cw, 0.0, cw, a)
    bline(x, y, 1.0, cw, ch - a, cw, ch)
    bline(x, y, 1.0, cw, ch, cw - a, ch)
    bline(x, y, 1.0, a, ch, 0.0, ch)
    bline(x, y, 1.0, 0.0, ch, 0.0, ch - a)
    tile(str_slice(SEC_LET, sec, 1), x + 12.0, y + 12.0, 44.0, 56.0 + 24.0 * hov, k, sat, hs, 0.5 + hov)
    col(k, sat, 78.0 + 12.0 * hov, hs)
    daemon_text(SEC_NAME[sec], x + 68.0, y + 28.0, 17.0)
    ink(52.0)
    daemon_text(num_str(sec) + "  " + live_tag(live[i]), x + 68.0, y + 50.0, 10.0)
    bind frac = 0.0
    bind big = ""
    bind lines = []
    bind sid = 0
    if i == 0 {
        bind mm = D[D_MEM]
        bind cats = mm[0]
        bind total = mm[1]
        bind frac = mm[5]
        bind big = pct(frac * 100.0) + "%"
        bind lines = ["USED " + fmt_mb(frac * total) + " OF " + fmt_mb(total), "STANDBY " + fmt_mb(cats[1]) + " FREE " + fmt_mb(cats[6]), "READS " + fmt_rate(mm[2]) + "/S",
                      "WRITES " + fmt_rate(mm[3]) + "/S", "FAULTS " + fmt_rate(mm[4]) + "/S"]
        bind sid = HS_MEM
    }
    if i == 1 {
        bind gp = D[D_GPU]
        bind n = gp[GP_N]
        bind mx_ = 0.0
        bind g = 0
        bind lines = []
        while g < n {
            bind v = gp[GP_VEC][g]
            bind mx_ = max(mx_, v[GV_UTIL])
            if n <= 2 {
                bind lines = list_push(lines, "G" + num_str(g) + " " + short_gpu(gp[GP_NAME][g]) + " " + pct(v[GV_UTIL]) + "%")
                bind lines = list_push(lines, "  " + fmt_mb(v[GV_VUSED]) + "/" + fmt_mb(v[GV_VTOTAL]) + " " + pct(v[GV_TEMP]) + "C " + pct(v[GV_POWER]) + "W")
            } else {
                if g < 5 {
                    bind lines = list_push(lines, "G" + num_str(g) + " " + pct(v[GV_UTIL]) + "% " + fmt_mb(v[GV_VUSED]) + " " + pct(v[GV_TEMP]) + "C " + pct(v[GV_POWER]) + "W")
                }
            }
            bind g = g + 1
        }
        bind v0 = gp[GP_VEC][0]
        if n <= 2 { bind lines = list_push(lines, "PCIE GEN" + pct(v0[GV_PGEN]) + " X" + pct(v0[GV_PW])) }
        if n > 5 { bind lines = list_push(lines, "+ " + num_str(n - 5) + " MORE") }
        bind frac = mx_ / 100.0
        bind big = pct(mx_) + "%"
        bind sid = HS_GPU
    }
    if i == 2 {
        bind bp = D[D_BRD]
        bind info = bp[BP_INFO]
        bind bv = bp[BP_BV]
        bind frac = bv[BV_UTIL] / 100.0
        bind big = pct(bv[BV_UTIL]) + "%"
        bind lines = [str_slice(info[BI_CPU], 0, 26), pct(info[BI_CORES]) + "C " + pct(info[BI_THR]) + "T " + num_str(round(bv[BV_MHZ] / 100.0) / 10.0) + " GHZ",
                      "ACPI " + num_str(round(bv[BV_TEMP] * 10.0) / 10.0) + "C  UP " + fmt_up(bv[BV_UP]), "PROCS " + pct(bv[BV_PROCS]) + " THR " + fmt_rate(bv[BV_THR]),
                      "IRQ " + fmt_rate(bv[BV_INTR]) + "/S DPC " + fmt_rate(bv[BV_DPC]) + "/S"]
        bind sid = HS_BRD
    }
    if i == 3 {
        bind np = D[D_NET]
        bind nv = np[NP_NV]
        bind frac = lg(nv[NV_RX] + nv[NV_TX], 9.0)
        bind big = fmt_bytes(nv[NV_RX] + nv[NV_TX])
        bind rs = "---"
        if nv[NV_RTT] > 0.0 { bind rs = num_str(round(nv[NV_RTT])) + "MS" }
        bind lines = [str_slice(adapter_name(np), 0, 26), "IN " + fmt_bytes(nv[NV_RX]) + "/S OUT " + fmt_bytes(nv[NV_TX]) + "/S", "PKTS " + fmt_rate(nv[NV_PRX]) + " IN " + fmt_rate(nv[NV_PTX]) + " OUT",
                      "TCP " + fmt_rate(nv[NV_EST]) + " LISTEN " + fmt_rate(nv[NV_LISTEN]), "GATEWAY " + rs]
        bind sid = HS_NET
    }
    bind bh = fit_h(big, cw * 0.3, 22.0)
    col(k, sat, 84.0, hs)
    daemon_text(big, x + cw - 14.0 - text_w(big, bh), y + 34.0, bh)
    hbar(x + 12.0, y + 84.0, cw - 24.0, frac, k, sat, 56.0 + 24.0 * hov, hs)
    bind lh = min(12.0, (cw - 24.0) / 24.0)
    bind q = 0
    while q < len(lines) {
        ink(66.0 - 4.0 * q + 8.0 * hov)
        daemon_text(lines[q], x + 12.0, y + 108.0 + q * 21.0, lh)
        bind q = q + 1
    }
    spark(x + 12.0, y + ch - 52.0, cw - 24.0, 36.0, hst[sid], 100, k, sat, 62.0 + 20.0 * hov, hs)
}

fn view_main(Z) {
    bind D = Z[Z_D]
    bind bp = D[D_BRD]
    bind bv = bp[BP_BV]
    bind mm = D[D_MEM]
    bind gp = D[D_GPU]
    bind nv = D[D_NET][NP_NV]
    bind fr = Z[Z_FRAC]
    bind gmax = 0.0
    bind g = 0
    while g < gp[GP_N] {
        bind gmax = max(gmax, gp[GP_VEC][g][GV_UTIL])
        bind g = g + 1
    }
    bind labels = ["MEMORY " + pct(mm[5] * 100.0) + " ", "GPU " + pct(gmax) + " ", "CPU " + pct(bv[BV_UTIL]) + " ", "NETWORK " + fmt_bytes(nv[NV_RX] + nv[NV_TX]) + " "]
    ring_stack(Z, 4, MAIN_HM, labels, fr, fmt_up(bv[BV_UP]), "SYSTEM UP", 2)
    bind mx = Z[Z_MX]
    bind my = Z[Z_MY]
    bind hit = card_hit(mx, my, Z[Z_W], Z[Z_H])
    bind i = 0
    while i < 4 {
        bind hv = 0.0
        if hit == i { bind hv = 1.0 }
        card(Z, i, hv)
        bind i = i + 1
    }
}

# ───────────────────────────── menu and hud ──────────────────────────────────
# The menu sits in the bottom 66 px: five section buttons, then the views of the current section.
# Everything is drawn from menu_geom/tab_rect, and the same functions answer the mouse.

bind VIEW_COUNT = [1, 4, 4, 3, 3]
bind MENU_H = 66.0

# [x of first button, y, button width, button height, gap]
fn menu_geom(W, FH) {
    return [18.0, FH - 62.0, clamp(W * 0.088, 92.0, 150.0), 26.0, 8.0]
}

# [x, y, w, h] of view tab j of a section
fn tab_rect(sec, j, W, FH) {
    bind g = menu_geom(W, FH)
    bind x = g[0] + 5.0 * (g[2] + g[4]) + 18.0
    bind names = SUB_NAME[sec]
    bind k = 0
    while k < j {
        bind x = x + text_w(names[k], 11.0) + 32.0
        bind k = k + 1
    }
    return [x, g[1] + 2.0, text_w(names[j], 11.0) + 26.0, g[3] - 4.0]
}

fn inside(mx, my, r) {
    if mx >= r[0] {
        if mx <= r[0] + r[2] {
            if my >= r[1] {
                if my <= r[1] + r[3] { return 1 }
            }
        }
    }
    return 0
}

# -1 nothing, 0..4 a section button, 10 + j a view tab
fn menu_hit(mx, my, W, FH, sec) {
    bind g = menu_geom(W, FH)
    bind i = 0
    while i < 5 {
        if inside(mx, my, [g[0] + i * (g[2] + g[4]), g[1], g[2], g[3]]) == 1 { return i }
        bind i = i + 1
    }
    if VIEW_COUNT[sec] > 1 {
        bind j = 0
        while j < VIEW_COUNT[sec] {
            if inside(mx, my, tab_rect(sec, j, W, FH)) == 1 { return 10 + j }
            bind j = j + 1
        }
    }
    return 0 - 1
}

fn menu_bar(Z, sec, view) {
    bind W = Z[Z_W]
    bind FH = Z[Z_FH]
    bind sat = Z[Z_SAT]
    bind hs = Z[Z_HS]
    bind mx = Z[Z_MX]
    bind my = Z[Z_MY]
    bind g = menu_geom(W, FH)
    bind i = 0
    while i < 5 {
        bind r = [g[0] + i * (g[2] + g[4]), g[1], g[2], g[3]]
        bind k = SEC_HUE[i]
        bind hov = inside(mx, my, r)
        bind on = 0
        if i == sec { bind on = 1 }
        bind fl = 30.0 + 18.0 * hov
        if on == 1 { bind fl = 82.0 }
        col(k, sat * 0.8, fl, hs)
        brect(r[0], r[1], 1.0, 0.0, 0.0, r[2], r[3])
        if on == 1 {
            col(k, sat * 0.8, 26.0, hs)
            bhatch(r[0], r[1], 1.0, 2.0, 2.0, r[2] - 2.0, r[3] - 2.0, 5.0)
            col(k, sat, 84.0, hs)
            bline(r[0], r[1] + r[3] + 4.0, 1.0, 0.0, 0.0, r[2], 0.0)
        }
        bind lab = num_str(i) + " " + SEC_NAME[i]
        bind tl = 52.0 + 24.0 * hov
        if on == 1 { bind tl = 94.0 }
        col(k, sat, tl, hs)
        daemon_text(lab, r[0] + (r[2] - text_w(lab, 12.0)) / 2.0, r[1] + r[3] / 2.0, 12.0)
        bind i = i + 1
    }
    if VIEW_COUNT[sec] > 1 {
        bind k = SEC_HUE[sec]
        bind j = 0
        while j < VIEW_COUNT[sec] {
            bind r = tab_rect(sec, j, W, FH)
            bind hov = inside(mx, my, r)
            bind tl = 46.0 + 24.0 * hov
            if j == view {
                bind tl = 90.0
                col(k, sat, 80.0, hs)
                bline(r[0], r[1] + r[3] + 3.0, 1.0, 0.0, 0.0, r[2], 0.0)
                bline(r[0], r[1] + r[3] + 6.0, 1.0, 0.0, 0.0, r[2], 0.0)
            }
            col(k, sat * 0.9, tl, hs)
            daemon_text(SUB_NAME[sec][j], r[0] + 13.0, r[1] + r[3] / 2.0, 11.0)
            bind j = j + 1
        }
    }
}

fn hud(Z, sec, view, auto, mute, pluck, alOn) {
    bind W = Z[Z_W]
    bind FH = Z[Z_FH]
    bind sat = Z[Z_SAT]
    bind hs = Z[Z_HS]
    bind k = SEC_HUE[sec]
    col(2, sat, 72.0, hs)
    daemon_text("HATCHSYS", 18.0, 26.0, 20.0)
    col(k, sat, 66.0, hs)
    bind mode = "SIM"
    if Z[Z_LIVE] == 1 { bind mode = "LIVE" }
    if Z[Z_LIVE] == 2 { bind mode = "STALE" }
    if Z[Z_LIVE] == 3 { bind mode = "PARTIAL" }
    bind tag = SEC_NAME[sec] + "  " + SUB_NAME[sec][view] + "  " + mode
    if sec == 2 {
        bind gp = Z[Z_D][D_GPU]
        bind tag = tag + "  GPU " + num_str(gp[GP_SEL]) + " OF " + num_str(gp[GP_N])
    }
    if auto == 1 { bind tag = tag + "  AUTO" }
    if pluck == 1 { bind tag = tag + "  PLUCK" }
    if alOn == 0 { bind tag = tag + "  NO ALERTS" }
    if mute == 1 { bind tag = tag + "  MUTE" }
    daemon_text(tag, 18.0, 52.0, 13.0)
    ink(52.0)
    daemon_text(Z[Z_STAT], 18.0, FH - 18.0, 13.0)
    bind keyhint = "0-4 SECTION  TAB VIEW  A AUTO  P PLUCK  N ALERTS  H HUD  M MUTE  ESC"
    if sec == 2 { bind keyhint = "UP/DN GPU  " + keyhint }
    ink(40.0)
    daemon_text(keyhint, W - text_w(keyhint, 10.0) - 18.0, FH - 18.0, 10.0)
}

# ───────────────────────────── drive ─────────────────────────────────────────
# What each section feeds the shared machinery (look, pulses, sound), from its own readings:
#   used   how full it is, 0..1: saturation and the root pitch
#   tg     four activity targets, 0..1: [in, out, third, fourth] -> rotation, hue drift, pulse rates
#   en     seven channel energies, 0..1: how lit each ring / lane / hue is
#   rw/ww  how pulses of each kind are spread over the seven channels
#   alc    the channel that allocations (the fourth activity) belong to
#   pwr/pww  how pulses are spread over the section's bodies (processes, cores, connections)
bind DR_USED = 0
bind DR_TG = 1
bind DR_EN = 2
bind DR_RW = 3
bind DR_WW = 4
bind DR_ALC = 5
bind DR_PWR = 6
bind DR_PWW = 7
bind DR_STAT = 8

fn add_all(l, v) {
    bind out = []
    bind i = 0
    while i < len(l) {
        bind out = list_push(out, l[i] + v)
        bind i = i + 1
    }
    return out
}

fn ones(n) {
    bind out = []
    bind i = 0
    while i < max(n, 1) {
        bind out = list_push(out, 1.0)
        bind i = i + 1
    }
    return out
}

# mm = [cats, total, rd, wr, fl, used, al, names, ws, ior, iow]
fn drive(sec, mm, gp, bp, np, A, gsel) {
    bind cats = mm[0]
    bind total = mm[1]
    bind bv = bp[BP_BV]
    bind nv = np[NP_NV]
    bind netA = lg(nv[NV_RX] + nv[NV_TX], 9.0)
    bind memAct = (lg(mm[2], 3.6) + lg(mm[3], 3.6)) / 2.0
    # GPU readings of the selected card, and the busiest card for the overview
    bind gv = gp[GP_VEC][gsel]
    bind ge = gp[GP_ENG][gsel]
    bind gmax = 0.0
    bind g = 0
    while g < gp[GP_N] {
        bind gmax = max(gmax, gp[GP_VEC][g][GV_UTIL])
        bind g = g + 1
    }
    bind vfrac = clamp(gv[GV_VUSED] / max(gv[GV_VTOTAL], 1.0), 0.0, 1.0)
    bind pfrac = clamp(gv[GV_POWER] / max(gv[GV_PLIM], 1.0), 0.0, 1.0)
    bind heat = clamp((bv[BV_TEMP] - 20.0) / 60.0, 0.0, 1.0)

    if sec == 1 {
        bind en = [clamp(0.25 * A[2] + 0.2 * A[0], 0.0, 1.0), A[0], clamp(0.6 * A[2] + 0.5 * A[1], 0.0, 1.0), A[1],
                   clamp(0.7 * A[0] + 0.2 * A[1], 0.0, 1.0), clamp(0.5 * A[1] + 0.3 * A[0], 0.0, 1.0), A[3]]
        bind rw = []
        bind ww = []
        bind q = 0
        while q < 7 {
            bind sh = 0.12 + sqrt(cats[q] / total)
            bind rw = list_push(rw, READ_W[q] * sh)
            bind ww = list_push(ww, WRITE_W[q] * sh)
            bind q = q + 1
        }
        bind ws = mm[8]
        bind pr = []
        bind pw = []
        bind q = 0
        while q < len(ws) {
            bind pr = list_push(pr, 0.2 + mm[9][q] + sqrt(ws[q]) * 0.03)
            bind pw = list_push(pw, 0.2 + mm[10][q] + sqrt(ws[q]) * 0.03)
            bind q = q + 1
        }
        bind stat = "RD " + fmt_rate(mm[2]) + "/S   WR " + fmt_rate(mm[3]) + "/S   FLT " + fmt_rate(mm[4]) + "/S   TOTAL " + fmt_mb(total)
        return [mm[5], [lg(mm[2], 3.6), lg(mm[3], 3.6), lg(mm[4], 5.0), lg(mm[6], 5.0)], en, rw, ww, 6, pr, pw, stat]
    }
    if sec == 2 {
        bind e0 = ge[0] / 100.0
        bind e1 = ge[1] / 100.0
        bind e2 = ge[2] / 100.0
        bind e3 = ge[3] / 100.0
        bind e4 = ge[4] / 100.0
        bind en = [e0, e1, e2, e3, e4, vfrac, pfrac]
        bind pr = gp[GP_PROC][gsel]
        bind pn = pr[0]
        bind pw = []
        bind q = 0
        while q < len(pn) {
            bind pw = list_push(pw, 0.1 + pr[3][q] * 0.05 + sqrt(pr[1][q]) * 0.02)
            bind q = q + 1
        }
        if len(pw) == 0 { bind pw = [1.0] }
        bind stat = "GPU" + num_str(gsel) + " " + pct(gv[GV_UTIL]) + "%   VRAM " + fmt_mb(gv[GV_VUSED]) + " OF " + fmt_mb(gv[GV_VTOTAL]) + "   " + pct(gv[GV_TEMP]) + "C   " + pct(gv[GV_POWER]) + "W   CORE " + pct(gv[GV_CLK]) + " MHZ"
        return [vfrac, [e0, clamp(e1 + e2, 0.0, 1.0), pfrac, clamp((e3 + e4) / 0.6, 0.0, 1.0)], en,
                [e0 + 0.05, 0.1, 0.0, e3, 0.0, 0.15, 0.1], [0.1 * e0 + 0.02, e1 + 0.05, e2 + 0.05, 0.0, e4, 0.15, 0.1], 5, pw, pw, stat]
    }
    if sec == 3 {
        bind u = bv[BV_UTIL] / 100.0
        bind en = [u, memAct, gmax / 100.0, 0.6 * lg(bv[BV_INTR], 6.0) + 0.4 * lg(bv[BV_DPC], 5.0), lg(bv[BV_DRD] + bv[BV_DWR], 9.0), netA, heat]
        bind cu = bp[BP_CU]
        bind pw = []
        bind q = 0
        while q < len(cu) {
            bind pw = list_push(pw, 0.1 + cu[q] / 25.0)
            bind q = q + 1
        }
        bind stat = "CPU " + pct(bv[BV_UTIL]) + "%  " + num_str(round(bv[BV_MHZ] / 100.0) / 10.0) + "GHZ   ACPI " + num_str(round(bv[BV_TEMP] * 10.0) / 10.0) + "C   IRQ " + fmt_rate(bv[BV_INTR]) + "/S   DISK R " + fmt_bytes(bv[BV_DRD]) + " W " + fmt_bytes(bv[BV_DWR])
        return [u, [u, lg(bv[BV_DWR], 9.0), lg(bv[BV_INTR], 6.0), lg(bv[BV_DPC], 5.0)], en, add_all(en, 0.08), add_all(en, 0.08), 3, pw, pw, stat]
    }
    if sec == 4 {
        bind en = [lg(nv[NV_RX], 9.0), lg(nv[NV_TX], 9.0), lg(nv[NV_PRX], 6.0), lg(nv[NV_PTX], 6.0), lg(nv[NV_EST], 3.5), lg(nv[NV_UDP], 4.0), lg(nv[NV_RETX], 3.0)]
        bind rs = "---"
        if nv[NV_RTT] > 0.0 { bind rs = num_str(round(nv[NV_RTT])) + "MS" }
        bind stat = "IN " + fmt_bytes(nv[NV_RX]) + "/S   OUT " + fmt_bytes(nv[NV_TX]) + "/S   TCP " + fmt_rate(nv[NV_EST]) + "   UDP " + fmt_rate(nv[NV_UDP]) + "/S   GATEWAY " + rs
        return [netA, [lg(nv[NV_RX], 8.0), lg(nv[NV_TX], 8.0), lg(nv[NV_PRX] + nv[NV_PTX], 5.0), lg(nv[NV_UDP], 4.0)], en,
                [en[0] + 0.02, 0.0, en[2] + 0.02, 0.0, 0.4 * en[4], 0.5 * en[5], 0.0], [0.0, en[1] + 0.02, 0.0, en[3] + 0.02, 0.4 * en[4], 0.5 * en[5], en[6]],
                4, ones(len(np[NP_NODE])), ones(len(np[NP_NODE])), stat]
    }
    # MAIN
    bind cpu = bv[BV_UTIL] / 100.0
    bind mu = mm[5]
    bind en = [mu, gmax / 100.0, cpu, netA, vfrac, bv[BV_DBUSY] / 100.0, heat]
    bind stat = "MEM " + pct(mu * 100.0) + "%   GPU " + pct(gmax) + "%   CPU " + pct(bv[BV_UTIL]) + "%   NET " + fmt_bytes(nv[NV_RX] + nv[NV_TX]) + "/S   UP " + fmt_up(bv[BV_UP])
    return [(mu + cpu + gmax / 100.0) / 3.0, [cpu, gmax / 100.0, netA, memAct], en, add_all(en, 0.05), add_all(en, 0.05), 3, [1.0], [1.0], stat]
}

# ───────────────────────────── alerts ────────────────────────────────────────
# When the machine "sounds" irregular, an alarm plays whatever section you are in, and a banner names it.
# Absolute limits (memory nearly full, GPU hot or out of VRAM, hot CPU zone, a sampler gone stale,
# heavy retransmits) and sudden jumps against a slow running average (CPU load, GPU load, gateway latency).
# Each kind can fire at most once every 25 s.

bind AL_TEXT = ["MEMORY NEARLY FULL", "GPU RUNNING HOT", "GPU MEMORY FULL", "CPU ZONE HOT", "SAMPLER STALE", "GPU LOAD SPIKE", "CPU LOAD SPIKE", "LATENCY SPIKE", "PACKET RETRANSMITS"]
bind AL_SEC = [1, 2, 2, 3, 0, 2, 3, 4, 4]
bind AL_COOL = 25.0

# m = [cpu, gpu, net] as 0..1, base = their running averages plus the gateway latency average
# returns the alert number (an index into AL_TEXT) or -1
fn alert_check(m, base, bvS, gpv, lives, usedM, nvS) {
    if usedM >= 0.93 { return 0 }
    bind g = 0
    while g < len(gpv) {
        bind v = gpv[g]
        if v[GV_THT] > 0.5 { return 1 }
        if v[GV_TEMP] >= 88.0 { return 1 }
        if v[GV_VUSED] >= 0.97 * max(v[GV_VTOTAL], 1.0) {
            if v[GV_VTOTAL] > 0.0 { return 2 }
        }
        bind g = g + 1
    }
    if bvS[BV_TEMP] >= 85.0 { return 3 }
    bind i = 0
    while i < 4 {
        if lives[i] == 2 { return 4 }
        bind i = i + 1
    }
    if m[1] - base[1] > 0.5 { return 5 }
    if m[0] - base[0] > 0.5 { return 6 }
    if nvS[NV_RTT] > 0.0 {
        if base[3] > 0.0 {
            if nvS[NV_RTT] > 3.0 * base[3] + 150.0 { return 7 }
        }
    }
    if lg(nvS[NV_RETX], 3.0) > 0.85 { return 8 }
    return 0 - 1
}

# one beep of an alarm; left counts down. Serious alerts (0-4) are a square wave and higher.
fn alert_beep(code, left) {
    bind f = 700.0
    bind wave = "tri"
    if left % 2 == 0 { bind f = 520.0 }
    if code <= 4 {
        bind wave = "square"
        bind f = 1200.0
        if left % 2 == 0 { bind f = 900.0 }
    }
    audio_sfx(0.0, 0.0, 6.0, 1.0, f, 0.12, 0.16, wave)
}

# the banner, top centre, flashing for 8 s: Z[Z_ALERT] = [text, born, section]
fn alert_banner(Z) {
    bind a = Z[Z_ALERT]
    bind age = Z[Z_T] - a[1]
    if age < 8.0 {
        if str_len(a[0]) > 0 {
            bind W = Z[Z_W]
            bind blink = 0.0
            if floor(age * 3.0) % 2 == 0 { bind blink = 1.0 }
            bind msg = "! " + a[0]
            bind h = 16.0
            bind w = text_w(msg, h) + 36.0
            bind x = (W - w) / 2.0
            col(0, 90.0, 50.0 + 30.0 * blink, 0.0)
            brect(x, 10.0, 1.0, 0.0, 0.0, w, 34.0)
            brect(x + 3.0, 13.0, 1.0, 0.0, 0.0, w - 6.0, 28.0)
            col(0, 90.0, 66.0 + 28.0 * blink, 0.0)
            daemon_text(msg, x + 18.0, 27.0, h)
        }
    }
}

# ───────────────────────────── main ──────────────────────────────────────────
bind SHOT_SEQ = [[0, 0], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [4, 0], [4, 1], [4, 2]]

bind start = do {
    bind args = get_args()
    bind full = 0
    bind forceSim = 0
    bind shot = 0
    bind ai = 0
    while ai < len(args) {
        if args[ai] == "full" { bind full = 1 }
        if args[ai] == "sim" { bind forceSim = 1 }
        if args[ai] == "shot" { bind shot = 1 }
        bind ai = ai + 1
    }
    if full == 1 { open_fullscreen("HatchSYS") } else { open_window(1280, 720, "HatchSYS") }
    bind W = get_width()
    bind FH = get_height()
    bind Hc = FH - MENU_H
    set_blend(1)
    set_frame_blur(0.45)
    audio_volume(0.7)
    audio_fx_reverb(0.55)
    audio_fx_delay(0.42, 0.45, 0.3)

    bind hil = hilbert_table(32)
    bind t0 = time_now()
    bind tprev = 0.0
    bind sec = 0
    bind view = 0
    bind auto = 0
    bind showHud = 1
    bind mute = 0
    bind pluck = 0
    bind alOn = 1
    bind alBase = [0.0, 0.0, 0.0, 0.0]
    bind alLast = [0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0, 0.0 - 100.0]
    bind alLeft = 0
    bind alNext = 0.0
    bind alCode = 0
    bind alText = ""
    bind alBorn = 0.0 - 100.0
    bind lastRead = 0.0 - 10.0
    bind lastSfx = 0.0
    bind autoT = 0.0
    bind prevDown = 0
    bind shotIdx = 0
    bind shotN = 0
    bind gsel = 0

    # sampler liveness: 0 = no data (simulated), 1 = live, 2 = stale; and the stamp last seen from each
    bind liveM = 0
    bind liveG = 0
    bind liveB = 0
    bind liveN = 0
    bind tsM = 0.0
    bind tsG = 0.0
    bind tsB = 0.0
    bind tsN = 0.0
    bind chgM = 0.0
    bind chgG = 0.0
    bind chgB = 0.0
    bind chgN = 0.0

    # memory: smoothed state and targets
    bind cats = sim_cats(0.0)
    bind tgtCats = cats
    bind total = 16384.0
    bind rates = [0.0, 0.0, 0.0, 0.0]
    bind tgtRates = rates
    bind names = SIM_NAMES
    bind ws = []
    bind pv = []
    bind ior = []
    bind iow = []
    bind i = 0
    while i < len(SIM_NAMES) {
        bind ws = list_push(ws, 100.0)
        bind pv = list_push(pv, 80.0)
        bind ior = list_push(ior, 0.0)
        bind iow = list_push(iow, 0.0)
        bind i = i + 1
    }
    bind tws = ws
    bind tpv = pv

    # gpu, board, network: the latest packs and the smoothed numbers inside them
    bind tgtGP = sim_gpu(0.0)
    bind gpv = tgtGP[GP_VEC]
    bind gpe = tgtGP[GP_ENG]
    bind tgtBP = sim_board(0.0)
    bind bvS = tgtBP[BP_BV]
    bind cuS = tgtBP[BP_CU]
    bind cfS = tgtBP[BP_CF]
    bind tgtNP = sim_net(0.0)
    bind nvS = tgtNP[NP_NV]
    bind hsAll = hs_new()
    bind lastHs = 0.0

    bind energy = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
    bind A = [0.0, 0.0, 0.0, 0.0]
    bind ev = []
    bind hist = []
    bind rot = 0.0
    bind hueShift = 0.0
    bind act = 0.0
    bind rdA = 0.0
    bind wrA = 0.0
    bind fltA = 0.0
    bind alA = 0.0

    while window_is_open() {
        bind t = time_now() - t0
        bind dt = clamp(t - tprev, 0.001, 0.1)
        bind tprev = t

        # ---- input
        bind mx = mouse_x()
        bind my = mouse_y()
        bind click = 0
        if mouse_down() {
            if prevDown == 0 { bind click = 1 }
            bind prevDown = 1
        } else {
            bind prevDown = 0
        }
        bind nsec = sec
        bind nview = view
        bind setv = 0
        if key_pressed("escape") {
            if sec == 0 { close_window() } else { bind nsec = 0 }
        }
        if key_pressed("q") { close_window() }
        if key_pressed("0") { bind nsec = 0 }
        if key_pressed("home") { bind nsec = 0 }
        if key_pressed("1") { bind nsec = 1 }
        if key_pressed("2") { bind nsec = 2 }
        if key_pressed("3") { bind nsec = 3 }
        if key_pressed("4") { bind nsec = 4 }
        if key_pressed("right") { bind nsec = (sec + 1) % 5 }
        if key_pressed("left") { bind nsec = (sec + 4) % 5 }
        if key_pressed("tab") {
            if VIEW_COUNT[sec] > 1 { bind nview = (view + 1) % VIEW_COUNT[sec] } else { bind nsec = (sec + 1) % 5 }
        }
        if key_pressed("space") {
            if VIEW_COUNT[sec] > 1 { bind nview = (view + 1) % VIEW_COUNT[sec] } else { bind nsec = (sec + 1) % 5 }
        }
        if sec == 2 {
            if key_pressed("up") { bind gsel = gsel + tgtGP[GP_N] - 1 }
            if key_pressed("down") { bind gsel = gsel + 1 }
        }
        if click == 1 {
            bind mh = menu_hit(mx, my, W, FH, sec)
            if mh >= 0 {
                if mh < 5 { bind nsec = mh }
                if mh >= 10 { bind nview = mh - 10 }
            }
            if sec == 2 {
                if view == 3 {
                    bind ghit = fleet_hit(mx, my, W, Hc, tgtGP[GP_N])
                    if ghit >= 0 { bind gsel = ghit }
                }
            }
            if sec == 0 {
                bind chit = card_hit(mx, my, W, Hc)
                if chit >= 0 { bind nsec = chit + 1 }
            }
        }
        if key_pressed("a") { bind auto = 1 - auto }
        if key_pressed("h") { bind showHud = 1 - showHud }
        if key_pressed("m") { bind mute = 1 - mute }
        if key_pressed("p") { bind pluck = 1 - pluck }
        if key_pressed("n") { bind alOn = 1 - alOn }
        if auto == 1 {
            if t - autoT > 28.0 {
                bind autoT = t
                bind setv = 1
                bind nview = view + 1
                if nview >= VIEW_COUNT[sec] {
                    bind nview = 0
                    bind nsec = (sec + 1) % 5
                }
            }
        }
        if shot == 1 {
            bind setv = 1
            bind nsec = SHOT_SEQ[shotIdx][0]
            bind nview = SHOT_SEQ[shotIdx][1]
            bind shotN = shotN + 1
            bind need = 170
            if shotIdx == 0 { bind need = 600 }
            if shotN == need {
                screenshot("hatchsys_" + SEC_NAME[nsec] + "_" + SUB_NAME[nsec][nview])
                bind shotIdx = shotIdx + 1
                bind shotN = 0
                if shotIdx >= len(SHOT_SEQ) {
                    bind shotIdx = len(SHOT_SEQ) - 1
                    close_window()
                }
            }
        }
        if nsec != sec {
            if setv == 0 { bind nview = 0 }
            bind ev = []
            bind hist = []
            bind autoT = t
        }
        bind sec = nsec
        bind view = min(nview, VIEW_COUNT[sec] - 1)
        bind gsel = gsel % tgtGP[GP_N]

        # ---- data: the snapshots, re-read about twice a second
        if t - lastRead > 0.4 {
            bind lastRead = t
            bind liveM = 0
            bind liveG = 0
            bind liveB = 0
            bind liveN = 0
            if forceSim == 0 {
                if file_exists("mem.snap") {
                    bind snap = parse_snap(read_file("mem.snap"))
                    if snap[0] == 1 {
                        bind liveM = 1
                        bind g = snap[1]
                        bind r = snap[2]
                        bind total = g[0]
                        bind tgtCats = categories(g, snap[3])
                        bind tgtRates = [r[0], r[1] + r[3], r[2] + r[4] + r[7], r[5]]
                        bind names = snap[4]
                        bind tws = snap[5]
                        bind tpv = snap[6]
                        bind ior = snap[7]
                        bind iow = snap[8]
                        if snap[9] != tsM {
                            bind tsM = snap[9]
                            bind chgM = t
                        }
                        if t - chgM > 8.0 { bind liveM = 2 }
                    }
                }
                if file_exists("gpu.snap") {
                    bind gn = parse_gpu(read_file("gpu.snap"))
                    if gn[0] == 1 {
                        bind tgtGP = gn
                        bind liveG = 1
                        if gn[8] != tsG {
                            bind tsG = gn[8]
                            bind chgG = t
                        }
                        if t - chgG > 8.0 { bind liveG = 2 }
                    }
                }
                if file_exists("board.snap") {
                    bind bn = parse_board(read_file("board.snap"))
                    if bn[0] == 1 {
                        bind tgtBP = bn
                        bind liveB = 1
                        if bn[9] != tsB {
                            bind tsB = bn[9]
                            bind chgB = t
                        }
                        if t - chgB > 8.0 { bind liveB = 2 }
                    }
                }
                if file_exists("net.snap") {
                    bind nn = parse_net(read_file("net.snap"))
                    if nn[0] == 1 {
                        bind tgtNP = nn
                        bind liveN = 1
                        if nn[5] != tsN {
                            bind tsN = nn[5]
                            bind chgN = t
                        }
                        if t - chgN > 8.0 { bind liveN = 2 }
                    }
                }
            }
            if liveM == 0 {
                bind total = 16384.0
                bind tgtCats = sim_cats(t)
                bind sr = sim_rates(t)
                bind tgtRates = [sr[0], sr[1], sr[2], sr[0] * 0.2]
                bind names = SIM_NAMES
                bind tws = []
                bind tpv = []
                bind ior = []
                bind iow = []
                bind q = 0
                while q < len(SIM_NAMES) {
                    bind base = 3000.0 / (1.0 + q * 0.7)
                    bind tws = list_push(tws, base * (1.0 + 0.3 * sin(t * 0.1 + q)))
                    bind tpv = list_push(tpv, base * 0.7 * (1.0 + 0.2 * sin(t * 0.13 + q * 2.0)))
                    bind ior = list_push(ior, 400.0 * pow(abs(sin(t * 0.3 + q * 1.7)), 6.0))
                    bind iow = list_push(iow, 300.0 * pow(abs(sin(t * 0.27 + q * 2.3)), 6.0))
                    bind q = q + 1
                }
            }
            if liveG == 0 { bind tgtGP = sim_gpu(t) }
            if liveB == 0 { bind tgtBP = sim_board(t) }
            if liveN == 0 { bind tgtNP = sim_net(t) }
        }

        # glide toward the targets so the drawing and the synth never jump
        bind k = clamp(dt * 2.5, 0.0, 1.0)
        bind cats = glide(cats, tgtCats, k)
        bind rates = glide(rates, tgtRates, clamp(dt * 3.0, 0.0, 1.0))
        if len(tws) == len(ws) {
            bind ws = glide(ws, tws, k)
            bind pv = glide(pv, tpv, k)
        } else {
            bind ws = tws
            bind pv = tpv
        }
        bind gpv = glide_ll(gpv, tgtGP[GP_VEC], k)
        bind gpe = glide_ll(gpe, tgtGP[GP_ENG], k)
        bind bvS = glide(bvS, tgtBP[BP_BV], k)
        bind cuS = glide(cuS, tgtBP[BP_CU], k)
        bind cfS = glide(cfS, tgtBP[BP_CF], k)
        bind nvS = glide(nvS, tgtNP[NP_NV], k)

        # the packs the views read
        bind gpD = [1, tgtGP[GP_N], tgtGP[GP_NAME], tgtGP[GP_DRV], gpv, gpe, tgtGP[GP_PROC], gsel]
        bind bpD = [1, tgtBP[BP_INFO], bvS, cuS, cfS, tgtBP[BP_DIMM], tgtBP[BP_DISK], tgtBP[BP_DIO], tgtBP[BP_SENS]]
        bind npD = [1, tgtNP[NP_AD], nvS, tgtNP[NP_NODE], tgtNP[NP_PROC]]
        bind fl = rates[0]
        bind rd = rates[1]
        bind wr = rates[2]
        bind al = rates[3]
        bind usedM = clamp(1.0 - (cats[6] + cats[1]) / total, 0.0, 1.0)
        bind memD = [cats, total, rd, wr, fl, usedM, al, names, ws, ior, iow]

        # ---- what the current section feeds the look, the pulses and the sound
        bind dr = drive(sec, memD, gpD, bpD, npD, A, gsel)
        bind tg = dr[DR_TG]
        bind used = dr[DR_USED]
        bind kk = clamp(dt * 2.0, 0.0, 1.0)
        bind rdA = mix(rdA, tg[0], kk)
        bind wrA = mix(wrA, tg[1], kk)
        bind fltA = mix(fltA, tg[2], kk)
        bind alA = mix(alA, tg[3], kk)
        bind A = [rdA, wrA, fltA, alA]
        bind act = (rdA + wrA + fltA) / 3.0

        # ---- look: rotation, hue and saturation follow the machine
        bind rot = rot + dt * (0.06 + 0.9 * act)
        bind hueShift = 10.0 * sin(t * 0.05) + 26.0 * wrA - 12.0 * rdA
        bind sat = 52.0 + 40.0 * used
        bind enT = dr[DR_EN]
        bind ne = []
        bind i = 0
        while i < 7 {
            bind ne = list_push(ne, mix(energy[i], clamp(enT[i], 0.0, 1.0), clamp(dt * 3.0, 0.0, 1.0)))
            bind i = i + 1
        }
        bind energy = ne

        # ---- pulses: in = circles, out = squares, allocations are circles in the section's allocation channel
        bind ev = ev_cull(ev, t, 1.9)
        bind hist = ev_cull(hist, t, 14.5)
        bind f0 = 55.0 * pow(2.0, 0.55 * used)
        bind nsp = 0
        bind rate = [14.0 * rdA, 14.0 * wrA, 9.0 * alA]
        bind src = 0
        while src < 3 {
            bind want = rate[src] * dt
            bind cnt = floor(want)
            if rand() < want - cnt { bind cnt = cnt + 1 }
            bind m = 0
            while m < cnt {
                bind kind = 0
                bind wts = dr[DR_RW]
                bind pws = dr[DR_PWR]
                if src == 1 {
                    bind kind = 1
                    bind wts = dr[DR_WW]
                    bind pws = dr[DR_PWW]
                }
                bind cat = dr[DR_ALC]
                if src < 2 { bind cat = pick(wts, rand()) }
                bind proc = pick(pws, rand())
                bind seed = rand()
                bind mag = 0.3 + 0.7 * rand()
                bind ev = ev_add(ev, kind, cat, t, seed, mag, proc)
                bind hist = ev_add(hist, kind, cat, t, seed, mag, proc)
                if nsp < 3 + 2 * pluck {
                    if t - lastSfx > 0.07 - 0.03 * pluck {
                        if mute == 0 { chime(kind, cat, mag, f0, seed, pluck) }
                        bind lastSfx = t
                        bind nsp = nsp + 1
                    }
                }
                bind m = m + 1
            }
            bind src = src + 1
        }
        # the scrolling views want four numbers a note: kind, category, born, magnitude
        bind h4 = []
        bind i = 0
        while i < len(hist) {
            bind h4 = list_push(h4, hist[i])
            bind h4 = list_push(h4, hist[i + 1])
            bind h4 = list_push(h4, hist[i + 2])
            bind h4 = list_push(h4, hist[i + 4])
            bind i = i + 6
        }
        # the history strips take a sample every half second
        if t - lastHs > 0.5 {
            bind lastHs = t
            bind hsAll = hs_sample(hsAll, gpD, bpD, npD, usedM, fl)
        }

        # ---- alerts: anything irregular, in any section
        bind gm = 0.0
        bind g = 0
        while g < len(gpv) {
            bind gm = max(gm, gpv[g][GV_UTIL])
            bind g = g + 1
        }
        bind alM = [bvS[BV_UTIL] / 100.0, gm / 100.0, lg(nvS[NV_RX] + nvS[NV_TX], 9.0)]
        bind lives = [liveM, liveG, liveB, liveN]
        if alOn == 1 {
            if t > 12.0 {
                bind code = alert_check(alM, alBase, bvS, gpv, lives, usedM, nvS)
                if code >= 0 {
                    if t - alLast[code] > AL_COOL {
                        bind alLast = list_set(alLast, code, t)
                        bind alCode = code
                        bind alText = AL_TEXT[code]
                        bind alBorn = t
                        bind alLeft = 3
                        if code <= 4 { bind alLeft = 4 }
                        bind alNext = t
                    }
                }
            }
        }
        bind ar = clamp(dt * 0.15, 0.0, 1.0)
        bind alBase = [mix(alBase[0], alM[0], ar), mix(alBase[1], alM[1], ar), mix(alBase[2], alM[2], ar), alBase[3]]
        if nvS[NV_RTT] > 0.0 {
            if alBase[3] <= 0.0 {
                bind alBase = list_set(alBase, 3, nvS[NV_RTT])
            } else {
                bind alBase = list_set(alBase, 3, mix(alBase[3], nvS[NV_RTT], ar))
            }
        }
        if alLeft > 0 {
            if t >= alNext {
                if mute == 0 { alert_beep(alCode, alLeft) }
                bind alLeft = alLeft - 1
                bind alNext = t + 0.22
            }
        }

        # ---- state, synth, picture
        bind zl = liveM
        if sec == 2 { bind zl = liveG }
        if sec == 3 { bind zl = liveB }
        if sec == 4 { bind zl = liveN }
        if sec == 0 {
            bind zl = 3
            if liveM + liveG + liveB + liveN == 0 { bind zl = 0 }
            if liveM == 1 {
                if liveG == 1 {
                    if liveB == 1 {
                        if liveN == 1 { bind zl = 1 }
                    }
                }
            }
        }
        bind zCats = enT
        bind zTotal = 1.0
        bind zrd = pow(10.0, rdA * 3.6) - 1.0
        bind zwr = pow(10.0, wrA * 3.6) - 1.0
        bind zfl = pow(10.0, fltA * 5.0) - 1.0
        if sec == 1 {
            bind zCats = cats
            bind zTotal = total
            bind zrd = rd
            bind zwr = wr
            bind zfl = fl
        }
        bind Dp = [gpD, bpD, npD, hsAll, memD, [liveM, liveG, liveB, liveN], lastHs]
        bind Z = [t, rot, hueShift, sat, W, Hc, zCats, zTotal, act, zrd, zwr, zfl, ev, names, ws, pv, ior, iow, h4, used, energy, hil, zl, FH, Dp, dr[DR_STAT], mx, my, sec, view, enT, CH_ALL[sec], [alText, alBorn, AL_SEC[alCode]]]
        bind level = 1.0
        if sec == 0 { bind level = 0.5 }
        if mute == 1 { bind level = 0.0 }
        if pluck == 1 { bind level = 0.0 }
        synth(Z, f0, level)

        fill(0, 0, 0)
        if sec == 0 { view_main(Z) }
        if sec == 1 {
            if view == 0 { view_rings(Z) }
            if view == 1 { view_tiles(Z) }
            if view == 2 { view_orbits(Z) }
            if view == 3 { view_weave(Z) }
        }
        if sec == 2 {
            if view == 0 { view_gpu_engines(Z) }
            if view == 1 { view_gpu_vram(Z) }
            if view == 2 { view_gpu_dials(Z) }
            if view == 3 { view_gpu_fleet(Z) }
        }
        if sec == 3 {
            if view == 0 { view_board_layout(Z) }
            if view == 1 { view_board_cores(Z) }
            if view == 2 { view_board_signals(Z) }
        }
        if sec == 4 {
            if view == 0 { view_net_flow(Z) }
            if view == 1 { view_net_links(Z) }
            if view == 2 { view_net_traces(Z) }
        }
        menu_bar(Z, sec, view)
        alert_banner(Z)
        if showHud == 1 { hud(Z, sec, view, auto, mute, pluck, alOn) }
        present()
    }
}

← Back to index