theredbird__

<body><script language="decker">
{deck}
version:1
card:0
size:[512,342]
locked:1
name:"real.deck"

{sounds}
sound1:"%%SND0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUHCQsQDwQFDAj3+voE9eXw6e79CfYTAgMQEQoVCfsJ9A0C+fH47Ob39uL7CPv4BxULGAgICP79Ee\/1\/PAD7O\/jB+\/2\/gr4Ef8FDwQQCg8F9P8C8PfyAe\/97+T6Ae0L\/gIPCR0BHBAM+QX1DgLz7v7e9\/PtBvMN\/xH3DhQXFv8GA\/7\/BgT\/6O\/q6wfsBfEG+QkB+x0DHgoQBA35+wjo8vgB6vr7A+sH8Q4P+B0RFQL9E\/gD7wTw\/u7x9Oj78\/L3Awb\/E\/sbBhUCFPwM+gbu9u7w8vP68wPyCAIADgQKCQcKFAMNAwb5\/+4A\/ej38P\/1CgMAAgL9G\/4PAAf4+\/kI8PHu9erq9fTz+gX5Bw4YAwocDw\/+\/gf88e0B8vP36QT0\/QACBRb9FgwHBAj6\/wkL9fMI4\/fy9fb0\/P8EAwUQCgkaDQcICPr\/AfcB9+b67vQHA\/z\/BAELCQQc\/BEGAwH5\/f3y6vr59+wC+QL2\/xMGDRYJBhUFCP76A\/EC+QDq\/vb++AT+AgoCEgMVDBb7BgX4\/vUF+e3x8fj6\/wD4Bf8KAAIRFw8ECgP5APL5+fvr8O769\/MCAvgPBwUCEBkNAfsS8AP+\/QHn9fcA8gDvBAUDBg4LFgYFFf0L\/Pz5\/uzvAPbx+wcH\/wH7BAAXCA4DDf0JAv\/+7Pzz\/PPo+PUK9Qn\/BwAKDAoOFAgJAwYC8fj67vbu+foA\/AsECA4GFA4JEgAC+Arz\/vj77ezw+f4BAf8MBAAJCAsWE\/8DAAEC\/\/3z\/fP2+fT8AP4ECfoECg4HEgwGBP7\/Au\/v9vP87Pv3B\/8I\/QENBxUHCgoPBAf2BAHxAPLs+PP+\/vQC\/AQSBBAIEgQFBgEEAfH68\/Tv8gL78f79\/wYMAwILBRIIDAAFAfj7Afnx8\/H3Afr+AwQFAwwRCA0BBgL9BgEC\/\/Pu\/OzvAgH+AQP+CgMEEgQRBgkFA\/z9\/vzv8vHw9QAACAT8AgMVEgUMBQj4A\/77\/PT5\/\/Lx7\/34+foJCgcMFAgFEAUAAAP4+vby9frx+\/74+AsNCQEJChAMCgX\/BwX7+vf29vz49v\/3AP7\/B\/4BDwkKCwMGAvn7Afrw+\/X79vn7\/\/0EAgsGBwUKDQUPB\/sA\/Pb58Pb6\/vrz+wIDDAEGCQ0IDgQFBPgD+vj6\/+719f7\/\/QAI+AgECgYKDwcDAAUDBf\/x+vz0+vn3AAUCAgQHDwUJBwT\/AQkBA\/329Prz9Pr69v79BQH9BQ8ICA0GBQwB\/PwA+fP49vL2+PsEBQsBBAkFDxEH\/gcA+gUA9PP58u\/2Avv3AAoECwgJDQcGCv0A\/wD5\/Pvz+fj3+wAAAQYGAwoKBwoLB\/sDB\/z2Avn0+PP2+wD59gcIBAsJBgYOBQAG\/vz++Pn18\/f6\/ff+AAADCgcBCwYKCAIJ\/f\/\/\/Pn9+fX79Pz6BP8ABgcJDhAIAgMFAvkD\/vf1+fP2+ff8BAkAAAQGCAwQBwUHBf4B+fr7+fX1+AH6+P7+AgcFCAcHCAwG\/AAAAPvy\/Pj28v36+v8HCQIJBgQJBwMGAwP+AP789PL39\/P+Avv+BAUDBAQNDQMDCQH3\/fj\/+vr69\/n\/A\/oAAAUJBwIGDQoCAAUBAf\/\/9vT69Pn4+f39AQQBBg4HCwsHAgIAAQP89fn8+\/T7+gD\/AAEDAwQGDAcIAgAFAvz49\/f49\/z4+P38\/gH\/CAwHBggGAgYA\/wH9\/PX6\/vz0\/QYAAQcEAQAICQkDAQcB+\/75\/P76+foA\/vv3\/wYHBgcGBggMBAAAAQD8\/Pj5+vn69\/f7\/gAHCQcEBwcJBQECAP8BAwD69\/n7+v7+\/QACBAYIBgkGAgADBQH7+\/77+fb3\/f39\/P4EBAUHCgoIBQMABv7+\/Pn89\/f3+P4B+fwDAgMDCAsKCAYCAf8B\/vz5+vn5+vj3\/QMDAQIGBAIFCgcCBgX9+v\/\/\/Pj7+Pb8\/v\/+BAQDBAYECAcEA\/\/\/+\/z7+\/z9+Pn6\/\/\/\/AwQGBQoIBwYEBQT++\/3+\/vr4+fj4\/f8FAwAEBwgHBwgIAQIF\/\/j7+vf6\/vj1+wD\/AgMAAQcJCwkIAwL9\/wH\/\/f39\/ff1+QACAPz+AggIBQUFBAMDAP\/8\/fv59PX7+\/wAAQEDBAgJCAYFAgIDAf\/+\/vr7+vf4+vwBAAMDAQECBQYHCQcBAf\/++\/r7\/fz4+\/3+AAEBBAcHBgcFAwUDAP39\/\/\/9+ff3+fsB\/v4AAgUICAUGBwQEAv\/9+\/38\/fz49vr+AAD+AgUIBQMEAwQFAgMC\/\/z5+fv++\/j8AAEAAQQEBAQGBgUCAAEA\/Pz7+\/r5+Pr9\/wD\/AwQFBgcLCQUBAQAA\/fv8+\/v89\/f9\/v8CAwACBQYEBQgFAQIA\/v7+\/Pz9+vn6\/wD+\/wMDAwcJBwUDBAH\/\/v79\/fr5+fr8\/Pz+AwYEAwMICgcEAQABAv75+v389\/f7\/f4AAAIFBgQEBQUFAQABAP4A\/v36+\/v8+vz+\/\/8CBQcGBQQDBAUC\/v7+\/\/78+vj5\/f39AAMDAwQFBwgFBAQEAf39\/\/v4+vn4\/AACAf79AgcJCQcGBQMC\/\/7+\/f38+vn6+vv+AAMCAgIEBgYGBAMDAv\/+\/fv5+vz9\/f38+\/0CBAQDAwQHBwUDAQH\/\/v39\/vz6+fn8\/gADAwECBAYFBgUEAP\/\/AAD\/+\/n5\/Pz+\/\/7\/AQMEBAUHBgMBAAEC\/\/z6+\/z8\/Pz+\/\/8AAgUGBgYFBQQDAf\/\/AP\/9\/Pv8+\/v8\/gAAAAMEBgUFBQMDAgAAAAD++\/n7\/Pz8\/P4BAQEBAQQGBgYFAgECAP\/9\/Pz6+Pn9\/gABAQABAwQFBgcGAwEAAP\/9\/Pz8+fn6\/QABAAEDBAQDBQYFAwH\/\/\/8A\/\/v5+vr7\/f3\/AQECAwQFBgYFAwIB\/\/7+\/fz8\/fv7+\/7\/AAEDBAQEBAQEAwIBAQIA\/v39\/fv5+fwAAQEAAQUGBgYFAv\/\/\/wACAwMEAwMGCggHCAwJAvkAAv77Av4A8fv79Pj78\/b75\/b19\/QC+fn\/+wQJ+wAJCQsHCQ4JFw4ODg4JCgD\/AQYDBAH5\/gD0+u\/xAPDv9vfy9O\/69\/X8+ff9Av0HAAAHBwsMDAUQCgsJCwwUBgALAgUG\/P39\/\/37\/vvx+vjw8fPu9\/X19\/sC9v74AQICBAUEBAwDAgsPEgkICREMCwQBEf8AAwADB\/f4+f76+vXz7\/P48un2\/Pvy\/wEC+QUCBQwG\/wEFDQgIBggJDRIECA8NAwEC+gT\/\/fz8\/\/f29fr48\/Pv+PT0+fv4\/gL8+vsCBAEGCQcEEQkCCwwHCwsJCQYKBgIHCwH7APoB+fn2+fz18\/n17fP49\/T7\/fr\/\/fwCAwD\/BwcMCAkCBwsMBwsPDAQFBwz8BgL9AAT\/\/fkA9Pb2\/Pb19O729\/T8APX3BAX+\/QIGBwcEAAcLDgwMDwwPCwsKCgcF\/AD\/\/PsB\/P7+9vP29vvv8u\/z+P349vv\/+\/sB\/wEFBwD+CwkHCwkCChMJDgsECAkEBAUDBAP8+vr6\/\/r1\/f368u\/49fX29\/X5\/gD7\/gQH\/wEDCwgHBQwRCgQODgoICAsGAAEEAv78+v349\/r58\/b67\/Xz9fT1\/v3w+gH8\/AQCBgEJCAQDCQgHCAoKEhANCAcKCQUEA\/8AAPr4+Pj5\/fL1+vnz8+\/19\/r99\/YBAv8EBgoBAAgHChAIBAoJCQ0JBwcDCQH\/AQb+\/voAAPv48\/b7+fTx8\/b3+fb5\/vj5\/\/z+BQECBAQHCwcJCggOCwQHDQ8IAgEFBgQB\/AH+\/vv78\/j6+\/Ly9vn4+PT2+gD8+wIFAf4ABQUGCAgEDAoICwwMDw0IBgMFBP76\/vr7APr3+vb39\/b09fr08Pb6+vv++wEFBP0ABQkHBAYIDAkKCQsRDAkIBQYEAAL+AP\/8+v3++\/369vj49PP18vj6+vr9AAD8AAEBAQUECQkIBQcLCwYICAcGDQgDAgQCAP3\/\/f39\/Pr8\/Pn49u\/z+\/r19Pr++\/n+\/QACAgUDBwUGBwkICQsKCQ0KBwYGBQUDAgD6+\/r6+P36+PX0+P349\/L09\/v9+\/j\/BQP8AAUDBQcHBQYKCgcLCwkMBwUGBAECAv8BAQD99vkB\/fb39\/X19vb2+Pr7+v0B\/wABAQME\/wMDBgQHCgsHCgwLBwgIBgYEAf\/+\/\/z6AP759\/T2\/Pn09ff2+Pn7+\/z8\/P0CBQQCBAEHCw0JBQkOCgsNCAUJAwIEA\/8BAP\/++\/339\/r7+fX19Pf49fb3+vz9\/wD8\/wIDBAgHBAMGCgsMCAUIDgkCBAcGBgL8\/QH9+\/v8+\/z59\/X6+vf19vf7+\/z7\/QAEAv4BAwMIBgQFCQoFCAsLCgkFBQYFAAMDAf7++vz7AP359Pn2+vv48vP3+fn8\/wH9\/gADCAYBAgYICAcDBgkLCAcJCgMCBQQEAv76\/P7++Pj8+\/n59\/X29\/n49\/r9\/\/8AAQQEAQIEBQkIBQUICgsGBwoJCQgCAQMAAAIA\/\/79+vj4\/Pj1+fj3+ff0+Pv5\/f8BAP8DAv8EBwcGBwgJBwgICgsKBAQGBQQDAP3\/\/\/z8+\/v7+PX19vr69fT2+fv9+\/z\/AAAAAAQCAwQFBggHBwkKCAgGBwYHBwL\/AAEA\/\/36+\/38+Pn9\/fb09vj7+vf5+\/7\/\/wD+\/gIHBQUGBgQHCQgKCQoIBggIBAQBAAEA\/f3\/\/fz7+vr5+\/r19ff39\/r7+\/7+\/gEEBAIAAgMFBwcEBwgICAkIBwYHCAQEA\/\/\/Af8B\/vv6\/f389\/b3+Pf39vj6\/fz8\/f8BAv8BBAcGBgYFAgYHBwkMCgkFBQUEAgH+\/gIB\/fr7\/Pv5+Pn7+\/f1+Pv6+vr5\/P4AAQACAQEDAwQICQYGCQkJCAcHBwYEAgMEAv\/+\/Pz8+\/n6+\/z59\/b29vj59\/j9\/\/\/\/\/wICAgACBQYEBgcHCAkICAoKCAYDAgADAwD8AP78+vr7\/Pn39\/j49\/T3+fz+\/vz8AAMCAQIEBAUGAwcKCggJCAkKCAQDBAYC\/\/\/\/\/v\/9+vr8\/Pr3+Pn49fX1+\/79\/P39\/wABAAIFBQEBBggICAcHCgsJBgcGBAEBAAAB\/\/z8\/Pz7+vj4+vv39Pj8\/Pr6\/P4AAP4BAwQDAgIEBggGBAcIBwoKBwQFBQQAAQL\/\/f3+\/\/\/6+Pn7+\/r4+Pf4+Pj5\/P7\/\/\/7\/AwQDAAIEBAMHCAgJCQUHCggFAwICAAEA\/f39+\/v8\/Pr6+Pj4+fj39vr8\/v38\/P8CAAECAwQGBgYFBgcHBwoKCgkFAwQEAwD8\/v\/\/\/\/z6\/f35+Pj6+\/n39\/n7\/Pv9\/wAAAQABBAUFAwQHCAcGBAcLCgYFBAMCAgEBAAD\/\/v\/\/+\/v6+fr7+fj4+fj4+v38\/v7\/\/wICAwMCAwYGBgYHCAgGBwUFBQUCAQICAP7+\/v8A\/vn5+\/v6+ff4+fn4+v0A\/v7\/AAEBAQEDBQUGBgYHBwgHBggIBgMCAwQDAP79\/\/78+\/z7+\/r4+Pr7+vj4+vz+\/v7\/AQEAAQICAwIDBAYHBwYICAgGBAEDAwMBAAD\/\/\/\/9\/f3+\/fv6+vn6+fj5+\/z8\/P7\/AAAAAAIEBAQFBgUFBgcHCAcGBQYEAwD\/\/wAA\/\/38\/f38+\/v7\/Pv3+Pn6+vr6\/f8A\/\/7\/AgMDBAMEBgYFBgcIBgYHBwYFAgABAgIA\/v39\/v38+\/z7+fr6+fn6+\/v7\/f7+\/wABAgIBAgQEBAUFBQYGBgUGBwYEAwIBAQEA\/\/\/+\/Pv8\/Pz7+vj5+vj3+Pr9\/v79\/v8A\/wABAwQEBAUFBgUGBwgIBwYFAwQCAP\/\/\/wD\/\/f39\/f36+vv6+vn4+vz9\/Pz9\/wABAQECAwMBAgMFBQYFBgcHBgUFBQMDAQD\/AP\/+\/v39\/Pv8\/Pz7+vn5+fr8\/P3\/AAD\/\/wIDAgIDAwQEAwICAgIDAwMDBAcICAkKDBALBw4PDQkHDAwICAICCQb6\/gQB\/\/38+fv\/\/fL4+\/j6+fL2\/vnz8\/P69vHz9PP37uzz+PL18\/Hw+ffz8v3++\/n89wIJ\/\/0FCAUCAQUFBgoFAw4KBgkKCA8NDQcKDQ0HDQsNEBAFCRASDAoFCQUEAAAACQH+\/v\/\/A\/z69vz++vX6+Pr\/+O\/0+fj09ff29fTw7fn78+\/z9Pf38+\/4\/f36+v79AAP7+gEDAQEEBgcHBgUHDwsBBQsLCgkGCQ0OCwsNEQsLCwcNDwsICwwLCgcFAwMJAPwBBQH++fn+Afn7\/P\/59\/f38fr78\/L59\/fz9fb29vnv8fj38\/bu8vn58\/f7\/vz5\/f7+AgL9AwkFAQMEBgYJBQMJDwoHCAoMEAgICgwLCwgLDA8RDQYODgsIBgUHBwUB\/gYJ\/\/z+\/f3++fr+\/fv4+Pr4+vr39fz48fH09fb18\/D2+\/fu8fX09fbx+Pz7+Pr8Af7\/\/wAECgD\/AwcFBAQGBgoLCAUICQkMCgUMDgwJCQ4PDAoKDBEOBgYMCgkHBQQIBgT6\/AED\/f\/5\/AAA+\/v4\/Pn39vf5\/vbx9fv39PT09vj18\/Dz9fPz9PP3\/Pj4\/P79+\/v\/AgIC\/v0HCAMDBAUICQQEBgoKBgcJBw0MCgoODQsICgwNDAsGCg8NBgcICAgE\/wAEBQH8\/f\/9\/\/z4+f379\/n6+\/j39vb6\/fTz9fb3+PLz9\/v49PH49PX19fT4+vn6\/v0BAP\/9AQYH\/wAGBwcFAQULCwcGCAsJCAgFBwoKBwoNDgsJCQ0NDwkGCgwIBgMBBAcC\/v8DA\/78\/fz7+\/j3\/f37+Pf4+fr59fT6+vf39fX5+PPz9fj28\/P19\/r69ff\/\/v38\/gEDAQD+AwcHBQYDBggHAwYJCwcGBwoJCwoHCw4ICAgLDA0JCAkNDAcFBgQFBgH\/AgMA\/f0B\/\/78\/Pr+\/Pj4+vn59vb3+\/v59fb39\/f48\/b5+PTz9fj39vf5+v76+Pz\/\/\/\/9\/gIEBAD\/AgUFBgUDBwkIBgcJCggHBggLDAcGCwsJCAcKDQ0JBgQGBgUGBQMGBAEAAQAA\/Pz8\/f79+Pv9+\/j49\/v8+vf19vj29fj3+Pr49\/n49\/T09ff5\/Pn5\/QD8+\/0AAgQA\/wEDBAMDBwUHBgQFCgoHBAYICQkJBggMCwcHCAoMCwcICwsIBQYHBQQCAAEDAP3+\/wD\/\/vz7\/P76+Pr7+vv49vn7+\/n39\/j29\/X19\/j19ff4+Pj4+fr8\/Pn7\/wAAAP7\/AwMCAQMGBQMDBAUGBAQGCgkIBwcHCgoJBQkLCggJBwkLCggHBggEAwMDAwL+\/gEC\/\/7+\/f7+\/Pr6\/f78+\/r4+vr39vf5+Pb2+Pj49vT1+vn39vb3+vr6+v0AAP8AAAABAf8AAQMDAwUGBgUEAwcKBwYHCAgIBwcICgoJCQoJCQkIBwgIBgQDAP36+\/v9\/wD\/\/\/8CAf8AAgMGAv8EBgUDAQUGBgUDBAoI\/wEFBgYFAQIFCQYAAQUEBgX+AAYHAf39AAD9\/P39Afv1+v38\/fv3+Pz89\/L3+vn49\/P6\/fr29vj59vX19vj58\/H6+vj39vb7+vjy9fn59Pj29\/v89vX3\/Pn49fn5+fX09wD7+fr6+v\/7+Pb8AP34+\/v9Av77\/gECAf8BBAUEAf8ICwUDBQcMCgUCBwsLCAkKCQoMBgQHDAkJCAsNCwkHCRAOBwgKCw4LBggMDgwJCQwKCwoEBgkIBwgKDAsKCAcJDQcDCAwLCwUDBwwIBgYKCAUDAv8CA\/\/9AgID\/v3+AAAA+Pn+\/\/389vj9\/vj2+Pz69\/f29\/r58\/b6+vj29ff3+vbz9vz6+PT09v339fX4+Pf0+Pb5+\/n0+fr59\/b3+fn59vb7\/vv59\/f7\/ff1+Pv8+fn9\/f\/\/\/f4DAv7+\/wACAgEBBAkHAwMEBgkHAgYICAUFBwoJCQgHCg0HBQYJCQgGBwgMDAgFCAgKCwgFCwwLCAcJCgkICAkNCwUDCAgIBwYFCQoJAwIGCQcIBAYJCQYGBQgFAwEAAgYB\/P4CAP78+\/4A\/vr4+fv6+vv5+\/78+vr6+vj29\/n7+vXz+fr49\/b2+fn29Pb4+fX29\/f6+vj5+vn39fX4+vr49Pb7+vf39\/r8+fX2+fv59vn6+vz6+Pr8+\/n5+\/8A\/\/z8AAT\/\/wEBAwUAAQQIBwIACAYGBgQFCAYEBQkICwkFAwsOBv4LDAYKCAENDQcHBg0MBAcJAwkKAgUOBgwHBwUMCAv+CA4GAg0ABwwLAAYGDgUCBQkBBQEBBg0BBQECAwUC\/\/kEBv39Avn+BPn3\/v7++fX9+\/f79\/IB\/fX5+Pn9+\/f29QD89\/v79Pz88fL7\/Pry9vz69fj1+AD58Pv2+Pz59Pf6Afn3\/Pz2\/frx+fz69\/f6Afn5+\/n8BPX4\/wD9APn+\/wUCAAAIAQEDA\/0JBgIABQYLAQQHCAkL\/QUMBwUIAgYLCgYDBw4IAwoEBg0IAwcJCwkEBwgHDQb\/CQ8GBAQECgwGBAUHCQQDCQUHDAYCBwgHBAEGBgYHAv8LBwAAAQAGA\/z9\/wAA+fwB\/AAB\/PwB\/Pr4+fz7+vv0+gH78ff5+\/358\/r6+fX1+f\/2\/Pr4+\/\/29vb6+\/n1+Pb8\/\/Xz+fn6\/Pb1\/f369vX+\/fn6+\/sA\/PX4\/fz++\/v5\/P\/\/9vr\/\/\/8A+gAEAwD\/\/wcE\/wICAwkE\/wQIBgYFBAYHCQT\/BQkFBgYDCQwGBQQJDAUDBwkICQIDCwsGBwcHCAkGBAIKCQMFBwQLCAMFCAgJAgQHBwYF\/wULBwIEBggHBAIABAcE\/gECAQT\/+v0AAv76\/AL9\/Pn4\/wP6+fv8\/P33+fn+\/\/r2\/Pr7+vf1+fr6+Pf7\/fr49vj\/\/fT3+\/v8+fb5\/P37+fr9+vn69vf6+\/n5+v4DCAP+AAEDBQMCAAMIB\/4CBwYLBP4PCgQHAQkPBAcGBRAM+wcMAgwHAgQKCA36BAkFBA35Aw0G\/wP5CQL9\/AH9Bvrz\/wX0Bfz3+wH7++\/9\/ff2\/+\/\/A\/L2+\/j++fD5+ff+8u0D+fT7+PQC+fj09P757\/v69f4B7ff7+\/z38v769\/fz9wf39QD7+gP69vn7BPvz\/\/\/5B\/30AQMAA\/7+CgQCAvwKEv4BDAgJDf8ECQsMCAMOCQgNBf8LCgcICQgTBwgHCBES\/QoPCgwN\/woPDAoJBREJCAwBBQwGAgwICw0FBwgHEQX8ChAGCgQACg4DBggLCgP+B\/sEBfv5B\/8CAPn+A\/0C9vQFAPn99vYD\/vP3+\/798fb69vz67vcB9vj28vj5+fjw9gP48\/jz+AH28Pn5+\/nw9\/z1\/vnw+gD0+Pb0+fv2+fL+A\/b2\/ff8APP0\/\/39+PUAAfz\/\/\/oJA\/gAAv8CAAEBBQkL\/AIJBgcJ+wgOBgMFAg8IBgcICRIEAAoLBgcDBwsLDQgACA0HCggADhAHBwUJDwgCCAkNDwAADgkGBwQEDAgIAf8HCwMHBAMOCgMGBQYJ\/v4CAwYC9P4J\/vr9+QAB+\/n3+fz69v35+gP59vz8+\/nz9v\/6\/PTu\/AHy9vn3+\/zx9vb7+fLy\/ff6+\/X2\/\/f49fT6\/\/n58\/QB\/vD4+\/v++PH4\/Pz58vcA+vz79PkA+\/j7+gMC\/f38AAr\/+QMFAgj\/\/wcIBgP8BwsFBAQBBgoCBAcIDAoGBgYLDwEBDQwICAIGDwsGBgUNDAMGBQUJBwEHCwkLBQUJCQoI\/wcOCQQGAAkMBQEEBgwGAAQGAwT\/\/wgJAwP+AQUDAP34Awb\/\/Pz6AgH29\/z+APj1\/Pz6+fP1Afz49\/f6\/\/j39Pf\/\/vn69\/f9+fH1+v358\/T9+vj38voB+PT39\/r99vb4+wD7+Pz7+v329Pz9+\/n1\/AD7+fn4AAP5+v8AAf77\/wMEBAABBwQCA\/8CCQcCAQMJCgIDBgcMB\/4FCggGBAEJCwoGAgYNCAUGAwkNCAQGBwwIAwQGCAwEAQkMBgQBBQsLBQQCBgkEAwYFCgsGAwcGCAMBBAYHB\/8ABwX\/\/vwBBgD8\/P0A\/\/n8\/v4B\/\/v+\/\/z69\/j8\/Pv59vsB+vX19\/3\/9\/b5+vr29Pv8+\/v4+P79+Pf2+v359\/f3\/f729fj5\/vv1+P39+\/b3\/v77+vn8Afz3+Pv+APz7+vwB\/\/r8\/gACAPwDBAMB\/wEHBAICAgUJAwEEBggIBQUGBwgDAAUHBgcEBQsJBQQECQsFBAYHCQgCBAkICQgGCAkHBgIECQgEBQMGCgYDBAUJBwIDBgYGA\/8FCAYEBAUIBgEA\/gMGAv7\/\/gEC\/Pr8\/gD9+f3\/\/fz4+P8A+\/r5+\/78+Pj5\/f76+fv5+\/r29vj5+vj4\/P36+fb6\/\/v3+Pr8\/fj3+vv9\/Pr8\/Pv7+ff5+\/r5\/wcFAQEABgkFAwMGDQsCBgsMEwQEGg4JDQIQFAEHBAEQCPEEBPsJ\/vT+\/v0D7PX+9fr\/6PoG8\/H37QH66\/T18QHv5fr77P707vz7+PrtAAH3+wT0Cwz1\/wYDDgT5CwkFEf\/+GAoHDwsJGQkKCgUWD\/8QDgkXE\/wMEQwQA\/4SBgEE+QES+vsE\/gII9Pj7+gX67P379ATx6Pn69fju8gL08vTp\/ALp7\/r0\/Pvp9\/77A\/31BgH+Bvn5BwIABgAIEgEEBgUTEPoLEQoRCv0QEgwODAcXCwoQAg0SBQQOBg0N+wIGAQz88gYI\/AL19AYB9Pv7\/gD08fzu\/Prp8P7z\/PTq9fvz+uvrAfjw+O\/2Bvrx\/f8EA\/P9A\/4JBPYGDgEHAfwICQgJAAkYBwUMBA4XAwUPDRIM\/w0SCRMNABAPAgcAAAYD\/QL6Bgv2+QH5AgHu9wP7\/fPuAP30+PbyAfbr9vP19+7w9fX7+ujx+\/f9+e0BBPv7+vkLAf0CAwYQ\/fwJBwcG\/AcOCg4H\/gsQCA0HAxYRCAsHDhYIBA8OERD8AQ8EBQP6Agr\/APj1\/wH3\/vf6B\/z1+\/f8\/u7y\/Pj89en4AfLy8+78+u\/y7\/P48\/H8+P0G9\/kEAQMA9gILBAb9+Q0N\/gUHBhAK\/ggIDQsBBREJDg4CChQKDAgFEhQKCQEEEgj6BQUGCvv2AgEB+\/L7Bfr9+PD6Afb09fQC+\/T28vgB8e349\/b67PH89\/Xy7f3\/9\/n59f8B+P4BAgsCAAQECQz8AQ8KCAb9CRIIBgYGEg4CCQgIDQgDDg8OEAYHDgkKBfwHDgP\/\/\/oGBvn5\/P8F+fL7\/Pn48fQA\/Pn48fj+9\/Xy7\/3+9fTz8\/737PP5\/P\/z9AH+\/vz1\/QkB\/\/\/+BQoBAwEFDwwFCQYIDwX\/CgwNCQEHEgsJBwIPFAYGCAgMCgAEBggLAQAGAgAC+PkEAfz59P4B+Pb29gD+8vX59\/r08fn6+vnx9v329PXv9v\/59fb4AgD2+v4BB\/\/4AwYDAv3\/CwkGAwAIDgUEBgMNEAcGCAoQCQMHCgwPBQQQDgcF\/wcNBwD\/\/gQF\/P0A\/gQB+\/z\/\/f\/29fz8+\/ry9\/\/58\/Ty+\/\/08\/X2+vfw9\/n4\/Pf2\/v37+vb7BAH+\/fwFCv\/9\/gILCf4CBwcHAQELCwkJBQgQCwYGBQ0QCQcHCBAOBAQGBwsE\/QQHAwD6\/QYC\/fz6\/wT69\/n7AAH5+Pj5\/vjy9\/j5+vTy+\/r39fH3\/fb09fb8\/\/f5\/f8CAf4BAgIC\/P0FBQIEAAUMBgECBAwMAwQICAsIAggNCgsICA4NCQgECAwHAwQBBgj\/\/QABBQH5\/QD+\/vn2\/wD7+vj8APv19vb8\/\/f19\/f7+fLz9vj69fL5+\/n59vkCAPz9\/AIGAP0AAgcIAgMFBAcD\/wMGBgcEBgwKBQUECg8IBQgJDQwEBQoLCwgEBwYDA\/\/+AgH+\/vwABQX99fr+AAgF\/wEDCQ4A\/wUAB\/\/tBgXx+fLvCPHq+uf1AePm+ur7AO72BPcI+vELBPsQ\/vsbDPwPBRAdBgMUAAoQ8QAP9wT\/9P8D7\/vu6wPw5\/np7wbv6v\/4Agnv\/w3+CAj2EBQAEQoHGhMFEQQKGAD7D\/oFD+\/1A\/gA+Ob7Aevz7uYA+Of99foM+\/EI\/QkP\/QITAxAS+gsUCRMNABgQ\/wf\/\/hT98Qb6\/Qnt7wD3+P3s9f3q+PXnAfz0\/v75EQL3CAMKGAADFwwQGAANGwcMCf4LCvcD+\/II+Ov+9fYE7ej58vX55fUI9f7\/8AcP+QIGBBMI+w8ICRkGARgPCxT7AQ8DBgHwBQnz+\/nsBP3o9fb1\/+rm\/vb5BfP2Df4BBPYEDQIKBwAYEgARCwsbC\/oPCAcM9vgN+v8B8vcG7\/X16vn25\/Px9Ab47AL+\/g35+BAJCAr7CxkECxEHFBUACAoBDQH2BQL+CfTwAfv3\/uryA\/Lw8ej7AO\/5\/\/wJAvIGBgIMAP4RDAoPAAcWCw0O\/g0S\/wIC+goB7v7++gPy6v\/58Prs7AD29fnv\/QT4\/QT7Dgv8CAsGFAb\/EhMNFAIFFwYAB\/0CC\/n3\/fX\/+ez5+fL\/8+f3+PP87\/II\/\/4A9QIR\/QALBA8S\/ggNDBMJARMNBQ39+QwD\/QD0\/An49vjv\/v\/p8vry+vfq+wD7Afb3DAUACPz\/EQgDCwQRFgcNDgcSCvgHCf8H+\/QGAPj68PQD8\/D27PL57PP8+AIB9QEEAQsA+g8OBQoBBBcNBw0IEBP\/AAgABAL0\/wT6Affw\/v70+e7xAffu9\/D6Bff1Af8IBvcCCwQJA\/8PEggOBwYUDgQH\/gYOAf0C+gIE8fb++f316\/n78vbu7gL89f35+wgA\/QUBDA4DCQ0HDw0ADRUMCwH+DQj+Afr+Cfvz\/Pf5\/fDy+vb79e35\/vb9+PQFB\/8A\/QEPBQAIBQwTBAYPDQwKAAgMBAX++gYD+fz39gP98\/b0+f7v7vf4\/f3x+wX\/\/\/35CAoDBQEDEAsECwgNFQwGCQYJCPr+BP4C+\/P\/Afb28\/P\/+e\/z8PT+9vT\/\/QIF\/QAIAwcE\/goPCAkFBRIPBQgEBw4D\/AMBAAH3+gL7\/frz+\/709PHv+\/31+Pb9B\/74AAAGC\/8ACwkIBgELEgoMCAUOCwAC\/wEIAvn9+\/4B9fT69\/r47vT79vb19AEC+v79\/wkF\/QUHCg4IBgsKDg0ECA0GBgP8BAb+\/Pr7A\/30+fj4\/fTx+Pf4+fb6Afz+\/PgDCAECAgINCgEFBgwRCAUMCwcH\/gEJAgH++gEC+vn39v798\/X09vz18vj7\/wH5\/AUCAQD9BgwFBQYHDw0FCAkLDwkBBAMDBPv6\/\/3++\/T6\/\/j18\/L7+\/L19\/oB\/fj+AAMGAgEIBgcGAQYMCAgIBw4NAwIBAwkD+v8A\/wH49v38+\/j1+Pz29PXz\/P\/6+\/8GBQECAQMHBgQEBAYJBQUIBwwIAA4PBggEBBEG\/gb\/BAv7+gX\/AwP5\/QL7APnyAP71\/PXzAvrw9\/T6APLw+\/b4++71\/vX59vL9\/\/X79\/cGAPcA\/QAK\/voEAwgJ\/AIMBQYF\/gsOBAgFBRALAwkGChMJAw0JDREDBg0ICgb8BwsAAP78CAT4\/\/z+Bvz0\/vv\/Aff3\/vn8++\/4\/PX59fH9+\/L19PUA9u\/59\/oA9PYA\/v8A+gEG\/gH\/+gcG\/gMDAw0G\/gUGCg8CAg0JCgsBCBAJCggFDg4FCQgGEAn\/BwUGCv\/8AwIDA\/f9Bv39+\/YAAvf5+PoC+\/H5+Pn\/9vH7+fn78PT79\/n27\/r+9ff49wIA9\/3+AQf9+gQDBAgAAQoEBQT+BwoDBgUDDgsBBgcJEAgBCwsMDQMFDwoJCAIGCwECAf0GA\/r\/\/v8F\/fX9\/f4B9vYA\/fz68voA9\/f49f378vX39\/328Pj5+Pz09P39\/f73\/Qb\/\/v\/8BQf+AQMECwX9BQcHCwMCCgkICAEFDAcICAMMDgYHCAgPCgEHBwcKAf0GBAED\/f0F\/\/389\/4A+Pr7+QD98\/j6+v738\/v7+vry9f749vf0+fz19fj4\/\/31\/P\/+BP75AQMDBP3\/CQYEBP8GDAQEBgYMCgEFCAkMBgIKCwkLBAMMCgkIAwcMBAMDAAYG\/P4BAQT+9\/4A\/v\/4+AD9+vr1+P349\/j2\/P319\/j4\/fny+Pr4+\/T0\/f37\/Pj8A\/79\/\/0CBP0AAwMHBf4EBgYJAwEKCgcHAgUNCAYHBgsNBQUICAwJAwcJBwgC\/gQFAwP9\/gUB\/v76\/wL7+f38AP31+fz7\/ff0+\/z5+fX1+\/n39\/T5\/ff2+Pf9\/vf6\/v4B\/foBAwEC\/v8GBAIDAQMIBAMFBAkJAwYIBwoHAwgLCQkEBAwKBwgFBwsFAgMCBQX+\/wIBA\/\/6\/wD+\/\/r5\/\/\/8+\/f6\/vr4+fj8\/fb3+fn6+PT4+\/n69vT7\/Pr7+fsB\/\/3+\/gIE\/v8CAwYE\/wIGBQYDAQcIBgYDBAkHBQcFCQsHBggHCgkEBwoICAMABQYDAv\/\/BAH+\/vz+APz7\/f3\/\/vn7\/fv8+ff7\/fv69\/f8+vf49vn8+Pb4+Pr7+Pr+\/f\/9+wACAAH\/\/wQFAgMBBAcEAgQEBwgDBAcHCAUDBwkHCAUFCgkHBwYHCgcEBAMFBQAAAgECAPz\/Af\/+\/Pv\/\/vv7+vr9+\/n6+vz9+fn6+vv69vn7+vr49vr7+fr5+wD+\/P7+\/wL+\/wICAwMAAgUEBAMBBgcFBQQFCAcEBQUHCQYFBwcIBwQGCQgHBQIFBgMCAAADAv\/\/\/v8B\/fz9\/f7++vv9\/Pz6+Pv9+\/r5+fz7+Pn4+vz6+Pn5+vv4+v39\/v38\/wH\/AP\/\/AwMBAgEDBQMCBAQFBgQEBgYGBQMGCAYGBQUICAYGBQcJBwQFBAQEAQEDAgIA\/gAB\/\/\/9\/P\/\/\/Pz9\/\/4ABAMIB\/\/+\/\/z++vH49e727fYH9\/8F\/A4RAQ8NAA8C8wX79QDu7Prx9QHx\/g79BgwAEhYACAf6Cv7v\/\/bw+uztA\/r6CfkEEgQKEQQOD\/sBA\/QE+ur58vAA9vMIAQAMAAcYCQoM\/QYK9fz97\/736Pn79wb\/+A4HBRMFCRUDAgb3\/wbw9fnr+vzv\/wL9DAX\/EQ0KFAEADv77AfL4Aezx+PEBA\/YECQIQCwIVDQMM\/fsH+vX87fL+7\/cA+AYK\/AgNBxQP\/g0H\/QX58wH28Pjt9gX3\/Qb9ChACCxMHDgj4BQH4APPu\/PPw\/fb7C\/4BCwMOFAYIDP8IA\/P+\/fP78ev+\/PcE\/AAQBAYPCA4R\/wEG+QH+7vn47\/r48wQD\/QkBBBQKCQ8CBgv5+gD1\/Prq9Pv2Af74CQgCDgcIFggBB\/3+BvX1\/PD2+u\/7AfwGBf0NDgcSCAIOA\/sC+PgB8e\/48fwB9v8IAgoLAhARBQsC+wf\/9fv08\/zw8\/\/5AQf8BA0HDhEECwr\/BP32APrx9\/DyAfj4BP8FDQIIEQoNCvwEBvn++fL79+75+PgG\/\/0JBAkRBwkNAwYF9\/4A9fn17fr89v\/+\/gsFAg0JDRECAQj+\/wDy+Pvy9vfzAAP7BQMCEAsHDwcFCv76Avn6\/O7z+vX8\/vkECAEJCAcTDAIHAf8F+fX99fX47\/gB\/AEE\/gkNBg0MBQwF\/AH8+QD18Pjz+P\/3\/QYCBgkDDRIICQX+BgH3\/Pj1+\/Lw\/Pr9Bf0BCwcLDgYLDAEDAPkA\/fP39PL9+vcBAAMKAgYPCwwL\/wMG\/P379Pv57\/b5+AP\/\/AYFBw4ICA4GBAX7\/gH4+ffw9\/v2\/f79CAUBCgoLEAYBBwH\/APb4\/fT09vT+AvwCBAIMCgYOCgYJAPsC\/Pn88vP59vr9+gIHAQYJBxANBAYEAAP89v349fjx9gD8\/wP\/BgwGCg0HCwf+Af\/7\/vjy+PX2\/fj8BQIDCAQKEAkIBgAFAvn7+vb69PD6\/PwC\/gAJBwgMCAoMAgIB+\/\/+9ff28vr69wABAQcDBA0MCwsCAwf+\/P33+\/rx9Pn5AAD8BAYGCwgIDggEBP39Avr4+PP2+vb7\/\/4FBQEICgoOBwIHA\/4A+fj99vT29fwB\/P8EAwoKBQwMBwgB\/AH++vv09Pn2+P37AAYBBAgHDQ0FBgUBA\/33\/Pr29\/L1\/\/39AgAFCwYIDAkKB\/8AAPz++fP49vX7+fsEAwIGBAkPCgcHAQQD+vv8+Pn28Pn9\/AH\/AAgHBgoICgwEAQL9\/\/729\/j0+fr3\/wIBBQMEDAsKCgQCBv\/8\/fj6+vP0+fr\/APwDBgUKCAcNCQME\/\/4C+\/f59fb69\/r\/\/wQFAQcLCQ0IAgYE\/\/\/6+f338\/b2\/AH9\/gQDCAkFCgwHBgL9Af\/6+\/b0+ff3\/PwABQIDCAcMDQUFBgEB\/vj8+\/b38\/X\/\/v0BAQUKBgcMCAgGAP39AAAAAgD\/AgICBAACBgMGBAEJBQIIAQMJ\/wEE\/QMC+wEA\/AL9+QH9+wD4+gD6+fz2+v\/1+fv1\/Pv0+vr2+\/nz\/Pn2\/Pb3\/vr6\/\/n9A\/v+AvwDA\/0CBAAGBP4IBQIIAwMKBQQJAwYMAwYJBAkKBAcKBQsJAgoHAwgCAAcDAAT+\/wb9\/gH7\/wH5\/P\/5\/\/33\/vz5\/vr3\/vv3\/ff4\/\/f4+\/b6\/fX4\/ff+\/vf\/\/\/wC\/\/wEAv4F\/wAIAgEGAQQJAQMIAggIAQcJBAkHAgsJBAsFBQ0HBQoEBQoBAgb\/BAT8AQL9AgD6AQD6APz6Afz5\/vn6APf4\/vf7\/fX5\/Pf7+\/T8+\/b8+Pf\/\/Pn\/+\/wE\/P0D\/QEE\/QEFAAQF\/gYHAQcEAQkGAggFBAwEAwkEBwsDBQsFCAoBBwgCBgP\/BgT+AwD9Bf78Afz9Afr6APr9\/vb8\/vj8+\/b9\/Pb8+ff++fb8+Pj99vb++fv\/+P0B\/AAA+wME\/QMC\/wcD\/wYDAggCAQgDBQkBBQkEBwgCCAoDCAcECwgDCAUDCQIABgEBBP3\/A\/7\/Afr\/Avr+\/fkA\/fj9+\/n\/+vb9+fn99vj9+Pn89fr99vr79\/7++f7++wP++wP\/\/wT+\/wYBAgX\/BAgBBQYBBwgBBgYDCgYCCQYFCgQECgYGCQIFCQIEBP8EBf0BAf0DAPoA\/vwB+\/kA\/Pv\/+Pr\/+fr89\/z+9\/r79\/379fv6+P349v77+\/\/6\/AL9\/gH9AQX+AQP\/BgT\/BAUCBwQABwUECAIDCQUFCAMGCgQGCAQJCQMGBgMHA\/8FAwAE\/v0D\/\/0A+\/4C+\/z++v7\/+Pz9+v789v38+f349\/36+Pv3+f34+fz5\/f\/6\/QD8AQD7AQL\/AwD\/BQMBBQECCAMDBgIFCAIEBwQIBwIHCAUIBgMJCAQIAwMIAwEEAQIE\/v8C\/gEB+\/8A\/P\/9+f7++v76+f\/7+fz5+v75+fz5+\/z3+vz5+\/r4\/f77\/\/z8Av\/9Af8ABAAABAEEBQADBQMFBQEGBwMGBAMIBgMGBAUJBQQHBQcIAwQGAwQE\/wMEAAL\/\/QIA\/f\/9\/QH9+\/78\/f\/6+v77\/P34+\/36\/Pr4\/Pz5+\/n5\/fr5\/Pv9\/\/z8AP4AAf0AAwACAf8EBAEDAgIHBAIFBAUHAwMHBQYHAwUIBQYGAwcIBAUEAgYEAAMBAQP\/\/gH\/\/wH8\/gD9\/v36\/v\/7\/Pz6\/vz5+\/v7\/fr4\/Pr7\/Pj5\/Pr7\/Pn9\/\/z+\/v0BAP4AAAAEAf8DAgMEAQIFAwQFAgQHBAUEAwcGBAUFBQgFAwYGBgcDAgUDAwP\/AQMAAAD+AQH9\/v79AP77\/f39\/vv6\/fz8\/Pn7\/fv7+\/n8\/fn6+vn8\/Pr8\/f3\/\/f0AAAAB\/gADAQECAAMEAgIDAgUEAgQEBAYEAwYFBQYDBAcFBQUEBgcEBAMCBQMAAQEBAgD+AAD\/AP39AP79\/fv9\/\/z8\/Pv9\/fr7\/AEA\/wEAAgQBAAQDBQYBBgoFCgUEDwgFCwMFDQABB\/0EBfkAAvsC\/vUA\/ff\/9vUA+fT78\/b\/8vT68fj77\/X58vj37fn38fnz8v759f73+gX7+wP8Awf8AgcBCAf+CQoDCgYCDgoEDAYHEQcHDgcMEAYJDwgODwILDAMJBP4JBf0E\/vwH\/foB+f0C9\/j\/9\/z98vr89fv48fv68vr08v318vjz9fzy9Pz2\/P\/1\/QL7AQH6BQX+BgEACwUBCAQFDQQDDAUKDQMJDgcMDAQNDwcOCgcRDAYLBgUNAwAIAAMG+\/8E\/QAA9\/8B+P369gD79Pv39v728vv19\/vy9Pv09\/nv9\/ry+fj0\/v33\/vz7Bf77BP8CB\/4BCQMGCQAHDAQJCAMMCwQKCQYPCQQMCQoPBwcPCgsMAgcLAwYE\/gUG\/AH\/+wT++P\/7+wD49v75+f3z9\/32+Pny+vvy9\/fy+\/fx9\/X0+\/b0\/fr7APj8A\/4AAvwDB\/8DBAAJBwAHBgUMBgILCAgMBQcOCAkLBQsPBwoLBw8LAwgHAwkD\/QUCAAT7\/AP9\/f\/4\/AH4+vv2\/fz0+fn2\/Pfy+ff2+vPz+vb1+PH2\/PX4+vf+\/\/n9\/\/0EAPsDAwEGAQAIBQQIAgUMBQcIBAoMBQgKBw0LBAsKCQ0IBg0KBwkCBAkDAgP+AwX8\/gD7Af\/3\/f36\/vn1\/fv4+\/X2\/ff3+PT4+\/T19\/T6+PH29\/b8+fX+\/fsA+\/wEAP8C\/wMIAQIFAggIAQYIBQoHAwoKBwsGBg0KCAsGCQ8ICAsHCwoCBQYDBgL9AwL\/Avz7Af77\/fn7APn4+\/f7\/PX3+vf7+PL4+fb59fP6+PX39Pb9+fj8+v4B+\/0B\/wMD\/QMFAgYCAQgHBAcEBQsHBQgGCQwGBwoICwsFCQsJCwkFCgkFBgICBwIAAv8BA\/38\/\/z\/\/vj7\/fr8+fb7+\/j69vb8+fb49fj79fX49vn58\/f7+fz7+P4A\/QD9\/QQCAAIBAwgDAgYEBwgCBQgHCQcDCQoHCQcGDAoHCQcJDQkGCAUHCAECBQIDAvwAAv7\/\/PoA\/vr8+vr\/+vf6+Pv89vf6+Pr59Pj69\/j29Pn59vj3+f77+v38\/wL9\/gIBAwP\/AwYDBQQCBwgFBgUFCggFCAcJCwYGCQkKCgUICwkKBwMHBwMEAQAEAv4A\/v8B\/fv+\/P3++Pr9+\/v59vr8+Pn39vv59vj3+Pv39fj3+fr2+f38\/f36\/wH\/AP\/\/BAMBAwIDBwQCBQUHCAQFCAcICAQICgcIBwYLCgcICAgLBwMGBAUGAQADAQEA\/P4B\/v78+v7++vv6+v379\/r5+vz39\/r5+fn19\/r4+Pf1+fv4+vr7\/\/77\/v8AAv\/\/AgIDBAADBgUFBAMHCAUFBQUJCAUHBwgKBwYJCQkKBgcKBwYFAgUGAgEA\/wMB\/f7+\/gD8+v39\/P35+f37+\/r3+vz5+Pj3+\/r2+PsFBAQJBwwKAAMD\/QD78\/v28Pnx+gX6AwYBDxAHEQwDDQL5A\/r3\/e\/u+vX4AvcCDAIKDQYTEQEHBPoF\/PH89PD48\/MD\/f8K\/wcSCA4PAgkJ+v7+8\/736\/b29gL8+goFBQ8GCxUFBQf6AAPz+Pjt+Pfx\/v\/9CQUAEAwKFAQDDP\/8APT4\/e7x9\/L\/AfgECAMPDAYUDAQK\/fsE+fX67\/H78vgA+gUJ\/wkOCRMN\/woE\/AP49P317vfx9wT7\/gcBCxAGDRAECgb4Af\/2+\/Pt+Pf1APr+DAMFDQcPEwQECP0CAPL6+fD29PIBAPwGAgMRCgoRBgcL\/PwB9vv67fP49f7++QcIAw0JCBUKAwf\/\/gT39frx9Pjw+gH9BQYADA8IEQkCCwP7APn3\/vLu9vT8AfkACQQKDQUQEAQIAvsD\/vT58\/D59PX\/\/AIJAQUOCg8PAgcI\/gD89Pz47\/T09gL9+wYECA8IChEIBgf7\/wH4+fbu9vn0\/P39CQUCCwoNEgcCCAH\/APX3+\/Lz9fP9AfwDBAMODAgPCgUJAPoB+vn67\/H59\/v++wMJAwkKCRINAgUC\/gL69Pn18\/fy+AH\/AgUBCQ8JDQwECQX8\/vv4\/PTu9ff6APv+BwYICwcNEQYFA\/0B\/\/b39vL39vT+\/wEHAgQNDA0OBAQIAP789vv68fL19wD\/+wQGBw0JCRAKBQX9\/QH6+Pbx9fn2+v7+BgYCCQsMEAgCBgP\/\/\/f3+\/Xy9PX8Av4BBQQMDAgNCwYHAPv+\/Pn58fH4+Pr+\/AIJBQcKChANAwMC\/wD79fj38\/b0+AAAAQQDCA4LCwsFBwX9\/Pz5+vbv9Pj6\/\/3+BgcHCggMDwgDAv4A\/\/f29vP29\/X9AAEFAwQLDQwMBgQGAP38+Pn58vL2+P8A\/QIGBwsJCQ4LBQP+\/QD79\/bz9fn3+v4ABQYDCAwMDggCAwL\/\/vj2+vbz9fb8AgABBAYLDAkLCgYFAPv9\/fn48\/H3+fv+\/gIIBgYKCw4MBAIB\/\/\/79vf39Pb2+AABAQQEBw0MCwoGBQT9\/Pz5+fbx9Pn7\/\/7\/BQcHCQkMDggCAf7\/\/vj29vT2+Pf9AQIFBAUKDQwLBgMEAPz7+Pn58\/P2+v8B\/wIGBwoKCQwKBQL+\/P779\/bz9fn5+\/8BBQYFCAsMDQgCAgH+\/fn2+Pb09vj9AgECBQYKDAoKCQYEAPv8\/Pn39PL3+vz\/\/wMHBgcKCw0LBAEA\/v779vf39fb4+gACAgQFCAwMCgkFAwL9+\/v5+fby9fr8\/wABBQcICgoLCwcCAP79\/fj29vX2+fn+AgMFBQYKDAsKBQIC\/\/z7+fj49PT4+\/8CAQMHCAoLCgoIAwH+\/P369\/b09vn7\/QACBQcGCAwMCwcCAQD9+\/n3+Pb09\/r+AgIDBQcKDAoJCAQC\/\/v7+\/j39PT4+\/0AAQQHBwgKCwsJAwD\/\/fz69\/b29ff5\/AEDAwUGCAwLBgH\/AAEFCAgJCQoMCwgHBQQDAP7\/\/fz69\/f49vTz8fLy7\/Dx9Pj5+fz+AAIBAgYHCAkICg0NDQ4NEA8LCAYEBAL+\/v38\/Pn3+Pf19PHx8\/Hx8vP3+\/r8\/f8CAwIEBggKCQkMDQ4PDQ4PDAkGAwMDAP79+\/z79\/f29fbz8fLx8fPz9vv8\/f7+AQQDBAYHCgsJCwwNEA8NDgwKBwMCAwH\/\/vv7\/Pn39vT29fHx8fH09PX6\/P3\/\/v8EBQUGBgkLCwsMDA8QDg0MCQgFAQIBAP\/7+vv6+Pf09PXz8vHw8\/b2+fv9AAD\/AgQFBwYHCwsMDAsOEQ8NCwgIBwMBAP\/\/\/fn5+fj49fP19PLy8PL2+Pn7\/P8BAAEDBQgIBwkLDA0MDA8QDgwIBgcEAQD+\/v77+fn4+Pfz8\/Py8\/Hx9fj6\/Pz9AQICAwQHCQgICgsNDgwODw4NCAUFBAIB\/v3+\/Pr59\/f49fPz8vPz8fT4+v39\/AACAwQEBQkJCQoKDA8ODQ4NDAoFBAMCAv\/8\/Pz6+ff29\/b08\/Hy9PP09\/n9\/\/3\/AQIFBQQHCQoLCgoODw4ODAsKBgMCAQEB\/fv7+vr49fb29PTy8PP09fj5\/P\/\/\/wECBAcFBggJDAwKDA4PDwwJCQcEAwAAAf77+\/n5+vb19fT08\/Dx9Pb5+vr+AAABAQMHBwYICQsNCwsNDg8NCQcHBAQB\/v\/\/\/Pv5+Pr49fXz9PXx8fT2+fv6\/QAAAgICBgcHCAgJDQ0MDQ0ODgoGBQQEAv\/+\/vz8+vf4+Pb18\/L08\/Hz9fn9\/Pz\/AAIEAwQHBwkJCAsNDQ4NDA0LBwUDAwMA\/f38\/Pv39vf29vTx8vPz9Pb3\/P79\/\/8BBQQEBgcJCwkKDA0ODgsLCgcFAwECAf79+\/r7+fb29vb28vHz8\/b39\/v+\/v8AAAQGBQYGCAsLCgwMDhAMCgkHBgQAAAH\/\/fv5+vr39vX09vTx8vP1+fn6\/f4AAQACBQYHBwcKDAsMDA0PDgkIBgUFAf8A\/v79+fj5+Pf18\/T18vLz9fn7+\/3+AAMCAgUGCAkHCAsMDA0LDg4KCAUDBAP\/\/\/39\/fr4+Pf39\/Pz9PPz9PT4\/Pz9\/v8CBAMEBQcJCAgKCw0ODAwNCggFAgIDAP\/9+\/z8+Pj39vf18vPz9Pb19\/v9\/v\/+AQQEBQUGCQoJCgsMDg4LCwkIBgIBAgD\/\/vv7\/Pn49\/X29vPz8vP3+Pj7\/f4B\/wADBQYHBQcKCgsLCw0PDAoIBgYEAAAA\/\/\/7+fr5+ff19fb18\/Py9vn5+\/z9AQEAAgQGCAcGCQoLDAsMDg0KCAQFBQEA\/\/3+\/fn5+fj59vP19PT08\/X6+\/z9\/QACAgMEBQgJBwkKCw0MCw0MCwgEAwQCAP\/8\/f37+fj3+Pf09PTz9fX1+fv9\/v7+AgMEBQQHCQgJCgoNDgwMCwkIBQICAgEA\/fv8+\/r59vb49fTz8vT39vn7\/P\/\/\/gEDBAYFBQkJCgoJCw4NDAoHBwYDAQH\/AP76+\/r5+ff19vb09PLz9\/n6+\/z\/AQABAgQGBwYICQoMCwoNDgwKBgUGAwEA\/v\/\/+\/r5+fn49fX19PXz8\/f6+\/38\/gECAgMDBggHBwkJDAwKDA0LCgcDBAMCAf79\/vz6+ff4+fb19PP09fT2+fv+\/v0AAgMEAwQICAgJCQsNDAwMCgoIBAMCAQH\/\/Pz8+\/r49vj39fTy8\/b29\/r7\/gD+\/wIDBQUEBwkJCgkJDA0MDAkHCAUCAQAAAPz7+\/r6+Pb29\/X18\/L29\/j7+\/0AAAACAgUHBQYICQsLCQsNDAwIBQYFAgH\/\/wD9+\/r5+fr39vb19fTy9Pj5\/Pz8AAEBAgIEBwcHCAgKDAsLDAwLCQUEBAIC\/\/3+\/fv6+Pj5+PX19PT29PX4+v3+\/f4BAgQEAwYICAkICQwMCwwKCgkFAwIBAQH9\/P37+\/n2+Pj29fTz9fb2+fr8\/\/\/\/AQIEBQQFBwgKCgkLDQwMCgcIBgMCAAAB\/vz7+vr69\/b39vb18vT3+Pr7+\/8AAAEBAwYGBQcICQsKCgwMDAoGBgUDAgD+\/\/78+\/n5+vj29vX19vP09\/n7\/fz+AQECAwMGBwYHBwkLCwoMCwsLBgQDAgIB\/v3+\/Pv69\/n59vb19Pb19ff5\/P79\/gABAwQDBAcHCAgICwwLDAsJCQcDAwEBAf78\/fz7+\/j3+Pf29fP09vb4+vv+\/\/8AAQMFBQUHBwkKCQkMDAwLCAcHBAIB\/wD\/\/Pz7+vv59vf29vbz8\/b4+vv7\/gAAAQICBQYFBgcICwoJCwwMCwcFBQMCAf7+\/\/38+vn6+ff29fX29fT2+Pv9\/f0AAQIDAgQGBwcIBwoLCgsLCgoIBAMCAgL\/\/f78\/Pv4+Pn39vX09fb19\/n7\/v\/+\/wECBAQEBgcICQgJCwsMCwkJBwQDAQABAP38\/Pv7+ff49\/f29PT29\/n6+\/4A\/wABAgUFBAYHCAoJCQsLDAsIBgYEAwH\/AAD9\/Pv6+\/r39\/b29\/X09vj6\/Pz9AAABAgIEBgYGBwcKCwkKCwsLCAUEAwMC\/\/7\/\/fz7+fn6+Pf29fb29fb4+v3+\/f8BAgMDAwYHBwgHCQsLCwsJCQgEAwIBAgD9\/f38+\/n4+fj39vT19vf4+fr9\/\/8AAQIEBAQFBwcJCAgKCwsLCAcHBAMCAAAA\/vz8+\/v69\/f39\/f19Pb4+fv7\/QAAAAECBAYFBgYHCQoJCgsLCwgFBQQDAv\/+\/\/78+\/n6+vj39\/b29vT2+Pr9\/f3\/AQEDAgMGBgYHBwkLCgoKCgoIBAMDAQIA\/f79\/Pv5+Pn49\/f19ff29\/n7\/f\/+\/wECBAQDBQcHCAgICgsLCwkIBwQCAgABAP39\/Pv7+vj4+Pf39fT2+Pn7+\/0AAAABAgQFBAUHBwkJCAoLCwsIBgYEAgH\/\/wD+\/Pv6+vv4+Pv+AAICAgMDAgMDBAYGBwgICgoICgwLCwkGCAcEAwEAAwH9\/v39\/vr5+vn6+fb2+Pf39fP19vT08vL19PLy8vL08fDy8vT29Pf6+vz7+v4BAAEAAQUFAwQFBgkHBggJCgsICQwMDAwKDQ4NDQwMDw8NDQwMDQgGBgUFBQEAAgAA\/vv9\/vz7+fj7+vf39vf59vT19fb28\/L19PTz8fP18\/Pz9Pn6+fr7\/AD\/\/gABAwUCAwYGBwcFBwoJCQkIDAwKCwoLDg0LDA0ODwwMDg4ODAgICQcFBAEDAwD\/\/v7\/\/vv7+\/v8+Pf5+Pj39PX49vX08vX28\/Pz8\/X08vLz9fj39\/r7\/f78\/gEBAQEBBAYEBQUFCQgGCAkKDAoJCwsMDAoLDg0NDQsODw0MCwoLCQUFBQQFAv8AAP\/\/\/Pv9\/Pv69\/n6+Pf29vj49PX19ff08vT09PXy8vT09PT09\/v6+vv7\/wD+AAECBAMCBAUGCAYGCQkJCQcKDAsLCwoNDgsLDAwPDgwNDQ0MCAYHBgUEAQIDAf\/+\/P7++\/v6+vz69\/j4+Pj29Pf29vXz9Pb08\/Py9PXy8vP0+Pj3+fv8\/v39AAEBAgECBgUFBQUHCQcHCAgLCggJCwsNCwoMDQ0NCwwODg0MCQoKBgUEAwQDAAAA\/wD9+\/z8+\/v4+Pr5+Pf19\/j29fX09vbz8\/T09fPy8\/T09fX2+vv7\/Pv+AP\/\/AAEEBQIDBQYIBgUICQkKBwgNCwoLCQ0PCgoMDBANCA0OCwwFBAsHAwX+Agf9\/gD7AQD2+\/z4\/vny\/Pr1+\/Py\/Pbz9\/H1+\/Hy9vD3+O7z9\/T7+fL9\/foB+\/oFAf8E\/gIKAgMHAgoMAgcLBg0LAwwNCQ4JBxANCg4JCxMLCg0GCwwCBAcBBgT6AQL9Afv3AP34\/Pb3APf1+fX5\/PLz+PP59+719\/P38+739vL49PX\/+vn9+v8E\/P4DAAUG\/gQIBAkFAQoLBgsGBg8KBwwICxEJCA4KDw8GCw8KDAgCCQkDBgD+BgH9APz9Avr4\/fj8\/fT4+\/f6+PH4+vT38\/H69vL18vP68vD28\/n89Pj++\/\/\/+AAD\/wMA\/wgGAgYDBQwGBAkHCg0FBw0JDAwFDA8KDQoHDw8JDAgHDQcCBgIEB\/7+Av8AAPf8APr8+fX8\/Pb59vX89\/P39Pb68vL39Pb37\/T48\/f28\/v9+Pz7+wMA\/AEAAgcBAAcFBwkBBgsHCggECw0ICggHDwwICwoLEAoIDgsMDAMFCQUFBP4DBf4A\/fsC\/\/n8+vr\/+fX6+Pn78\/X69\/f28fb59PXz8fj38fT09fz49vz7\/QH6\/AIBAgP+AwgDBQQCCQkEBwcHDQkFCgkLDgcIDQsMDAcLEAsMCgYLCwQFAwIHA\/0B\/\/8C+\/r\/\/Pz99vn9+fn49Pr79fb19Pr38fX19fnz8fb19fjz9\/36+\/v5\/wL+\/wD\/BgQAAwQFCQkHAwQGBgkJBAYHBwoHBAgJCAoFBg0JBwgFCQ0HBggHCwsEBwkICwcDCQkHCQQECgcEBAADBwD\/AP4CAvr8\/v3\/\/Pf8\/fv89\/f++\/j59vn9+Pb49vr79PX49\/r38vf59\/j08\/r59vf09vz39ff1+fv09fj3+vjz+Pr4+vb0+\/r4+fb4\/vn3+ff8\/vj5\/fwA\/\/r\/AgADAP4FBQMEAQMKBgQGBAgLBQUIBwsKBAgLCQsIBgwMCQoHCA8LCAoIDA8ICAoKDQwGCQwKDAkGCwwJCgcHDgsICQYKDQgGCQgLCwQGCQgKBgMICQUFAQEHAwAA\/gAE\/vv+\/P\/\/+fr8+\/369vr8+fr39fz69\/f1+Pz39ff2+frz9fj3+ffy9\/n29\/Tz+vj19vT2+\/f09vb5+\/T1+Pj6+PP3+vj59vX7+\/j49\/j9+vf6+v4A+\/v\/\/wIB\/QAEAgQCAAYHBAUDBAoHBAYGCQsGBQkICgoEBwsJCwgFCwwICQcIDgsHCQgLDQgHCgoLDAYICwkKCAUKCwgJBwcMCgYHBggMBgUIBwkJAwUJBwcEAAQFAgH\/\/gMB\/P78\/gH8+fz7\/f33+Pz6+\/n1+fv4+Pb1+\/n19\/b3+\/b09\/f4+fP0+Pf49\/P2+vf39fT6+fX29ff79\/T39\/n79fX5+Pn59Pj7+fn49vv8+Pn4+v\/9+v7+AAP+\/gICAwP\/AgYEBQMCBwgFBQUGCggEBwcJCwYFCQkKCgUIDAkKCQYLDAgJCAgNCgYJCQoMCAYKCgsKBQcLCQkIBQkLCAcHBwsJBQcGCAoGBAcHCAcCAgUDAwH+AQP\/\/v38AP\/6\/Pv8\/\/r4+\/v7\/Pf3+\/r6+PX4+\/f39vX6+fX29vf69\/T39\/j59PX5+Pj39Pf69\/f29fn59ff39\/v49fj5+fv29vr6+vn2+fz6+fn4\/f76\/Pz9AQD8AAECBAAABAQFBQEECAYFBQMICQUGBgcKCQUHCAkLBwYKCgoKBggMCQkIBgoMCAgICAwKBggJCQsHBgkKCQkGBwsJCAcFCAoHBgYGCggEBgcHCQUDBQUEBP8AAwEA\/\/z\/Af38\/Pv+\/vn6+\/v9+vf6+vr69\/f7+vn49fj79\/f39vr59fb39\/r39Pf4+Pn19fn4+Pf19\/r49\/f2+vr29\/j4+\/n1+Pr6+\/f3+\/r6+vf5\/fv7\/Pv\/AP3+\/wADAv8CAwMFAgEGBgUFAwUJBwUGBQgJBgYHCAoJBQgJCQoHBgoKCQkGCAsJCAgHCgsIBwgICgkGCAkJCgcGCQkICAUGCggHBwUICgYFBgYICAQFBgUGAgADAwEB\/v4BAP79+\/7\/\/Pv7+\/38+Pn7+vz59vn6+fn39\/r5+Pj2+Pr39vf3+fn19vj3+ff1+Pn4+fb1+vn39\/b4+\/j3+Pf6+vf3+fn7+ff5+\/r6+Pj8+\/r7+vwA\/v3+\/gEC\/wACAgQDAAMFBAUDAwcHBgYEBgkHBgcJDAoKCgkMDAkKCgoNCggLCwsMCAkOCwoKBwwOCQgJCAwLBggJCQwHBQkJCQkFBQoHBQQABAYBAAD\/AwL8\/f7+APz5\/f38\/fj4\/fv5+fb6\/fj3+Pb7+vT29\/f69\/P3+fj49PT6+Pf39Pf79\/b29fr69fb39\/r49Pj5+Pr19fv6+Pj2+P35+Pj3\/Pz3+fv8AP76\/gAAAv7+BAMDAwADCAUDBAMICQQFBgYKCAQHCQgKBwULCgkJBggNCggJBwsNBwcJCQwLBggKCgsHBgoLCQkGBwwJCAgGCQsHBgcHCgkEBggHCQUDCAgGBgICBwMBAf4BA\/79\/v0A\/\/r7\/fz++vf7\/Pr79\/f8+vj49vn89\/b39vr59Pb49\/n39Pf59\/j09Pr49vf19\/v39fb2+vr19vj4+vj0+Pr4+vb2+\/r4+ff5\/fn4+fn8\/vr7\/v4B\/\/wAAgEDAAAFBQMEAgQJBgQFBQgKBQUHBwoIBAcKCQoHBQsLCAgGCA0KBwkHCgwHBwkJCwoGCAoJCgcFCgoICQYHCwkGBwYJCwYFBwcJCAMGCAcIBQMGBgMDAAAEAv\/\/\/f8C\/fv9\/P7++Pr8+\/z59\/v8+fn39\/z69\/j2+fv39ff3+fn09fj4+ff0+Pn39\/X1+vn29\/b4+\/f19\/f5+vX2+fj6+PX4+vn59\/f8+\/j5+Pn9+vn7\/P8A\/P0AAAIB\/gIEAwMCAQYGBAQDBQkGBAYGCAoFBQgICgkFCAoJCQgGCwsICAcIDAkHCAgKDAcGCQkLCgUICwkJBwUKCggIBgcLCQUHBggKBgUHBwgIAwUIBgYDAAQEAQH\/\/wIA\/f79\/gD7+vz8\/fz4+fz7+\/n2+vv5+Pf3+\/r2+Pf4+\/f19\/j5+fX2+fj49\/T4+ff39vb6+fb39\/j79\/X4+Pn69vf6+fr49vn7+fn4+Pz7+Pn5+\/78+\/3+AAH+\/gICAwIAAwYEBAMCBwcEBQUGCQcEBwcICQUGCQkJCAUICwkIBwYKCwgICAgLCQYICAoLBwYJCQoJBgcKCAgHBQkKBwcGBwoIBQcHBwkFBAcHBwcDBAYEAwL\/AgP\/\/\/7+Af\/7\/f39\/\/v5\/Pz8+\/j5\/Pr6+fb6+\/j49\/f6+fb39\/j69\/X4+Pj59fb5+Pj39fj69\/f39\/r59vf4+fv49vj5+vr39\/v6+vn3+fz6+fn4\/Pz5+\/z9AP78\/wABAv\/\/BAMDAwEEBgQEBAMHBwQFBgcJBwUHCAgJBgYJCQkIBggKCQgIBwoLBwgICAoJBggJCQoHBgkJCQgFBwoIBwcGCQkGBgYHCQcEBgcHCAUEBwYGBQECBQIBAP4BAf7+\/f3\/\/vv8\/Pz9+vn7+\/v7+Pn8+vn59\/n7+Pf4+Pr59vf4+Pr39fj5+fj29vr59\/f2+Pr49\/f3+vr29\/j5+\/j3+fr6+vf4+\/r6+fj6\/Pr5+vr9\/vv8\/v8B\/\/0AAgIDAAEFBAQEAgQHBQQFBAcKDgwKDAsOEAwLDg4QEAsOEg8SDgwUEw4QDAsRCgUIBQcJAAAEAQIB+f4A+\/359f399\/j19f338vbz9vrx8fbz9fXu8\/by9fPx+vv3+vn6A\/\/7AP8CBwAABgQHCAEGCwgKBwQMDQgLCQgRDQgMCg0RCwkODA8PBQgLBwcE\/gUG\/wD9+wP\/+fv5+v\/49Pr3+fry9Pn19\/Tv9fjy9PHv9\/Xv8vHy+PPx+Pf6\/fb6AP4AAPsCBgEEAgEKCAMGBgcNCAUKCQwOBwgODA0NBwwRDA0LCREPCAkGBgsFAAQBAgT8\/AD+\/v32+v75+ff0+\/r09vT0+\/Xw9fT1+PHw9fT09e7y9\/T39vT8\/vn8\/PwEAfwCAQQIAgEIBwgJAwcNCQoJBg0OCQoKChEOCAwMDRELCQ8NDAwDBQoFBQP+AwX+\/v37Af73+\/r6\/vf0+fj4+fL0+fb29fD2+fLz8\/D39e\/y8\/X79\/T7+\/wA+fwDAQIC\/gQJAwQEAwsKBAcICQ4JBQsLDA8ICQ8NDQ0IDREMDAwJDgwFBgYECAP9AgEBAvv6AP38\/Pb5\/vj49\/T6+vP19fT59vD09fX38fD29fT07\/T79\/j59\/4A+\/3\/\/wUD\/QMEBQgDAgkICAoEBw4KCQoHDQ8JCgsLEA4IDA0NEQsIDQsJCQIDCAQCAf0CA\/z8\/fr\/\/fb5+vn89vP5+ff48vP69vT08PX48vLz8ff27\/T29\/z59v3+\/gH8\/QUCAgMABQkEBAYFCwoECAoJDQoFCw0MDggJEA4MDQkNEgwLCgcLCgIEBAMGAfwBAf8A+vn\/\/fr79vn++Pb39Pn68\/T29fj17\/T29Pbx8Pb18\/Xy9v35+fv6AAL8\/gEABQT+BAYFCQQDCgoICQYIDgoJCggNDwkKDAsPDggMDg0PCQUKCQYGAQEHAv8A\/QAC+\/r8+v789fj6+fv28vj49vfy8\/n28\/Tx9fjy8fTz9vfy9vn5\/fr4\/gD+Af7+BgQCBAIGCgUEBwYLCwUICgoNCgYMDgsNCQkQDgsMCgwRCwcJBgkIAQIEAgQA+\/8B\/f75+P78+fn2+P339ff1+Pny8\/b19\/Xv9Pb09fHw9\/b19\/X4\/\/v6\/fwBA\/3\/AwIFBP8FCAYIBQMKCggJBwgPCwgLCQ0PCQkNDA8OCAwPDAwHAwkIBAQAAAUB\/v\/8\/wH6+fz6\/Pv1+Pv4+fby+Pn19vPz+fby9PL1+PLx9PT3+fT4\/Pv+\/PkAAgAB\/wAHBQIEAwYLBgQIBwoLBQgMCgwKBwwOCwwJCRAOCgwKDA8IBQcFBwcAAAMBAv\/6\/gD8\/Pn4\/vz4+Pb4\/Pf09\/b4+fLz9\/X29fD09\/T18vH4+fb4+Pn\/\/fr+\/gEE\/wAEBAUFAQUJBwgGBAsLBwgICQ4LCAsKDA8JCQ0MDg0ICw4JCQYDBwcCAgAABAD8\/vz+APr4\/Pv7+vX3+\/j49vP4+fX18\/P59vL0+gUDAwYECgsFCg0LDwoDCQgCBfz8Bfz5\/PX5\/fHy9u\/29Onw9PP79\/MA\/\/wE\/QAMBgUKBQsSCAsQCxMSBQoLBAgC+gQB+v\/39v\/38\/fw8\/ru7vTw+vzy+\/\/8BAL7BwgECwUGEQwKEAsPFwsJDAQJCf0BA\/wB\/fP8+\/T68u\/59O7z7vP+9fb++gIG\/AIIBAsKAgwOChELCRURCg0GBQ0C\/wP8AAL2+Pz2+vjt9vbw9e\/t+vn1\/Pj7B\/\/+BgIIDQMHDQkQEAYQEw0PCQILCAAE\/fwF+\/f89fn98fH38fX16vP59fz59gID\/QQBAQ0HBAsHDBMJChMNERADCAoDBgH5AgH4\/ff0\/vbx9vHy+e7t9\/P6\/fT8Av4EA\/0ICgQLBwYSDQkQDA8UCAULBAcH+\/4D+\/778vv78\/fy7\/jz7fPy9f\/39wD8Agb9AgkFCgsCDRAJEA0KFQ8GCgUDCwD8Avz+AfX2\/PX49+309+\/z8e\/8+\/X8+\/wHAP4HAwgNBAYPCw4RCBAUCgwIAQkG\/gL9+wP69fv29\/vw8Pfx8\/Tr9fz2\/Pz4AwT9BAMCDQgDDAkMEwoKEw0NDgEGCgEDAfkBAff7+PT99u\/28fL37u769vr\/9v0E\/wQE\/gkMBAoJBxIOCBAODRIHAgoEBAb6\/QL6\/Pvy+fvy9fPu+PPs9fX2\/\/n3Av8CB\/4CCwYJCwQNEgkPDgoTDQQJBQIJAPoC\/Pz\/9PX89fb37fT47vL08v389v3+\/QcB\/ggFBw0EBxAMDhAIDxQICgcBCQb8AP76Avrz+\/b2+vDw9\/Hx9e72\/vf8\/fkEBv4EBQMNCQMNCwwSCgoTDQoMAQUKAAEA+QAA9vn48\/z27vXy8fbv7\/v5+v\/3\/QYAAwUACQ0ECQsIEQ8IEA8LDwYBCQQBBfr8A\/r6+vP5\/PHz9e\/38+z29\/f\/+vcDAQEH\/wIMBwgLBQwTCg0PChENAgcGAgcA+QH9+v709f329Pbu8\/ju8vbz\/f73\/gD+BwP9CAcHDQUHEQwMEAoOEgcHCAEHBvv\/\/\/oA+vL7+PT58e\/38\/D18PcA+Pv\/+wQH\/gQHBAwJAw0NCxELChIMCAoBAwoA\/wD5\/wD1+Pr0+\/ft9fTw9fHx\/Pr5\/\/n9BwECBgEJDQUIDQkRDwcQDwoNBgAJBQAD+vsD+vj69Pf88fL17\/b07vf5+P\/7+AMDAAYAAw0HBwsHDBMJDBAKDgwBBgcBBQD4Af75\/fX0\/fbz9u\/z+O\/y+PT9\/\/f+Av8GA\/4JCQYMBgcRDQsPCw0SBgUIAQYG+v4A+v\/78vr69Pjx7\/jz7\/bz9wD5+gD8BAf\/AwgFCwoDDQ4KEAsJEgwGCQIDCf\/9Afr9APX3+\/X59+719vD18vH9\/Pj\/+\/0IAQAGAwgNBQgNCQ8PBw8QCQwGAAgF\/gL8+gP79\/r1+Pzx8fbx9fXu9\/v4\/vz4AwT\/BQIDDQgGCwgMEgoKEQsNDAEGBwEEAPgA\/\/j79vT99vL28fP47\/H59vz\/9\/4D\/wUE\/ggKBQsHBhENCQ8MDBEGBAgCBAX6\/QH7\/fvz+vvz9\/Lv+PTv9vT3APn5Af4DBv4DCQYKCgMMDwoPDAoSDAUIAwIJAPsB+\/4A9ff79vj37vT38PTz8v38+P78\/QcB\/wYECA0FBw4KDg8HDhEJCgYACAb+AP37Avv2+\/b4+\/Lx9\/L09e\/3\/fj9\/fkCBP8EAwIMCAQLCQsRCgkRDAwMAQUIAQIA+gAA+Pv49P338fbz8\/jw8Pn4+\/\/3\/QQAAwT\/CAsECQgHEA0IDg0MEAcDCAQDBfv9Afz8+\/T6\/PT29PD49e719vf\/+vgA\/wEG\/wIJBggKBAsPCQ0MChENBQcFAwgB+wH9\/f\/29vz39\/fw9Pjx8\/Tz\/P33\/f39BgL+BQUHCwUFDQsMDggNEgkJBwIHBv4A\/vsB\/PX7+Pf78\/H39PP18Pb9+fv8+gIF\/wIEAwsIAwoJCg8KCBANCwsCBAkCAgH7AAH5+vn2\/Pjx9vXz9\/Hw+fn6\/vn8BAABAwAGCwQHCAYPDQcMDQwPCAIIBQMF\/PwD\/fz79vr99fX18fj27\/T39\/769wAAAAX\/AQkGBgkECRAJCgwKEA4EBgYDBwL7Af\/9\/\/f2\/fn3+PH0+vLy9PP7\/ff7\/v4EAv0FBgUKBAQNCwoNCAwSCggHAgcH\/v8A\/QH99fv6+Pv08fj28\/Xw9f35+vz6AQX+AQQCCQgCCAoJDgkHDw4LCwMECgQBAf0AAvr5+\/f8+vL29\/T48u\/4+vn9+PsEAQACAAULBAUIBw0NBgsNDA8IAggHAwT+\/QP\/+\/z3+v739ffz+Pjv8vf4\/fv2\/gH\/A\/\/\/CAcFBwQIDgkICwkPDwUGBwQHA\/sAAf7\/+ff9+\/f49PX79PL08\/r99\/r+\/gMC\/AIGBQgEAwsLCQsIChEMBwgEBwgA\/wH+Af\/3+vz5+\/by+Pj09fHz\/fr5\/PoABP7\/AwMHBwEGCgkMCAUNDwoKBQMJBQEB\/gAD\/Pr8+fz89Pb59vj08Pf6+fz5+gIB\/gEAAwgEAwcGCwwFCAwLDQkDBwgEBP\/9AwH8\/Pn7\/\/n2+Pb4+fLz9\/f7+\/f9Af8B\/\/4GBgQFBAYMCQYKCQwOBwUHBQYD\/QAD\/\/\/7+P39+fn29vv49PX0+f34+f7+AQH8AAUEBgQCCAoHCAcHDQwICAUGCAIAAQABAPn7\/vz8+fX5+vb29PT7+vj7+\/4C\/\/4CAgUGAgQICAkIBQkNCgoGBAgGAQH\/AAP++\/38\/P339\/r5+ffz9vr5+vr6AAH\/AP8BBgQCBQUICgUGCQoLCQUGCAUEAf4CAf79+\/z\/\/Pn6+Pn79vX3+Pr7+Pv\/\/wD\/\/gMFAwMDBAgHBQcHCQwIBwcGBQQAAAMBAP36\/f77+vn4+\/r39\/b3+vn5\/f7\/AP7\/AwMEAwIFCAYGBgYKCgcHBgYHAgACAQEB\/\/4EBAYJAf4C\/f\/+8Pz27Pzn+AfzAwL7EQ4DEQ0FEgP4Cvn7Ae7x+fD2AO7\/DfoJB\/8UEwMJBvsN\/fEC9fX76fAC+fwK9QYRAQ0MAg8M\/AEB8wj46\/zv8wH19QkBAg37ChYFDQr7BQnz\/frtAvTo\/fb6Bv75EAYHFQANFQEGAvX\/Be349uj8+O\/\/\/v4OBP8VCgsY\/gUO\/P4A8fgB6fL17AQA9AUFARILARgKBxD6\/wn69\/3r8\/\/q+P7zCAf6CAoFFg\/9EgX\/Cfj1BPbz++n2B\/L\/A\/kLDQAKEQQRCfcK\/\/oF8vH\/9e8A8fsM+wIJ\/Q8TAwgO+w4C8wL89P\/y6v\/69gj3\/xEABg4DDhP9Agf2Bv\/s\/fnw\/PfxBgL6DfwDFQUKEAEHD\/f7AvEA\/en2+vMD\/vMLCP8SAggXCgIL\/P4L8\/b\/7fz66\/oA+AcF9w0NAxQIAhIF\/Ab4+Qfw8fzt+wLy\/Qf9CQv7EQ8FDwP6DAL2AfXyBO\/w\/\/QACfcADQEMEQAODf8J\/\/QG\/fL+8u4E9vUE+wIO\/QISBBAP+gcJ+QL+8v\/+7fv18wn8+QgABBMCBBICBwv1AAX2\/vvr\/f3wAP35CwT9CwUHFgP\/Df0BB\/L5AfT5+ez+BfUGAPsPCv8PCgUQAfcI+f0D8PT\/8PoA8QAJ+wcG\/xEPAQwE\/Q398wT4+ADv8AL5\/AX4Aw\/\/CgsADwv8BQL4CPzw\/\/XzAfX0B\/4AC\/sFEgQLC\/4HCvcA\/\/QC+O37+fcF+\/gJAwQO\/wcVAwQH+AII9fv88\/767\/\/++wn\/+w0IBRECAQwB\/wP1\/gTz9\/rwAAH1AAUACwX+DgsEDP39Cf76APP3\/\/L3\/vYDBvoFCAEPC\/8LCAAH\/PcF+\/b98fQE9\/oC+wQJ\/wULBQ8J+wcE+wT68wD89Pv0+Qf9\/Qb\/Bw8BBgwBBwT4AQH5APjw\/fv3Afn7CwMBBwIJEQICB\/8EA\/T8\/\/b79vIAAfsE\/v0LBgMKBQUNAP4D+wAB8vj89\/799QMG\/gYCAA4KBAcA\/wf9+gD4\/P\/z+P\/7AQH6BAkDCQUDDgj+A\/39Bfv2\/Pb4\/fb7A\/8EBv4HCwULBf4IBv3\/+vkC+PP6+P0E+\/wFAgUHAAkNBAYC+wQC+fz49v\/69\/77AQf+\/ggGCQsBAwgBAf34AAD3+vb1Af\/6AAADCQQDCQcJCP0ABP7\/\/PX8\/vb5+voEAv0DAgMLBgMIBAQF\/f4A+\/378\/oA+\/7+\/AYFAQQEBg0E\/gQA\/wH6+f\/6+vr3\/gP+AQD\/CAoEBwUDCAL7\/\/3+APf2\/Pr7\/foABgIDBAIKCwQDAf8FAfr9+\/v+9\/b+\/gAC\/QEIBQUFAwgIAP\/\/\/AH+9\/r5+gD9\/AEBAgUAAwkHBwT\/AwX+\/Pv6\/\/31+fv9Av\/9AwQGCAQGCgQDAfz\/Avz7+fj9\/fj8\/gAGAv4FBwYIAwEGAgH\/+\/4A+vn59\/8C\/f4AAggFAQYHBgcA\/QIA\/Pv4+v76+vz8AwP+AQMECgkEBAMCA\/37\/v38\/Pb5AP79\/v0DBgIDBQUJBf8BAQAC\/vr9\/Pr7+fwCAAACAAUIBAQEAwUD\/P7\/\/f\/69\/v8\/f\/+AAUDAgMCBgkEAgD\/AwH7+\/v7\/vr4\/QAAAf4ABwUFBgQGBwH+\/v0AAPn6+vr\/\/fv\/AQIFAQEIBwUD\/wEE\/\/39+\/7++Pn8\/gIC\/wIDBAYDAwYEAgH9\/gH++\/r5\/P76\/f8ABAMAAwYGCAUCBAIA\/\/v7\/vv5+vn+Av7+AAIGBgIFBgUFAP0BAP79+\/v9+\/v9\/AEDAAEDBAcHAwIDAgP\/\/P\/+\/Pv4+f\/\/\/v8AAwUCAgQFBwYBAAH\/Af36+\/z7\/Pv8AQEAAQADBwUEBAMEA\/7+\/v7\/\/fr7\/P3\/\/v4BAgIDAwUIBAEA\/wIC\/fz9\/P37+f0AAQIBAQUFBAUDAwQB\/\/\/+AP\/7+fr7\/\/\/9\/wICAwIBBQcFBAEBA\/\/9\/fz9\/vr6\/P4BAf8AAgQGBAIFBQIB\/v4B\/\/z7+\/z+\/P3\/AAMEAQIFBQYDAQEBAP\/9\/P\/9+\/v7\/gH\/\/wACBQUCBAYEBAL\/AAD+\/fv7\/Pv7\/f4AAwEAAgMGBgMDAwIC\/\/z+\/v38+\/v+\/\/7\/\/wIDAgIEBAYFAf8A\/wD++\/z9\/P38\/AABAAECBAYFAwQDAwL+\/v\/+\/\/36+vz9\/\/\/\/AgMDAwIEBgQCAQABAf78\/f39\/Pv9\/wABAP8CAwMEBAMEAgD\/\/v8A\/fv8\/P7\/\/f8BAgICAgQFBAMBAAH\/\/v79\/v78+\/z+AAH\/AAMEBQQCBAQCAQD\/AP\/8\/Pv8\/fz9\/wACAwEBAwQFBAECAgAA\/v3+\/fv7\/P4AAP8AAQMEAgMFBAMB\/\/7\/\/\/79\/P39\/f7+\/wIBAQIDBQUDAgMCAQD+\/v\/9\/fv6\/P7+AAABAwMCBAQFBQEAAQAA\/\/z8\/fz8\/P0AAQABAQIDAwMEAwMCAP7\/\/v7++\/v9\/v\/\/\/wECAgIDBAYFAgEAAAD+\/f7+\/fz7\/P4AAQEAAgQEBAQDBAIA\/\/\/\/\/\/38\/Pz9\/v3+AQECAgECBAQDAgECAf\/\/\/v3+\/Pv8\/gAA\/wACAgMDAgQEAgEA\/\/\/+\/f39\/f79\/v8AAQIBAQMEBAQCAgIA\/\/7+\/\/78\/Pz9\/\/\/\/AAEDAwICBAQDAgAAAQD\/\/fz9\/fz9\/v8BAQABAgMEAwIDAgIA\/v7+\/v39\/P3\/\/wAAAQICAQIDBAQCAAEAAP\/9\/f79\/Pz8\/gAAAQECBAQEBAQDAgD+\/\/\/\/\/vz7\/f3+\/v8BAgECAgIDBAICAQEB\/\/7\/\/v79+\/z\/AAAAAAEDAgIDAwQDAQD\/\/\/\/9\/P39\/f7+\/gABAQEBAgQEBAMBAQH\/\/v7+\/v38\/f7\/\/\/8AAQICAwIDBAMBAAAAAP\/+\/v39\/fz+\/wABAQECAwMDAgICAQD\/\/v7+\/fz9\/f\/\/\/wEBAgIBAgMEAwIBAAEA\/v79\/v79\/f7+AAAAAQIDBAMDBAMCAP\/+\/\/\/+\/fz9\/v7+\/wACAgECAwMDAgEBAQD\/\/v7+\/v38\/P4AAQEBAQIDAgIDAwIB\/wD\/\/\/79\/P39\/f7\/AAEBAQICBAQEAwIBAP\/+\/v7+\/fz8\/v\/\/AAABAgICAwMDAwEAAAAA\/\/7+\/v39\/f7\/AAEBAQIDAwMCAgIBAP\/\/\/v79\/P3+\/v\/\/AAICAgICAwQDAgEAAAD+\/v39\/v39\/v8AAAABAgMDAwICAwEAAP\/\/\/\/\/+\/f3+\/v7\/AAECAQEDAwMCAQEBAP\/+\/v7+\/f3+\/gAAAQECAgICAgMDAgEA\/wD\/\/v39\/f79\/v8AAQEAAQIDAwMDAwIA\/\/7+\/\/7+\/f3+\/\/\/\/AAECAgICAwMDAQAAAP\/\/\/v7+\/v39\/v8BAQECAgIDAgICAQEA\/\/\/\/\/\/79\/f7+\/v8AAQICAQICAwMDAgEAAP\/+\/v39\/f3+\/wAAAAABAgICAwMCAgD\/\/\/\/\/\/\/7+\/v7+\/v8AAQEBAgIDAwIBAQEA\/\/\/\/\/v79\/f3+\/wAAAQICAgICAgMCAQAA\/\/\/+\/f39\/v7+\/wAAAQEBAgMDAwICAgEA\/\/7\/\/\/7+\/v7+\/\/8AAAECAgIDAwICAQAAAP\/+\/v7+\/f3+\/wABAQICAgICAgIBAAD\/\/\/\/\/\/v39\/f7\/\/wABAQIBAgIDAwICAQEA\/\/7+\/v79\/f7\/AAAAAQECAgICAwICAQD\/\/\/\/+\/v7+\/v7+\/wABAQECAgMDAgEBAAD\/\/\/\/\/\/\/79\/f7\/\/wAAAQICAgICAwIBAAAA\/\/\/+\/f39\/f7+AAEBAQECAgICAgICAQD\/\/\/\/+\/v7+\/v7\/\/wABAQEBAgIDAwIBAAAA\/\/7+\/v7+\/f7\/AAABAQICAgICAgIBAAD\/\/\/\/+\/f39\/v7\/AAABAQEBAgMCAgIBAQD\/\/\/7+\/v79\/v\/\/AAAAAQEBAgIDAwIBAAAA\/\/7+\/v7+\/v7\/AAABAQICAgICAQEBAP\/\/\/\/\/\/\/v7+\/v\/\/\/wABAgICAgICAgEBAAD\/\/\/7+\/v39\/v\/\/AQEBAQICAgICAgIBAP\/\/\/\/7+\/v7+\/\/\/\/AAABAQECAgMDAgEAAP\/\/\/v7\/\/v7+\/v8AAAABAQICAgICAgIBAAD\/\/\/7+\/f39\/v\/\/AAEBAQICAgICAgEBAP\/\/\/\/\/+\/v3+\/v\/\/AAABAQEBAgIDAgEBAP\/\/\/\/7+\/v7+\/v8AAAEBAQIDAgICAQEA\/\/\/\/\/\/\/+\/v7+\/v\/\/AAECAgICAgICAQEAAAD\/\/\/7+\/v39\/v8AAAABAQECAgICAgIBAP\/\/\/\/7+\/v7+\/v8AAAEBAQECAgICAQEAAP\/\/\/\/\/\/\/v7+\/\/8AAAABAgICAgICAQEAAAD\/\/\/7+\/v79\/v8AAQEBAgICAgICAQEBAP\/\/\/\/7+\/v7+\/\/8AAAEBAQECAgICAgEAAP\/\/\/v7+\/v7+\/\/8AAAEBAQICAgICAgEA\/\/\/\/\/\/7+\/f7+\/v8AAQECAgICAgIBAQEAAP\/\/\/\/\/+\/v3+\/\/8AAAABAQEBAgICAgEAAP\/\/\/v7+\/v7\/\/\/8AAQEBAQICAgICAQEA\/\/\/\/\/\/\/+\/v7\/\/\/8AAAEBAgICAgICAQAAAP\/\/\/v7+\/v7+\/\/8AAQEBAgICAgICAgEBAP\/\/\/\/7+\/f7+\/\/8AAQEBAQECAgICAQEAAP\/\/\/v\/\/\/v\/\/\/wAAAAEBAQICAgICAQEA\/\/\/\/\/v7+\/v7+\/v8AAQEBAgICAgIBAQEAAP\/\/\/\/7+\/v7+\/\/8AAAEBAQECAgICAQAAAP\/\/\/v7+\/v7\/\/wAAAAEBAQICAgICAQEA\/\/\/\/\/\/7+\/v7+\/\/8AAQECAgICAgIBAQAAAP\/\/\/\/\/+\/v7+\/wAAAQEBAQECAgICAQEAAP\/\/\/v7+\/v7+\/\/8AAQEBAQECAgICAQEA\/\/\/\/\/\/\/+\/v\/\/\/wAAAAEBAgICAgICAQAA\/\/\/\/\/v7+\/v7+\/wAAAQEBAgICAgIBAQEAAP\/\/\/\/7+\/v7+\/\/8AAQEBAQICAgIBAQAA\/\/\/\/\/v7+\/v7\/\/wAAAAEBAQICAgICAQAA\/\/\/\/\/v7+\/v7+\/wAAAQEBAgICAgEBAQAA\/\/\/\/\/\/7+\/v7\/\/wAAAQEBAQECAgICAQEAAP\/\/\/v7+\/v7\/\/wAAAQEBAQECAgICAQEA\/\/\/\/\/\/7+\/v\/\/\/wAAAQECAgICAgIBAAD\/\/\/\/\/\/\/\/+\/v7+\/wAAAAEBAgICAgICAQAAAP\/\/\/v7+\/v7+\/wAAAQEBAQICAgIBAQAA\/\/\/\/\/\/7+\/v\/\/\/wAAAAEBAQECAgICAQAA\/\/\/\/\/v7+\/v\/\/\/wAAAQEBAgICAgEBAQAA\/\/\/\/\/\/7+\/v7\/\/\/8AAQEBAQICAgIBAQAA\/\/\/\/\/\/\/+\/v7\/\/wAAAAEBAQEBAgIBAQAA\/\/\/\/\/\/7+\/v\/\/\/wAAAQEBAQICAgEBAAAA\/\/\/\/\/\/\/+\/v\/\/\/wAAAAEBAQEBAgEBAQAAAP\/\/\/\/\/+\/v7\/\/wAAAQEBAQEBAQEBAQEAAP\/\/\/\/\/\/\/\/\/\/\/wAAAAEBAQEBAQEBAAAA\/\/\/\/\/\/\/\/\/\/\/\/AAAAAAEBAQEBAQEBAQAAAAD\/\/\/\/\/\/\/\/\/\/wAAAQEBAQEBAQEBAQAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAEBAQEBAQEBAQAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAEBAQEBAQEBAQAAAP\/\/\/\/\/\/\/\/\/\/\/wAAAAEBAQEBAQEBAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAQEBAQEBAQAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAABAQEBAQEBAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAABAQEBAQEBAAAAAAD\/\/\/\/\/\/\/\/\/AAAAAAABAQEBAQEBAAAAAAAA\/\/\/\/\/\/\/\/AAAAAAAAAAEBAQEBAAAAAAD\/\/\/\/\/\/wAAAAAAAAAAAAEBAQEBAAAAAAAAAP\/\/\/\/\/\/AAAAAAAAAQEBAQEAAAAAAAAAAP\/\/\/\/8AAAAAAAAAAAABAQEAAAAAAAAA\/\/\/\/\/\/8AAAAAAAAAAAEBAQAAAAAAAAAAAAD\/\/wAAAAAAAAAAAAEBAQAAAAAAAAAAAAAA\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/wAAAAAAAAAAAAAAAAAAAQMFBwgJCAcHAgADAwH9+f3++\/r29\/358PHz+Pv7+Pv+BAP9\/wMECQgDBw8PDQkKDxENCwkICgT+AQICAv76+\/v79vL19vT08u71+vf3+PwA\/vz+AQMFAQEICQoJCQsPDgwICAkIAwQBAQQC+\/n6\/fz49ff39vLv8vf19ff4\/AD8\/PsBBQQCBAQGCwcFBgkMDAoLDAoHA\/8EBgH+\/v0A\/fj19\/r6+Pb29PX28vL2+\/v9\/AACAwIBAgkIBQYICQ0LCAkMDQsHBQUDAwH8\/P37+vr6\/Pv49\/X2+PTy9fn8\/fr6\/gICAgMFBgUFBAQHCQcHCgwOCwgHBgYFAP8AAQD++fn8\/Pn39\/n49fTz9fj6+fz+AAD\/\/wECBQUDBgkKCQcHCQ0LCggHBgQBAgABAgD8\/fz8+vj3+Pf39fX3+fn6+vv+AP7+AAMEAwMGBwgJCAkLCwkJCAcGBQMCAgQC\/\/39\/P78+fr6+fj29vf3+Pj5\/P\/+\/f7\/AAEBAgQHCAYFBgYICgkJCwoJBgMDAwEA\/\/8B\/\/z6+vr6+fj3+Pj49fX4+\/z\/\/v8CAwMDAwUFBQQFBwoIBwgKCQgGBQQEAwH+\/v79\/f37\/P38+vr5+ff29vj6\/Pv7\/gAAAQECBAQEAwUGBwYHBwgKCgkIBwUDAQAAAAD\/\/Pz9\/Pv6+vr6+ff29\/n5+vz9\/gAA\/wACAgMDBAYHBwYGBwkICAcGBgUDAQECAQD+\/v38\/Pr6+fn4+Pj4+fr6+vv+\/\/7\/AAECAwIDBQYGBgcIBwcHBwYGBAMCAgMCAf\/\/\/v78+\/v7+\/v5+fn5+fj5+\/39\/v\/\/\/\/\/\/AAMEBQUFBQYHBwYHCAkIBwQDAwIA\/\/\/\/\/v39\/Pz7+vn5+vr5+fn6+\/z9\/f8AAQIDAgMEAwMDBAYGBgcHBwcGBQUEAgEA\/\/\/\/\/v39\/f38\/Pv6+vr4+Pj5+vv8\/v8AAQEBAwMDAwQEBQUFBQYHCAgHBgQDAgAAAAD\/\/v7+\/fz8\/Pz8+vn5+fn6+vv8\/v\/\/\/wABAQICAgQFBQUFBgcHBgYGBgUDAgIBAP\/+\/v7+\/fz7+\/r6+vr6+\/v7+\/z9\/v7\/\/wABAgECAwQEBAQFBgYGBgYGBQQDAgICAQEBAP\/9\/Pz7+\/z7+vv7+vn5+vv9\/v7\/\/wAAAQICAwQEBQYGBQUFBgYGBQQDAwIBAP\/\/\/\/7+\/f39\/Pv7+\/v7+vr7\/Pz8\/f7\/AAEBAQIDAgIDAwQFBQUFBgYGBQUEAwEAAAD\/\/\/7+\/v39\/Pz8\/Pv6+vr6+vv8\/f8AAAABAQEBAgIDAwMDBAUFBgYGBQUEAgEBAQD\/\/\/7+\/v79\/fz8+\/v7+\/r6+vv8\/f7\/AAABAQEBAgIDAwQEBAUFBQUFBQQDAgIBAf\/\/\/v\/\/\/v79\/f38+\/v7+\/v7\/Pz9\/f7+\/\/8AAAECAgMDAwMEBQQFBQUFBQQDAwICAQEAAP\/\/\/v39\/Pz8\/Pz8+\/v7+\/z9\/f7\/\/wAAAQECAgICAwQEBAQEBAQEBAQEBAMCAQAAAP\/+\/v7+\/v38\/Pz8+\/v7\/Pz8\/f7\/\/wAAAQECAgICAwMEAwQEBQUFBQQEAwIBAAAA\/\/\/\/\/v7+\/v39\/fz8+\/v7+\/v8\/f7\/\/wAAAQEBAQECAgICAwQEBQUFBQUEAwIBAQAA\/\/\/\/\/\/7+\/v39\/fz8\/Pz7+\/v8\/f7\/\/wABAQEBAQECAgMDAwQEBAQEBAQEAwMCAQEA\/\/\/\/\/\/7+\/v79\/fz8\/Pz7+\/z8\/f7+\/v\/\/AAABAgIDAwMDAwMEBAQEBAQEAwMCAQEAAP\/\/\/v7+\/f39\/f39\/fz8\/Pz9\/f3+\/wAAAAEBAQICAgMDAwMDAwQEBAQEBAMCAgEBAP\/\/\/\/7+\/v39\/f39\/Pz8\/Pz8\/f3+\/\/8AAAEBAQECAgIDAwMDBAQEBAQEAwICAQEAAP\/\/\/\/\/\/\/v7+\/v39\/Pz8\/Pz8\/f3+\/\/\/\/AAEBAQEBAgICAgMDBAQEBAQEAwMCAQAA\/\/\/\/\/\/\/\/\/v7+\/v39\/f39\/Pz8\/f3+\/\/8AAAABAQEBAQECAgMDAwQEBAQEAwMCAgEBAAAA\/\/\/+\/v7+\/v39\/fz8\/Pz8\/f3+\/v\/\/AAAAAQECAgICAwMDAwMDAwMDAwMCAgEBAAAA\/\/\/\/\/v7+\/v7+\/f39\/f39\/f39\/v\/\/AAABAQEBAQICAgIDAwMDAwQDBAMCAgIBAQD\/\/\/\/\/\/v7+\/v7+\/f39\/f38\/f3+\/v\/\/AAAAAQEBAQICAgIDAwMDAwMEAwICAQEBAP8AAAD\/\/v7+\/v79\/f39\/P39\/f\/\/\/\/\/\/AAAAAAEBAgIBAgMDAgICAwMDAgIBAQAAAQAAAP\/\/\/\/\/+\/v3+\/v79\/f3+\/v7+\/v8AAAAAAAEBAAECAgICAwMDAwMCAgICAQEAAAEA\/\/\/\/\/\/\/\/\/v7+\/v79\/v7+\/v7+\/wD\/\/wABAQEBAQECAgIBAQIDAwMCAgICAQEBAQAA\/\/\/\/\/\/\/+\/\/\/\/\/\/7+\/v7+\/f7+\/\/\/\/\/wAAAQEBAQEBAQECAgICAgICAwIBAQEBAQAAAAAA\/\/\/\/\/\/\/+\/v7+\/v7+\/v7+\/\/\/\/AAAAAAABAQEBAQECAgICAgICAgIBAQEBAAAAAAAA\/\/\/\/\/\/\/\/\/v\/+\/v7+\/v\/\/\/wAA\/wD\/AAABAQEBAQEBAQEBAgICAgIBAQEAAAAAAAAAAP\/\/\/\/\/+\/v\/+\/v7+\/v7+\/\/8AAAAAAAAAAAABAQEBAQECAgICAgIBAQEAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/\/\/+\/v7+\/\/\/\/AAAAAAAAAAABAQEBAQEBAQEBAgIBAQEBAQAAAAAAAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/v\/\/\/\/\/\/\/wAAAAAAAAEBAQEBAQEBAQECAgIBAQEBAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/\/\/+\/v\/\/\/\/\/\/AAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAAAAAAAA\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAECBAUGBggKDQsJDA4NDAgKCwoGAgMHCAEAAP8A\/vz6+\/\/++Pf4+fv79fT4+vf29Pbz8vPz8vfz7fD19Pf18fH4+vjz+Pz9\/P77\/gQC\/wIFBQMBAwYHBwQDCQsJCAkJDQwKCAsPDQgLCw4PDwgHCw8NCAQHBgUDAP8EAQAA\/vwA\/vv5+\/z7+Pr6+vz48fL29\/f39vX29fTv9Pfz8\/T1+Pj18\/b7\/vz5+\/z9AP\/+AAIDBAMECAgGAgMICwYHCQoKCwcHCQsNDAoMCwwMCQoMCwsLCwoJBQQDAwYD\/\/8CAgH\/\/fz\/\/fn5+\/v8+vn2+fr29Pf4+vf19ff3+fTy9fj49vHx9vn4+Pn8\/fz8\/f4AAv8AAgUFBgUEBQgJBwUHCAkHBgcKCQkJCgsKBwgKCg4NCgsNCwgHBwcGBQL\/AgQCAP\/9\/gD++\/v8\/fr3+Pn6+vn3+vr39\/b29\/j39Pb4+fX09PX3+fb3+Pr6+\/z\/AAH\/\/gAFAwIBAwMEBAYHCQkIBgcHCAoJBwgJCgoKCgoKCQkKDQ0KCAkIBwYGBAQCAQAAAAD\/\/\/39AAH+\/Pj5+Pr7+\/r7+vb2+fr6+Pb19vb29PX29vf49\/j7\/Pv9\/vz8\/P3+AAIB\/wEDBAUFBAUGBAQFBwcHBgYGCAkICAoKCgkJCQoKCgcGCAkHBgYGBgYCAQEBAQD\/AP\/+\/vz8\/fv5+vr6+vr5+fn7+vn39\/f49vb3+fn49vf4+Pn5+fn6+\/v9\/gAAAAAAAQL\/\/wIEBQUDAwQGBgYGBwgIBwYGBgcHCAkICAcHBwgKCAYHBwcHBgQEBAIA\/v7\/\/\/\/\/\/\/79\/Pr7\/Pz9\/Pv7+\/v6+vv7+vr6+fr7+vn4+Pj4+fj4+fr6+\/z8\/f3+\/wAAAAACAgMDBAQDBAQEBAUGBgUEBAMEBQUGBwYFBQYHBwcHBgcICAcGBAMCAgEBAQEBAQH\/\/v79\/f7+\/fz8\/Pz7+\/v7+\/v6+vv6+fn5+fr6+vn6+vr5+vv8\/v38\/P3+\/v\/\/\/wACAgICAwIDAgIDBAMDBAQFBQUEBAUGBgUFBgYGBgYGBgYFBAQEAwQEAwICAQEBAP\/+\/v7\/AP\/+\/v7+\/v37+\/v7+\/z8+\/v6+\/r7\/Pz8\/fz7+vr6+\/v8\/Pz8\/f3+\/v8AAQEBAQAAAQECAwMDAwMEBQUEBAQEBAMDAwQFBQUFBQUFBAUFBgYFBAQDAgIBAgIBAAD\/AAAAAP\/\/\/\/79\/f39\/v79\/f39\/Pz8\/Pz8\/Pv7+\/v7+\/v7+\/z8\/P3+\/v7+\/v7+\/v8AAQEBAQECAgIDAwICAQECAwQEBAMDAwMDBAUFBAQEBAUFBQUEBAQDAwMCAQEAAAAAAAAAAAD\/\/\/7+\/v7+\/v7+\/fz8\/Pz8\/Pz8\/f39\/Pz8\/Pz8\/Pz7+\/v8\/f7\/\/\/\/\/\/\/\/\/\/wAAAAEBAQICAgIBAgICAgMDAwMDAwMEBAQEBAQEBAMDAwMDAwICAQAA\/\/\/\/\/\/\/\/AAECAwQEBQYGBAMEBQUFCAoIBQICBwoIBQQGCAgGBQQFBwcGBQYJCQQBAQD\/\/v4AAP79\/fz9\/fv5+ff6\/Pz8+\/v49vb4+Pf29vn8\/Pr39vf39vb3+Pf39vn59\/b29ff5+vf19\/j3+Pf5+vn29vX3+fr5+Pj49\/f3+v38+vj4+vz8+\/v8\/fz8+vn7\/P7\/AQEBAgICAwMDAwQFBgYGBQcICAcHBwcHBggJCgwKCAgJCQgGBgkKCwkICAgGBggLDQ4MCQcICQoLCwoKCQgJCgsLCwwLCQcGBgUHCAkKCgkHBgUFBgcHBgYGBQUFBAQEAgEA\/\/7+\/wABAgL\/\/Pz7+\/r6+fj4+fn5+fn5+\/r5+Pj39\/j5+vr5+Pb29fX2+vz8+fb09fX3+Pn5+Pj4+Pf4+Pf5+vv6+vn6+fj3+Pr9\/v78+\/n49\/j6\/Pz8+\/v8+\/z9\/v\/\/\/f8AAQIEBQQEBAQCAgIDBQUEBAUGBwcGBwYGBwgICQgHBwcICAcGBwkKCggGBQUGBgYHBwgJCQkHBQUGBwcHBwcICAkJCAYFBAYGBwgHCAgGBQYGBgYFBQQDAwMDAwMBAP\/\/\/v79\/v79+\/n7\/P38\/Pz8\/Pv6+vv7+\/r6+\/v7+vn7\/Pv7+vj39\/j5+fr6+fn5+fn5+Pj5+vr6+fn6+vr5+fn5+fv7\/Pz7+fj5+vv7+\/r6+vv7+\/v7+vn5+vz8\/f3+AP\/+\/v8AAQIBAgQEAwMDBAQEBAQDBAQFBwgIBwYFBQYHBwcGBgcIBwcGBgYGBwgIBwYFBQYGBgcHBwYGBQYHBwcHCAgIBwUEBAQEBAQGBwYFBQQDAgEBAgMEBQYFBAMCAQAAAAAA\/\/7+\/v7\/\/\/\/\/\/v79\/P38\/Pz8\/Pv7+\/v7+\/r6+\/v7+vr6+vr6+\/v6+vn4+fn7\/Pz7+\/v6+vv7+\/v7+vr7+\/z8+\/v7+\/r6+vv7+\/r7+\/v7\/Pz9\/fz8\/Pz9\/v7\/\/wABAAAAAAAAAAECAgICAgMDAwMDAwMDAwQEBQUEBAMEBAQDAwMEBQUGBgYGBgYFBQUEBAQFBQQEBAMDAwMEBAQFBAQEBAUFBAQEBAUEBAMDAwQFBQUFBAQDAwMCAgICAgEBAAD\/\/\/\/\/\/\/\/+\/wAA\/\/\/\/\/\/\/\/\/\/7+\/f39\/f38\/Pz7\/Pz8\/Pz7+\/r7+\/v8\/f38+\/v7+\/v8\/Pz8\/Pv8\/Pz8\/P39\/f79\/fz8+\/v8\/P39\/f7+\/v7+\/v79\/f39\/f39\/f39\/v\/\/AAAAAQEBAQEBAQAAAAEBAgICAgICAgIDAwMDAwQEBAQEBAMDAwMDAwMEBAQEBAMEAwMDAwMDAwMDAwMDAwMEBAQEBAQEAwMCAgICAgICAgICAwMDAwMDAwMCAgICAgEBAQAAAP\/\/\/wAAAAAAAAD\/\/\/7+\/v7+\/f39\/v7+\/v79\/f39\/f7+\/v7+\/v79\/f39\/f38\/Pz8\/f39\/f39\/f39\/f39\/f39\/f38\/f3+\/\/8AAAEBAQEBAQIDAwQEBAQFBgcHBAIDBAcKCQcEBAYFBAYJDAwIBAMGCggFBAQFBgQDAwUFBAMB\/f3+\/fv8\/f37+\/r8+\/z+\/v36+Pr9\/Pf5+Pn7\/Pv39\/j4+fn39fX39\/X19vj7+\/n6+vf2+fr5+Pj29ff3+fn6+vn59\/b19\/v+AAD9+\/j3+v3\/\/fv6+vr6+fn8\/P3\/Af\/+\/wAEBgUEAgACBAcHBgUEAwQGCAgJCggHBgcFBAcICgkICAkIBwcJCw0MCwoJCQsMCgkJCgsKCggJDAwLCwsLCAUDBgcFBAUICgoIBQQEAwMGCQoJBwYEAgECBAMBAQICAQICAP7+\/wD\/\/v3\/\/v369\/n8\/v79\/Pv6+\/v59fT3+fr5+vn4+Pn7\/Pz59\/b39\/f4+vv6+Pf39\/f39\/j6+\/r6+vz8+\/n4+fn5+Pn9\/fz7+\/r6+\/r59\/n7\/f38+vr7\/QACAf\/\/\/wIDAQABAgMCAgQFBQQEBQYFBAUGBwcHBwYFBgcICAcHCAgJCgoIBgQFBQUGCAkJCQgGBggHBwcICAcHBwUFBwkJCQcFBAMDAwUGBgUDBAYJCQcFBAUFBQUDAQEA\/gABAgIB\/\/78+\/v7\/f79\/f38\/Pv6+vr6\/Pz8+\/n5+fj2+Pn6+\/v6+fj4+Pj4+Pf39\/n6+ff39\/r9\/v37+fn4+Pj4+Pn6+vz8+\/r5+fn7+\/z7+\/z8\/Pz7+vr7\/f7+\/v\/\/\/wAAAAAAAQECAwIBAP\/\/AQICBAUGBQUFBAMEBQYGBwcHBwcHBQQEBQcJCQcFAwMEBQYGBwgICAgICQgGAwICAwQFBgcGBgUFBAUFBgYFBAQEBAQFBgUEBQYGBgQCAQECAwMDAgH\/\/\/7\/\/v79\/P3+\/v7+\/f38\/Pz8\/Pz7+\/r7\/Pv7+vr6+vv8\/v7+\/vz7+\/v6+fj5+vv7+\/v7+\/v7+\/v7+vn5+vr6+vv7\/Pz9\/Pz8\/P3+\/v79\/Pr6+\/z9\/f39\/f39\/f7+\/v7+\/wABAQIDAwMCAgIBAQICAgMDAwMDBAQFBQUGBgUFBAMCAgMEBQUFBAMDAwMDBAQFBQUEBQUFBQUEBAMDAwQFBQUFBQUEAwICAgMDAwQFBQQDAwMDBAQEBAQDAgEBAP\/\/\/\/\/\/AAEBAQEBAP\/+\/fz8\/Pz8\/f7+\/f39\/fz8\/f39\/fz8\/f39\/fz8\/Pz8\/f7+\/v7+\/v79+\/v7+\/r6+\/z8\/Pz8\/f39\/f39\/f39\/f39\/f38\/Pv8\/f3+\/v7+\/v7+\/f7+\/v7\/AAEBAgEBAQAAAAAAAAABAQEBAQIDBAQEBAQDAwMDAwMDAwQEBAQFBQQEAwMDAwMDAwMDAwMEBAQEBAQEBAMDAgIBAgICAwMDBAQEBAQDAwICAgICAgICAgICAQEBAQEBAQAAAQEBAAD\/\/\/\/+\/v7+\/v7+\/v7+\/v7\/\/\/7+\/v7+\/f39\/v39\/f39\/f39\/f39\/f39\/f39\/f39\/v7+\/v39\/f39\/f3+\/wAAAAABAQEBAgQFBggJCgkICQoHCgwLCwgGBggGAAQGAwICAf37\/f3\/\/\/z19fr++vb1+\/r38vLz9\/n28\/T09vP3+fLv9Pf09PX7+\/Xw8\/n+\/\/j7AwMBAP8CAAUFAAEGDw0QEQ8IBwkLDAcFCgsKCwwODgwODw0MDQkMDwwKBwcHBwcD\/f0B\/\/79\/AAC\/ffz9ff6+ff4+ff4+vXx7O30+v338vP3+ff2+PLx8PT3+fv6+f7\/\/\/8BAwMCBQcFAP4BCAsKBgcKDQsMBQIIDhEOCAYHCxMVDwwICQwLCwwIAf\/\/AwkKBAD8+Pz\/\/gD\/\/fz\/AP779PL5\/v36+Pj39vT19\/j6+PX08\/Dz+fz68\/Dz+v79\/P76+fwBAf8AAAQGBgQFBQkHAwMFCwkHCAoKCAcHBwwPCwoJCgkICAoJDAsHBgYGAgADBgYD\/\/4AAwUC\/Pr7\/AD\/\/ff09ff6+vj4+fn39PTz9Pb29vb49\/n7+ff39fT4\/f\/\/\/v7+\/v7+AgMCAQICBAYICAoLCQQBBAkMDQoJBwYHBwgJCAcGCQoHBwoLCQgGBQMFBQIC\/gACAgD\/+\/n6\/f7+AP\/9+\/n5\/P779vX1+Pn6+Pj18\/P19\/b08\/T5\/P3+\/Pn7+\/38\/f4AAAH\/\/gAEBwgHBwcEAgMGCAcFBgYGCQwPCwgGBwsLCgsLCQkIBwYDBAICBAYGAv79\/f8B\/\/v9\/f3+\/fv6+vr5+v3+\/fr59vf5\/Pz59fPz8\/X4+v38+ff3+fr6+\/z9\/Pz9AAECAgD\/AQMFAwIDBQcIBwYICQsLCQYEBAYKDAwJCAYFAwQICwsKBwMCAgICBAYFAf36+fv+\/\/79+\/v6+\/3+\/fv6+\/z7\/Pz7+fj4+Pn8+\/n29vf6+\/n49\/j5\/AACAf\/+\/gACBAMCAQIBAgMEBQUGBgQDAQIDBAUFBgYGBQYHBwYGBQUHCQkKCAcFAwIDBAMB\/\/79\/P3+AAAA\/\/79\/Pz8+\/z7+vn6+\/z8\/Pv6+Pf4+vz8+\/r5+fn5+fr5+fv9\/wEBAf\/9\/QABAgMDBAUFBAMBAQEDBAcICAcGBgcHBgUFBQQEBgkIBwYFBQYFBQQDAgIBAP\/+\/f3+AAAAAQEA\/vz8\/P39\/fz8+\/v8\/Pz8\/Pv6+vr6+\/r5+fr6+fn5+\/3+\/\/\/\/\/\/8AAAAAAAEBAgICAQICAwQFBwcGBAICAgQFBgYGBgUEAwIDAwQFBQUEBAQEAwMCAgEBAQEA\/\/7+\/wAAAP78+\/v6+vv7+\/z9\/v7+\/fz7+\/v7\/Pz8\/Pz7+\/v8\/Pv8\/f7\/AAABAP\/\/\/\/8AAQICAwMEAwMCAQECAwQEBAUEBAQDAwMEBQUGBgYFBAQEBAQEBAQCAQD\/\/\/\/\/\/\/\/+\/v7+\/v7+\/wAA\/\/\/+\/v39\/Pz8\/P39\/f38\/Pz8\/Pv7+\/v7\/Pz9\/f39\/v8AAAEAAAABAQICAgICAQICAgICAgICAgICAwMDAwQEAwQEBQUEBAIA\/\/8AAAECAgMEBQYKDxILAgQKA\/r3\/fvz9vX++vX48u\/v+Pzu+fz5\/\/7+BQr\/+wEMCAEPDAgOEhAKBwgODw37+gMDBQD8+fn7AP\/87u3x6+r19O7w8\/79+\/oACP38A\/0ACQkJDQkTDhASCREPBwwJBvn5BQD6\/QT48\/X59vT4+Pbx9\/ry9\/n3+fkA+\/v5AwoH\/AENDAkOExEECg8KAwgOAf8DAPsAAgEB\/fX2+\/v29\/Dv8\/L29\/f19vz8+vz\/AAMFBQIDBAYKDgwKDxIKDQgIBQQFBAUFAvn0+AD19Pn68\/D5\/vbu9PX69fz\/BQcAAAMB\/wQFBgcLCwkICAsLEgwLAwIGAwP++fr+\/\/\/78\/Xw9vXy8vT29\/T0+wEAAvv7+wUFAAoJCAwJBwYMDhEQEA4DAwUICQUA\/v35\/P36+\/X19vf3+fPs7\/f8APr19vwDBAD8AgYMDgwDAQkLCAoKCQ0PCAgKDQn\/9vr\/BQX\/+\/z7\/PTz8\/n28\/Dy9fcA\/\/r6\/f4BAAUIBggHAgIKDAkKDQ8NCQUEAAMCAwIDBQD8+fX1\/fz5+vr27\/P7\/fv5\/Pz7+gEDAwMDBwoMCwYDBQcKBgYMEBAHAv8EAwEC\/\/7\/\/fz+\/PX09\/z9+\/j18\/H3\/f78+\/3+AP4A\/v8BAQEGDAwKCgoPEAoHCAgGAP3\/\/\/z6\/f7\/\/fv59vr7+vXz8fb5\/QD8\/Pz+AwYEBgUEAgAHCwkFBAgLDQsGBgYHBQIAAPz7+\/r6+vv5+Pj29\/v6+Pf4+\/36+fr+AwL\/\/f8EBAQHCwoLCQcKCwoJCQYF\/\/3+AQMB\/vz7+\/v8+vr6+vr5+Pr6+fn7+\/n4\/P8DBQUDAgIDBwgIBwUEBggHBgQFBwgHBAIA\/vz6+v39+ff5\/Pv5+fj3+v39\/f79\/wH\/\/v8CBgkJBwYFBQUEAwQHBwcHBgMA\/v8A\/\/\/\/\/fv7\/P36+ff4+vr7\/Pz+\/f38+\/3+AgQGBgYEBQcIBwQDBggKCQYEAgABAgD+\/P0AAgD++vj4+fv9\/v36+vr6+\/v9AAD+\/v8BAwUGBgUDAgQEBQUHCAcGAwAAAQIBAAD+\/v7+\/f\/+\/Pr6+\/v5+fr7\/P38\/Pz9AAICAgECBAQEAwMFBQYHBwcFAwIBAQICAQAAAAAA\/v79\/Pv7\/Pz8+\/z7+vr7+\/3\/AP\/+\/gADBgUFBQYGBQQDBAYHBwYDAQAA\/wABAf\/9\/Pv7\/P39\/fz7+\/v8\/f38\/P3+\/v3+AAIDBAQDAwMDBAQFBgYFBQUEAwICAQAAAAAA\/\/38\/Pz7+\/v7+vv8\/f39\/P3\/AQICAgIBAQECAQECAwQFBgUFBAQEBAMCAf\/\/AAAA\/\/79\/Pv7+\/z9\/f38\/Pz9\/v7\/\/\/\/\/\/wABAQICAgMDAwQFBQQDAwQFBQUDAQD\/\/v7\/\/\/\/\/\/v39\/f39\/fz8\/Pz8\/P3\/AAAAAAABAQECAwMDBAMDAwICAwQEBAICAQAAAP\/\/AAD+\/Pz9\/f38\/f38+vv8\/f39\/wICAgIBAAECAgIBAgQDAwQDAgIEAwIBAQMDAQEA\/v7\/\/v\/+\/P39\/P39\/Pv7\/f7\/\/wABAgAAAAD\/AQICAQIDBQYFAwMCAgD\/AAABAAECAQD+\/f\/\/\/v38\/v39\/vz8\/Pz8\/v8AAQEAAQMDAgD\/AgMDAwMDAgMDAwIBAQEBAAAAAP\/+\/v\/\/\/v3+\/v7+\/f39\/f\/\/\/\/\/+\/wAAAQICAQAAAgMCAQECAwMEBAIAAQEBAf8AAP8AAQD+\/v\/\/\/v38\/Pz9\/v7\/\/\/7+\/wAAAAABAgIBAgICAQIDAwIBAQICAQEA\/wACAQD\/\/\/\/+\/v7+\/v7+\/\/7+\/f79\/v8AAQAA\/\/8AAQEBAQEBAQICAwMDAwMDAQEBAQEA\/\/\/\/AAAAAP\/\/\/v39\/v7\/\/\/7+\/f7\/\/\/\/\/AAEAAAEBAQEBAQIBAgMDAgEBAQEBAQEBAAAAAAD\/\/v7+\/v\/\/\/\/\/+\/f3+\/\/\/\/\/v\/\/AAAAAAEBAQEBAgICAQABAgICAgIBAQEAAAD\/AAAAAP\/\/\/\/\/\/\/\/\/\/\/\/7+\/v\/\/\/\/\/\/\/wAAAQEBAQEBAQEBAQICAgEBAQEBAQECAQD\/\/\/\/\/AP\/\/\/\/\/+\/\/\/\/\/\/\/+\/v7+\/\/\/\/AAAAAAAAAAECAgECAQEBAQEBAQICAgIBAQAAAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/v7+\/wABAQAAAAAAAAEBAQEBAQEBAQEBAQEBAQAAAAAAAQAA\/\/8AAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/v\/\/AAAAAAAAAQEBAQEAAAEBAQEBAQEBAQEBAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAQEBAQEBAQEBAQEBAQEAAQEAAAAAAAAA\/\/\/\/AAD\/\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAD\/\/wAAAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAQEBAQEAAAABAQEBAQEBAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/8A\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAABAQAAAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD\/\/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAAEA9Pb5AgX6\/AP\/CwoNDQ0ICQcQCfz9+gL38O\/3\/P78AAAR\/v0KFwkQBwYI8gkB9u358\/L0\/vP68gv\/AAkJDBYKC\/wPAvMA+fr68ezx\/f7wAf\/\/DAr\/BhYNCwUE9\/\/++vXv8fby+vEG9vwK9gYKCQwRC\/8C+AwH+vr6Avzw7f\/z\/\/4BAA8HAgINCwcGAwH6\/wb96vjz9PD+\/Ab6AAkECRIIBgoB+gEA\/PkE+uj86\/\/5+\/4BCAcIDgcIDAgFCAfx\/AT3+fX08\/rzBPX5CQEGBwgCDQkICwcA\/fz38QH47APy+v7\/\/AUBAg4UBgoHBgn\/\/f758\/j29Pby9gAG\/QIEBgb\/Ew4MBAr6Af789vb48QD+9\/z+Af4ACgf\/FgoGBwX5+wr3Avf19\/T09Pz7\/QQMBAYJCAwHCPsNCPf\/\/Pr2\/vP5\/Pj+AAEC+wsKBQoNBwUA+gQE+Pr29fb3+v73\/gEGCAcHCgsSBQYAAQAB+\/v6+\/b49vT\/AQECBAcHBgYQBAIGAwL\/9v75+vj2+fv9+AT\/BwoHCwkKCQkMAPr9\/\/f7+vj4\/PT\/\/fkFBQQHDAYGDQwCAwL\/\/fgB\/\/L2\/vj5\/gD8AQEMBwsPCAYFBQMDAgH1+fn2+Pn1\/gUD\/QIFCAgKCAgHAvsA\/\/z+\/fjy+Pf8\/f0AAgMIAQoEBwkFAAQAAf4B9PT6+Pf8+AEI+\/8IBAUICQQKBv7\/\/gD8+vn69vf4\/QP+\/wIFBQMMCgwHAgIB\/f8B9vYA+fb4+\/r+\/wABDAcEBwkCAAkF\/fb9\/vv5+P35+vv\/AgMCAwUKBAQNBQAAAQD9\/Pn6+vr8+vz+AgQC\/gAFCwkHBQIEAQT+\/fz6+fj6\/\/76+\/4EBAQFCAQFBgQFBP\/+\/f76+Pf6+\/v9\/wEHAgQDBwoIBgQEA\/r\/\/vn7+\/n4+f39\/QH\/BQYEBQcLBAYG\/\/4A\/vz7+Pn6+Pj7AAAAAgMICQYHAgMGAwD\/\/v\/5+\/z4\/P38\/f8D\/v8ICAYEBgYEAgAA\/v76+vz6+Pr8\/gP+\/gQGAgMHCAYCAQL\/AAH9+vr59\/sCAfr8AQMEBggCBAoIAP8AAP7++vf6\/v77+\/4ABAEDBggGAgQFBAH+Af79+fn6+vn6\/Pz7AAUHBQMFBwcGBAABAgD9\/Pv7\/fr5\/f3+AAECBAIGCQUFBAIBAP\/9\/\/\/5+Pj6\/P7\/\/v3\/BgUGBQYGBQEB\/\/\/+\/fz7+\/r7\/v79AAABAgQFBggGAgIEAf\/\/\/vz8+vj7+\/sAAgEBAgQEBwkHAwH\/AAEA\/v38\/fv4+v3\/AAABAQMGCAUFBQQCAQL\/+\/r8AP\/6+fv+\/gEBAQQEBAUGBgIB\/wAC\/vz8\/Pv6\/Pz8\/wMEAwICBAQDBgcB\/wEB\/\/39\/f79+vn7\/gABAgIEAwQGCQYDAAAA\/fz9\/vz5+fv9\/\/8AAwICBAQFBgUDAgEA\/\/79\/Pv7+vr9AAEB\/\/4BBAUFBQQEAgAA\/\/\/9\/Pv7\/fz6\/gAAAQECBAUEBQUEBAIA\/\/79\/vz7\/f38\/P7\/AQECAwMGCAYEAQD\/\/wD8+\/r8\/Pv7\/P4BAgH+\/gIFBgYFBAEAAP79\/Pz8+\/v7\/f8A\/\/8DBAQEBQYEAQL\/\/\/\/\/\/v38+vv+\/f3+AAECAQUCBAQCAwIBAP\/+AP\/9+vv7\/v7\/AP4GBAUFAwMFAwIB\/wIB+v78\/vr8\/f7+AQH\/BgEGBAUEAwb9AwD9\/\/38\/Pv8\/\/37AgMAAgMDBAUDBgX\/\/wAB\/f38\/Pv9\/PoE\/AIBAgIDBAMFAgUA\/gD\/\/v39\/\/j5AfkA\/wABAgL\/AwYIAQMBAQD\/\/QH7\/vr9\/P7+\/wD9AQUACQEFAwMCAv4B\/wD7\/vv7\/P3+Av8B\/wEGAgQEBAMCAwL7\/wH8\/fz7\/P4A\/AACAf8DAwQEBAYBAv79Af7+\/fz7+P8B\/v4BAQICAAcGAQIDAQEA\/\/7\/\/Pv9+\/r\/\/gAAAQQABgMBAwcCAgEA\/\/79\/vz9\/fz+\/v8AAf8EAQYEBAQCAgEAAAD9\/f39\/P3+\/v8AAQQCAQMEBAQCAgL+A\/\/+\/f38\/PoB\/v0C\/wEBAgMDBAEBBQH9Af\/+\/v36\/Pz\/\/v\/\/AAEBAQUABgAFAQEBAP\/+AP\/9\/vv9\/v7\/Av4AAgUCBAQDAgEAAAH8\/f78\/fz9\/v4B\/gQBAwIDAwQDBQAB\/gD\/\/v79\/P78\/\/79AQABAgIDBAIFAQT+AQD\/\/\/79\/f\/5\/f4CAAABAQIDAwQFA\/4DAAD\/\/\/79\/fz9APwAAAABAQICBAMDAwL+AQD\/\/\/4A\/fz\/+\/7\/\/wABAQECBQICAwIBAQAA\/wD7\/v78\/v7\/\/wEC\/gQAAwMDAwID\/wIA\/v\/+\/v39\/vz+Av8AAQECAgIFAgECAQAA\/\/\/+\/vz\/\/fz\/\/\/8AAQECAQQCAgIBAQAA\/\/\/+\/v79\/v3\/\/wAAAQADAQIDAgMBAQEA\/gH\/\/v79\/f7\/\/wEAAAECAwEDAwICAQEB\/\/3\/\/v7+\/f7\/\/\/\/\/Av8BAgICAwICAQEAAP0B\/\/7+\/v7+\/wD\/AAECAgECAgICAQACAP\/+\/\/7+\/v7+AP7\/AQABAgECAgIBAQMA\/gD\/\/\/\/+\/v7+\/\/\/+AAIBAQICAgIAAQIBAAD\/\/\/\/+\/v7\/\/\/8B\/wABAQECAgIBAAEBAf\/\/\/\/\/\/\/v7\/\/\/4BAQABAQEBAgIBAgABAQD\/\/\/\/\/\/v7+\/\/\/\/AAAAAQEBAQEDAQABAAAA\/wD\/\/f\/\/\/\/8AAAABAQABAgICAQEBAAD\/\/\/\/\/AP7+\/\/\/\/AAD\/AQIBAQICAQEBAAAAAP\/\/\/\/7+\/\/\/\/AP8BAAABAQECAQEAAQH\/\/wD\/\/\/\/\/AP8A\/wEAAAEBAQECAQEAAAAAAP\/\/\/\/\/+\/v8AAAAAAAEBAQIBAgEAAAAAAP\/\/AP7+\/\/\/\/AAAAAAEBAQEAAgIAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAABAQABAgEBAAAA\/wAA\/\/\/\/\/\/\/\/AAAAAAEBAAAAAQEBAQEAAAABAPv7+vn0+Pv9AAIEBw0LDQ8QDQoHBAH\/\/fr49u765vb6\/QACBAcGDwcPDAkGAwH++\/v37PLx8\/b5\/P0ABQETBwsNCwgGAwEA9Qbr9vDy8\/b5+\/4HCAACCgoMCwkGAwAB+f319vTy8vX49wMN9QcGCAoMDAkGBgf6+fv59\/Xz8vXq+gAEAgQGCAoLDQoD+QIJ\/fr5+Pb08vXyAfwAAQUBBwkLDQoIBP8D\/gD2+Pb18\/X4\/f3\/AQQFBwkLDAoGBQQAAv37+ff19PX6+vwA\/wMFBwkKDAsJCf4G9An9+vj29PX3+gH4BgkDBggKCwsJBQD6DfP7+vj39fT39QEIAvwDBggJCwwE\/wUMB\/78+vn39ekD8vv9AAIEBgcJCwQIDQj6Af\/9+\/n49u7++\/7+AAIDBQcJCRX\/EfwEAf\/9+\/r49\/Xt9gL9\/wEDBQcICgYV\/gb7AQD+\/Pr5AvP8+Pv9\/wEDBAYICQkQAAgGAgD+\/Pr59\/T5+vL5AgECBAYHCQsJCv0FBAD+\/fv5+Pb2+Pj++\/4CBAYHCQoLCAECCPcF\/vv6+Pf2+PoD9\/wDDAUGCAkLCAb+DP76Bvn6+ff2+Pr89wEFBwQGBwkKCAgCAvwE\/vz6+fj2+Pr8\/fwG+wwFBwgKCQcFA\/4J9gL6+fj29\/n7\/f8F+wMFDQkJCQcFAwEA\/vz\/+fX49\/n7\/f8AAgMFAw4FDgYFAwEA\/v389Pn69fn7\/f4AAgMEBwgLDggFAwIA\/v37+vnz9\/v8\/P4AAQMEBQENBwYGBAIA\/\/38+\/339vn6\/P7\/AQIDAAYLCAgGBAIB\/\/0B\/\/L99vj6\/P7\/AQIEBv8IDwcGBAIB\/\/78+\/j8+PL8+\/3\/AQIDBQYIDggBBgIBAP79\/Pr3+fT7\/P3\/AAIDBAMKBQoLBAMBAP79\/Pv49\/n++\/3+AAEDBAUGBwgLAAUBAP\/9\/Pv6+fj6+\/z+AAECBAUGBwgGBgECAP\/+\/Pv6+vf5+\/3+\/wECAwUGBgkGBQMCAf\/+\/fv6+ff5+\/3+\/wECAwQGBwgHBQMCAQH+\/fz6+fj5+\/3+\/wECAwQFBgcHBQQCAAD+\/vz7+vn5+\/z+\/wEBAwQFBgcHBQQDAAH+\/vz7+vn4+\/z9\/wABAgQFBQcHBQQDAQAA\/vz7+vn4+vz9\/wABAgIFBQcHBgQDAQD\/\/v75\/Pn5+vz9\/gABAgMHAgkHBgQDAgD\/\/v38+vr2+\/v9\/gABAgMFBQcFBwQDAgH\/\/v3+9\/z5+vv9\/v8BAgMEBQcHBQUDAgEA\/v39\/Pr5+vv9\/v8AAQMEBQUGBgYDAgEA\/v38+\/r6+fv9\/QH\/AQIDBAUGBgMEAwH\/\/\/79\/Pv6+f75AP4AAQIDBAUGBwQGAQEA\/\/79\/Pv6+fv8\/\/wBAwEDBAUGBgUEAgEB\/gH5\/fv6+fv8\/f8AAQIBBAUGBgUEAwEA\/\/79\/fj9+fv8\/f4AAQIDAwUGBQYEAwIA\/\/7+\/\/j7+vv8\/f7\/AQIDAwQFBgYEAwIBAf79\/fz7+vr8\/f7\/AAEBBQQFBgYEAwIBAP\/++\/36+\/r8\/f7\/AAECAwIGBQYEAwIBAP\/+\/fz7\/Pn9+v\/\/AAECAwQFBQYCBAIBAP\/+\/fz8+\/r8\/voB\/wECAwQEBQYEAwMBAf\/+\/fz8+\/r8\/f7\/AQACAgUEBQYFAwIBAP\/+\/vz8+\/r7\/f7\/AAECAwQDBQYFAwIBAP\/+\/v38+\/v7+\/7\/AAEBAgMEBQUGBAIAAf\/\/\/v38+\/v8+\/7+AAABAgMEBAUEBQIAAv\/\/\/v38\/Pv7\/P7+AAABAgMEBAUFBAMCAAD\/\/v38\/Pv7\/P3+AAABAgMDBAUFBAMBAQD\/\/v39\/Pv7\/P3+AAABAgMDBAUFBAMAAgD\/\/v79\/Pv7\/P3+\/wABAgIDBAUFBAMBAwD\/\/v79\/Pz7\/P3\/\/\/8BAgIDBAQFBAMCAQH+\/\/79\/Pz7\/P3+\/wD\/AgICBAQFBAMCAQD\/\/\/79\/Pz7\/P3+\/wAAAQIDBAQFBQMCAQAA\/\/79\/fz7\/Pv\/\/\/8AAQIDAwQFBAMCAQAB\/\/79\/fz7\/P3+\/wAAAQIDAgQFBAMCAQEA\/\/7+\/P38+\/3+\/\/8AAQIDAgQEBAMCAgEA\/\/79\/vv8\/P3+\/v8AAQICAwQEBQUCAgEA\/\/7+\/fz8\/P39\/v8AAQICAwMEBQMCAgAA\/\/\/+\/f38\/P3+\/v8AAAEDAwMEBAQDAgEA\/\/\/+\/f78+\/39\/v8AAQECAwMEBQQCAgEA\/\/\/+\/f38\/P39\/v8AAQECAgMEBAMCAgEAAP\/+\/v38\/Pz9\/v8A\/wECAgMDBAQCAgEAAP\/\/\/v38\/Pz+\/v8AAAECAgMDBAIDAgEAAP\/+\/v39\/Pz9\/\/8AAAEBAgMDBAMDAgEAAP\/\/\/v39\/f3+\/v8AAAEBAgIDAwMCAgEAAP8A\/v79\/f3+\/v8AAAEBAQMDAgQCAgEBAP\/\/\/v79\/f3+\/v8AAAEBAgIDAwMCAgEBAAD\/\/v7+\/f3+\/v8AAAEBAgICAwMCAgEBAAD\/\/\/7+\/f3+\/v\/\/AAABAQICAwMCAgEBAAD\/\/\/7+\/f3+\/v\/\/AAABAQICAwMCAgEBAAD\/\/\/7+\/v3+\/v\/\/AAABAQICAgMCAgEBAAD\/\/\/7+\/v3+\/v\/\/AAABAQICAgMCAgEBAAD\/\/\/\/+\/v3+\/v\/\/AAEBAQECAgMCAgEBAAD\/\/\/\/+\/v7+\/v\/\/AAABAQECAgICAgEBAAD\/\/\/\/+\/v7+\/v\/\/AAABAQECAgMCAQEBAAAA\/\/\/+\/v3+\/\/\/\/AAAAAQECAgICAgEBAAAA\/\/\/\/\/v7+\/\/\/\/AAAAAQEBAgICAgABAQAA\/\/\/\/\/v7+\/v\/\/AAAAAQEBAgICAgEBAQAA\/\/\/\/\/v7+\/v\/\/AAAAAQEBAgICAgEBAAAA\/\/\/\/\/\/7+\/v\/\/AAAAAAEBAQICAgEBAAAAAP\/\/\/v7+\/v\/\/AAAAAQEBAQICAgEBAAAA\/\/\/\/\/\/7+\/\/\/\/AAAAAAEBAQICAgAAAAAAAQEBAQECAwYHBQgIBwYFAwIBAP7+\/Pr5+Pf29fT08\/P19\/v7\/P3\/AAIDBAYHCAkKCwwKDQ4PDw8NCwkHBgQCAfz+\/fz6+fj39vX08\/Py8fH09ff5+vz9\/wABAwAEBgcICQoLDAwNDg4MCggHBQsCAP\/+\/Pv6+fj\/9vr19PPy8fHz9ff4+vv9\/v4BAgMEBgcICAkKDwwEDQ0LCggGBQMCAf\/+Avz5+fj49\/b19PPz8vL09ff9+vn9\/gABAgMEBQYLCQkKCwsMDQ0LCgkHBwQEAQD\/\/fz7+vn49\/b2+fX18\/L09vf5+vz9\/gIBAgMEBQYHCAkKCg0MDA0LCggHBQQDAQD\/\/v38\/Pj5+Pf29vX09PP19vj5+vv9\/wABAgMEBQYHCQkLCgsMDA0MCgkHBwQBAgD\/\/v38+\/r5+fj39vb19PT19vj5+\/z6\/wEBAgMEBQYHCAcKBwoLDAwLCggHBQQDAgL8+P38+\/r6+fj39Pb19fT19\/j5+\/z9\/wD+AQMEBQYHBwgJCQwLCwsLCggHBgQDAgH\/AQP9\/Pv6+fn49\/f29fX2\/vj5+\/z9\/wABAgMEBQUFBwkICQoKCwsLCQgIBAIDAgEA\/\/79\/Pv69\/n49\/f29vX19\/j6+\/f9\/wABAgMEBAUGBwcJCQkKCgsLCQgHBQQDAgH\/\/\/79\/Pv6+vn4+Pf39vr19\/j6+\/z9\/v8AAQIGBAUFBgcICAkJCgoKCAcGBQQDAgEA\/\/7+\/Pr7+vn5+Pf39vb29\/n6\/vz4\/\/8AAQIDBAUFBgcHCQkJCgoKCQcGBQQDAgH\/\/v\/9\/Pz7+vr5+Pj39\/b2+Pn6Avz9\/v8AAQIDBAQFBgYHBwgKCgoKCAcGBQQDBAEA\/\/79\/Pz7+vr5+fj39vb5+Pn6+\/z9\/v8AAQICAwQFBQYHBwgICQkJCAcHBQYCAgEA\/\/79\/fz7+\/n4+Pj49\/f3+Pn6+\/z9\/v8A\/wICAwQFBQYGBwgICAsJBgcGBQQDAgEA\/\/79\/fz7+\/r6+fj4+Pf3+Pn6+\/z5\/v8AAQICAwQEBQYGBwIHCAkJCAcGBAQDAgIA\/\/7+\/fz8+\/r6+fn4\/vj1+Pn7\/P39\/v8AAQICAwIEBQUGBwcHCAgJBwYFBQMDAgEA\/\/7+\/fz8\/fv6+fn5+Pj3+fr7+vz\/\/v8AAQECAwQECwb\/BgcHCAgIBwYFBAMCAgEA\/\/7+\/fz8+\/v\/+vn5+Pj4+fr7\/fz+\/v8AAQECAwMEBAUGBwcHBwgIBwYFBAMCAgEA\/v7+\/f38+\/v6+vj5+Pj4+fr7\/P3+\/v8AAQECAwMEBAUFBgYHBwgIBwYFBAMCAgEA\/\/\/9\/f38\/Pv7+vr5+fn5+fz7\/P3+\/v8AAQECAwMCBAUFBgYHBwcIBwYFBAgDAQEA\/\/\/+\/f38\/Pv7+v76+fn4+fr7\/P3+\/v8AAf8CAwIDBAUFBQYGBwcFBwUFBAMCAQEA\/\/\/+\/\/37\/fz7+\/r6+fn5+vv7\/P3+\/\/8AAQECAgMEBAUEBQYGBwcHBgUFBAMCAQEB\/\/7+\/v39\/Pz7+\/r6+vn5+vr8\/P3+\/\/8AAQECAgMCBAMFBQYGBgcHBgUEBAMCAgEA\/\/\/+\/v39\/Pz7+\/v6+vr5+vv8\/f3+\/\/8AAQH\/AgMDBAQFBQYGBgcHBgQEBAMCAQEA\/\/\/+\/v37\/P77+\/v6+vr5+vv8\/f3+\/gAAAQECAgMDBAQFBQUGBgYHBgUEAwMCAgAAAP\/+\/v39\/Pz8+\/v6+vr6+vv8\/f3+\/\/8AAQECAgMDBAQEBQUGBgYHBQUEAwICAQEA\/\/\/+\/v39\/Pz8+\/v7+vr5+\/v8\/f7+\/\/8AAQECAgMCBAQEBQUFBgYGBQQEAwICAQAA\/\/\/+\/v39\/fz8\/Pv7+vr6+\/z8\/f7+\/\/8AAQABAgMDAwQEBQUFBgYGBQQEAwICAQEA\/\/3+APz9\/fz8\/Pv7+\/r6+\/z8\/f7+\/wAAAQECAgIDAwQEBAUFBQYGBQUDAwICAQAA\/\/\/+\/v79+\/38\/Pv7+\/r6+\/z9\/f7+\/wAAAQECAgIDAwQEBAUFBQUGBQQDAwICAQD\/AP\/\/\/v79\/fz8\/Pv7+vv7+\/z9\/f7\/\/wAAAQECAgIDAwQEBAUFBQUFBQMDAwIBAQAA\/\/\/+\/vz9\/fz8\/Pz7+\/v7\/Pz9\/f7\/\/wAAAQECAgIDAwQDBAQFBQUFBAQDAgIAAQAA\/\/\/+\/v79+\/38\/fz7+\/v7\/Pz9\/gD+\/wAAAQECAgIDAwMCBAQFBQUFBAQDAgIB\/wEA\/\/\/\/\/v79\/f38\/P37\/Pv7\/P39\/v7\/\/wAAAf8CAgIDAwMEBAQEBQUEBAMDAgIBAQAA\/\/\/\/\/\/7+\/f39\/Pz8\/Pv8\/P39\/v7\/\/wAAAQEBAgICAgMDBAQEBAQEBAQCAgEBAQAA\/\/\/\/\/\/7+\/f39\/fz8\/Pz8\/f3+\/v\/\/\/wAAAQEBAAICAwMDAwQEBAQEAwMCAgMBAQAA\/\/\/\/\/v7+\/v39\/f38\/Pz8\/f3+\/v\/\/AAAAAQEBAgICAgMDAwMEBAQEAwMCAgEBAAAA\/\/\/\/\/v7+\/v39\/f39\/Pz9\/f7+\/v\/\/AAAAAQEBAQICAgMDAwMDBAQDAwECAQEBAAAA\/\/\/\/\/\/7+\/v39\/f39\/f39\/f7+\/\/8AAAAAAQEBAQICAgIDAwMDAwMDAgICAQEBAAAA\/\/\/\/\/\/7+\/v7+\/f39\/f39\/f7+\/\/\/\/AAAAAQEBAQIBAgIDAwMDAwMDAgICAQEBAAAA\/\/\/\/\/\/7+\/v7+\/v39\/f39\/v7\/\/\/8AAAAAAQEBAQICAgICAgMDAwMCAgIBAQEAAAAAAP\/\/\/\/7+\/v7+\/v3+\/f3+\/v7\/\/\/8AAAAAAQEAAQECAgICAgIDAwMDAgIBAQEAAAAA\/\/\/\/\/\/\/+\/v7+\/v7+\/f3+\/v\/\/\/\/8AAAAAAQEBAQEBAgICAgICAwICAgEBAQEBAAAA\/\/\/\/\/\/\/+\/v7+\/v79\/v7+\/v\/\/\/\/8AAAABAQEBAQEBAgICAgICAgICAgEBAQAAAAD\/\/wAAAAAAAAAAAAECBAUHCQkKCwwMDA0MCwoJDAcGBQQDAgEA+f\/+\/vz8+\/v6+fn4+Pf39vX29vX19fT09PT08+vz8\/Tz8\/Pz9fb3+Pn6+\/z4\/v8AAQECAwMEBQUGBgcHCAgICQkJCgoKBwsLCwwMDAwMDAwMDQwMCgkIBwYFBAMDAQEA\/\/7+\/fz8+\/v6+vn5+Pj39\/f28Pb29vb19fX09PT09PT09PX29vf4+fr7\/P3+\/\/8BAgECAwMEBAUGBgYHCAgICAkJCQoKCgoLCwsMCwsLDAwMDAwLCQgHBgUEBAMCAQAA\/\/7+\/f38\/Pv7+vr5+fn++Pf39\/f29vb2\/\/b19fX19fX19fX29\/j5+vv9\/f7\/\/wABAQIDAwQEBgYGBgYHBwgICAkKCQkKCgoKCgoLCwsLCwsLCwwKCQgHBgUEAwICAQADAP7+\/f38\/Pv7+vr5+fX5+Pj49\/f39\/f29vb29vb29vX19fb3+Pn6+\/z9\/f7\/AAABAQIDAwQEBQUFBgYHBwcICAgICQkJCwoKCgoKCgoKCgoKCwkICAcGBQQDAwIBAQD\/\/wb+\/f38\/Pv7+vr6+fn59vj4+Pj39\/f39\/f39vb2+fb29vf4+fr7\/Pz9\/v\/9AAEBAgIDAwQEBQQFBgYGBwcHBwgICAgIDQkJCQkJCQkKCgoKCQgHCQYFBAMDAgEBAP8B\/\/79\/fz8\/Pv7+\/r6+vn5+fn4+Pj4+Pf39\/j39\/f39\/f39\/j5+fv8\/P3+\/v8AAAEBAgIDAwMEBAUFBQYGBgYHBwcHBwgICAgICAgJCQkFCQkJCAcGBgUEAwMCAgEAAP\/\/\/v79\/f79\/Pz7+\/v6+vr6+fn5+fn5+Pj4+Pj4+Pj4+Pj4+fr5+\/z8\/f7+\/\/8AAAEBAgICAwMEBAQFBQYFBgYGBgYHBwcHBwcHBwcICAgICAgHBwYFBAQDAwICAQEAAP\/\/\/v79\/f39\/Pz8+\/v7+\/v6+vr6+vr5+fn5+fn5+fn4+fn5+vv7\/P39\/v\/+\/wAAAQEBAgICAwMDBAQEBAUFBQUFBgYGBgYGBgYGBwcHBwYIBwcGBQUEBAMDAgIBAQAA\/\/\/\/\/v7+\/f35\/fz8\/Pz8+\/v7+\/v3+\/r6+vr6+vr6+vr5+vr7+\/z8\/f3+\/v\/\/AAAAAQEBAgICAwMDAwQEBAQEBQUFBQUFBQUGBgYFBgYGBgYGBgYFBAQEAwICAQEBAAAA\/\/\/\/\/v7+\/f39\/f38\/Pz8\/P37+\/v7+\/v7+\/v6+vr6+vr6+fv7\/Pz9\/f7+\/\/\/\/AAAAAQEBAgICAgMDAwMDBAQEBgQEBAUFBQUFBQUFBQMGBQUFBQUEBAMDAgIAAgEAAAD\/\/\/\/\/\/v7+\/f79\/f39\/fz8\/Pz8\/Pz8+\/v7+\/v7+\/v7+\/v7+\/z8\/f3+\/v7\/\/\/8AAAABAQEBAgICAgIDAwMDAwMEBAQEBAQEBAQEBAQFBQUFBQUFBAQDAwICAgEBAQAAAAD\/\/\/\/\/\/f7+\/v7+\/v7\/\/\/8AAAECAwMEBAQFBQUFBQUFBQYGBgYGBgYGBgUFBQUFBQUFBQUFBAQDAgIBAQD\/\/\/\/+\/v39\/Pv8+\/v7+vr6+vn5+fn59\/j4+Pj4+Pj4+Pf39\/f39\/f39\/f39\/f39\/f39\/f39\/f3+fj4+Pj4+Pj4+\/n4+fn5+fn5+fn5+vr6+fr6+vr6+\/v7\/P39\/v8AAAABAgICAwMEBAUFBQYGBgYHBwcHCAgOCAgICQkJCQkJCQkJCQoJCgoKCgoKCgoKCgoKCgwKCQkJCQkJCQkJCQkJCQUJCAgICAgICAgHBwcHBwcHBwYGBgYFBAQDAwIBAQAA\/\/\/\/\/v79\/f38\/Pz7+\/v7+vr69fn5+fn5+fn4+Pj4+Pj4+Pj4+Pj4+Pj4+P\/4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+fn5+fn6+fn5+fn6+vr6+vr6+vr7+\/z8\/f3+\/v\/\/AAABAQICAgMDAwQEBAUFBQUGBgYGBgYHBwcHBwcHBwgHCAgICAgICAgICAcICAgICAgICAgICAgICAgFBwcHBwcHBwcHBwcGBgYGCAYGBgYGBgUFBQUFBAQDAgIBAQEAAP\/\/\/v7+\/f39\/fz8\/Pv7+\/v7+vr6+vr6+vn5+fn5+fn5+fn5+fn4+Pj4+Pj5+Pj4+Pn5+fn5+fn5+fn5+fn5+fn5+fr6+vr4+vr6+vr6+\/v7+\/r7+\/v8\/f3+\/v\/\/\/wAAAQEBAgICAwMDAwQEBAQFBQUFBQUGBgYGBgwGBgYHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcGBgYGBgYGBgYGBgYGBgUFBQUFBQUFBQUFBAQEBAQDAwICAQEAAAD\/\/\/\/+\/v79\/f39\/fz8\/Pz89\/37+\/v7+\/v6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+\/r6+vr7+\/v7+\/v7+vv7+\/v7+\/z8\/Pz8\/Pz8\/f3+\/v7\/\/wAAAAEBAQECAgICAwMDAwMEBAQEBQQFBQUFBQUFBQUFBQUFBQYGBwUGBgYGBgYGBgYGBgUGBQUFBQUFBQUFBQUFBQUFBQUFBQUEBAQEBAQEBAAFBAQEBAMDAwICAQEBAAAAAP\/\/\/\/\/+\/v4A\/f39\/f39\/fz8\/Pz8\/Pz8\/Pv7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/z8\/Pz7\/Pz8\/Pz8\/Pz8\/Pz8\/f39\/f39\/f7+\/v\/\/\/\/8AAAAAAQEBAQICAgICAgMDAwMDBAMDBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBgQEBAQEBAQEBAQEBAQEBAQEBAQEBAMDAwMDBAMDAwMDAwMDAgICAQEBAAAAAAD\/\/\/\/\/\/\/7+\/f\/+\/v79\/f39\/f39\/f39\/fz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/f38\/f39\/f39\/f39\/f39\/f39\/gUDAwMDAwMDAwMDBAUFBgYHBwcICAgICAgICAgICAgICAgIBwcHBwcHBwcHBwcHBgULBAMDAgIBAQAA\/\/\/+\/v39\/fz8\/Pv7+\/v6+vr6+vT5+fn5+fj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj48\/j4+Pj4+Pj4+Pj5+fn5+fn5+fn5+fr6+vr6+vr6+vv7+\/v7\/P39\/v\/\/AAABAQICAwMDBAUFBQUFBgYGBgcHBwcHCAgICAgICAkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCAgICA0ICAgICAgICAcHBwcHBwcHBwYGBgYGBwUFBAMDAgIBAQAA\/\/\/+\/v79\/f38\/Pz8\/Pv7+\/r6+vr6+fn5+fn5+fn5+Pj4+Pjv+Pj4+Pj4+Pj4+Pj4+Pj49\/j4+Pj4+fn5+fn5+fn5+fX5+vr6+vr6+vr6+vr7+\/v7\/Pz9\/f7+\/\/8AAAEBAgICAwMDBAQEBAUFBAUFBgYGBgYGBwcHBwcHBwcHBwcICAgICAgICAgICAgICAgICAcHBwcHBwcHBwcHBwcHBwcGBgYGBgoGBgYGBgUFBQUFBQQEAwMCBQEBAAAA\/\/\/+\/v79\/f39\/Pz8\/Pv7+\/v7+vr6+vr6+vn5+fn5+fn5+fn5+fr5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fr6+vr6+vr6+vr6+vv7+\/v7+\/v7\/Pz9\/f7+\/\/8AAAABAQICAgIDAwMDBAQEBAUFBQUFBQYKBgYGBgYGBgYGBgcHBwcHBwcHBwcHBwcHBwcHBwcHBgYGBgYGBgYGBgYGBgYGBgUFBQUFBQUFBQUFBQQEBAQDAwMCAgEBAAAA\/\/\/\/\/v7+\/v39\/f38\/Pz8\/Pv7+\/v7+\/v7+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr3+vr6+vr6+vr6+vr6+vr7+\/v7+\/v7+\/v7+\/v7+\/z8\/Pz8\/Pz8\/f39\/v7\/\/\/8AAAABAQECAgICAgMDAwMDBAQEBAQEBAQFBQUFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYFBQUFBQUFBQUFBQUFBQUFBQUFBQUEBAQEBAQEBAQEBAQEBAMDAgICAQEBAAAAAP\/\/\/\/\/+\/v7+\/f39\/f39\/fz8\/Pz8\/Pz8\/Pv7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/z6+\/v7+\/v7+\/v7+\/v7+\/v8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/P39\/f39\/f7+\/v0A\/wAAAAABAQEBAQICAgICAwMDAwMDAwMEBAQEBAQEBAQEBAQEBAQEBAQFBQUFBQUFBQQFBAMFBAUEBAQEBAQEBAQEBAQEBAQEBAQEBAQDAwMDAwMDAwMDAwMCAgIBAQEBAAAAAP\/\/\/\/\/\/\/v7+\/v7+\/v39\/f39\/f39\/f38\/Pz8\/Pz8\/fz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz9\/f39\/f39\/f39\/f39\/f39\/f39\/v8GBAUEBQUFBQUFBgcICQoLDA0NDg4ODw4NDAoJCAcGBQQDAgEAAP\/+\/f38+\/v6+vn5+Pj39\/f29vb19fX09PT09PTz8\/Pz9PX29\/j5+vv8\/f7\/AAEBAgMDBAUFBgYHBwgICQkJCgoKCgsLCwsMDAwMDAwMDA0MCwoJCAYFBAQDAgEAAP\/+\/f38+\/v6+vn5+Pj49\/f29vb29fX19QD09PT09PPz8\/P19vf4+fr7\/P3+\/\/8AAQICAwQEBQUGBgcHCAgICQkJCgoKCgsLCwsLDAwMDAwMDAsKCQgHBgUEAwIBAQD\/\/v79\/fz7+\/r6+fn5+Pj49\/f39vb29vX19fX19fX09PT09fb4+fr7+\/z9\/v8AAAECAgMEBAUFBgYHBwcICAgJCQkKCgoKCgv+CwsLCwsMDAwLCgkIBwcFBAMCAQEA\/\/\/+\/v0B\/Pv7+vr6+fn4+Pj39\/f39vb29vb29fX19fX19fb3+Pn6+\/z8\/f7\/\/wABAQIDAwQEBQUGBgYHBwgICAkJCQkJCgoKCgoDCwsLCwsLCgkIBwYGBQgDAgEBAP\/\/\/v79\/fz8+vr6+vr5+fn4+Pj39\/f39\/b29vb29vb29vX29\/j5+vv8\/P3+\/\/8AAQECAgMEBAQFBQYGBwcHBwgICAkJCQkJCgoKCgoKCgoKCgoJCAcGBQQEAwIBAQAA\/\/7+\/f38\/Pv7+\/r6+fn5+fj4+Pj39\/f39\/f29vb29vb29vf4+fr7\/P39\/v\/\/AAEBAgIDAwQEBQUFBgYGBwcHBwgICAgJCQkJCQkJCQkKCgoJCAcGBgUEAwMCAQEAAP\/+\/v39\/Pz8+\/v7+vr6+fn5+fj4+Pj4+Pf39\/f39\/f39\/f4+fr7+\/z9\/f7\/\/wD9AQECAgMDBAQFBQUFBgYGBwcHBwcICAgICAgICQkJCQkJCAgHBgUEBAMCAgEBAAD\/\/\/7+\/f38\/Pz7+\/v6+vr6+fn5+fn5+Pj4+Pj4+Pj4+Pj4+fr6+\/z8\/f7+\/\/8AAAEBAgICAwMEBAQFBQUGBgYGBgcHBwgHBwgICAgICAgICAgHBgUFBAMDAgIBAQAA\/\/\/+\/v39\/fz8\/Pz7+\/v7+vr6+vr5+fn5+fn5+fj4+Pj4+fn6+\/v8\/f3+\/v\/\/AAABAQICAgMDAwQEBAUFBQUFBgYGBgYGBwcHBwcHBwcHBwcHBgYFBAQDAwIBAQEAAP\/\/\/v7+\/f39\/Pz8\/Pv7+\/v7+vr6+vr6+vn5+fn5+fn5+fn6+\/v8\/P3+\/v\/\/AAAAAQECAgIDAwMDBAQEBAUFBQUFBgYGBgYGBgYGBgcHBwcHBgYFBAQDAwICAQEAAAD\/\/\/7+\/v79\/f38\/Pz8\/Pv7+\/v7+\/r6+vr6+vr6+vr6+vr6+\/v8\/P39\/v7\/\/wAAAAEBAQICAgMDAwMEBAQEBAUFBQUFBQUGBgYGBgYGBgYGBgYFBAQDAwICAgEBAAAA\/\/\/\/\/v7+\/f39\/fz8\/Pz8\/Pv7+\/sAAAABAQICAwMEBAYGBwYGBQQDAgD\/\/vz7+vn49\/b19PPy8fL09vj5+\/3+AAEDBAUGBwkKCwwMDQ4PDw0LCQcGBAMBAP79\/Pr5+Pf29fTz8vHx8vT29\/n7\/P7\/AQIDBQYHCAkKCwwNDhcODAoJBwUEAgH\/\/v37+vn49\/b19PPz8vHy9Pb4+vv9\/gABAgQFBgcICQoLDA0NDg4MCggHBQQCAf\/+\/fv6+fj39vX09PPy8fP19\/j6\/P3\/AAEDBAUGBwgICwsMDQ0ODQwKCAYFAwIB\/\/79\/Pr5+Pf29vX08\/Ly9Pb3+fv8\/v8AAgMEBQYHCAkKCwwNDQ4NCwkIBgUDAgD\/\/v37+vn49\/f29fTz8\/P19vj6+\/3+\/wECAwQFBgcICQoLDAwNDgwLCQcGBAMBAP\/+\/fv6+fj39\/b19PPz8\/X3+Pr7\/f4AAQIDBAUGBwgJCgsMDA0NDAoIBwUEAgEA\/\/38+\/r5+Pj39vX09PP09vf5+vz9\/wABAgMEBQcHCAkKCwsMDQ0LCQgGBQMCAQD+\/fz7+vn4+Pf29fT18\/X2+Pn7\/P3\/AAECAwQFBgcICQoLCwwNDAwJBwYEAwIA\/\/79\/Pv6+fj39\/b19PTz9ff49vv9\/v8AAgMEBQYHBwgJCgsLDAwLCggHBQQDAQD\/\/v38+\/r5+Pf39vX19PT29\/n6\/P3+\/wEDAwQFBgcHCAkKCgsMDAsJCAYFBAIBAP\/+\/fz7+vn49\/f29fX09fb4+fv8\/f4AAQIDBAUGBwcICQoKCwsMCggHBgQDAgEA\/\/79\/Pv6+fj49\/b29fT29\/n6+\/z+\/wABAgMEBQYGBwgJCQoLCwsJCAYFBAMCAP\/+\/f38+\/r5+Pj39vb19fb4+fr8\/f7\/AAECAwQFBgYHCAgJCgoLCggHBgUDAgEA\/\/79\/Pz7+vn4+Pf39vb29\/n6+\/z9\/wABAgIDBAUGBgcICAkKCgsJCAcFBAMCAQD\/\/v38\/Pv6+fn49\/f29vf4+fr8\/f4AAAECAwMEBQYGBwgICQkKCggHBgUEAwIBAP\/+\/fz7+\/r5+fj49\/f29\/n6+\/z9\/v8AAQIDAwQFBgYHBwgJCQkJCAYFBAMCAQD\/\/\/79\/Pv7+vn5+Pj39\/f4+fr8\/f7\/\/wABAgMEBAUGBgcHCAgJCQgHBgUEAwIBAP\/+\/v38+\/v6+vn4+Pj3+Pn6+\/z9\/v8AAQECAwQEBQUGBwcICAkJBwYFBAMCAQEA\/\/79\/fz7+\/r6+fn4+Pf4+fr8\/P3+\/wABAgIDBAQFBgYHBwcICAgHBgUEAwIBAP\/\/\/v39\/Pv7+vr5+fj4+Pn6+\/z9\/v8AAAECAgMEBAUFBgYHBwgIBwYFBAMCAQEA\/\/7+\/fz8+\/37+vn5+fj5+vv8\/f3+\/wABAQIDAwQEBQUGBgcHCAgHBgUEAwMBAAD\/\/v79\/Pz7+\/r6+fn5+Pn6+\/z9\/v\/\/AAEBAgMDBAQFBQYGBwcHBwYFBAMCAgEA\/\/\/+\/f38\/Pv7+vr6+fn5+vv8\/f3+\/wAAAQICAwMEBAUFBgYHBwcGBQQEAwIBAAD\/\/\/79\/fz8+\/v6+vr5+fr7+\/z9\/v\/\/AAEBAgIDAwQEBQUGBgYHBwYFBAMCAgEAAP\/+\/v39\/Pz7+\/v6+vn5+vv8\/f7+\/wAAAQECAwMEBAQFBQYGBgcGBQQDAwIBAQD\/\/\/7+\/f38\/Pv7+\/r6+vr7\/Pz9\/v\/\/AAABAgIDAwQEBAUFBgYGBgUEBAMCAgUAAP\/\/\/v79\/fz8+\/v7+vr6+\/v8\/f7+\/wAAAQECAgMDBAQEBQUFBgYFBQQDAwIBAQD\/\/\/7+\/f39\/Pz7+\/v6+vr7\/P39\/v\/\/AAABAQICAwMEBAQFBQUGBgUEAwMCAgEAAP\/\/\/v79\/f38\/Pv7+\/v6+\/z8\/f7+\/wAAAQECAgIDAwQEBAUFBQYFBAQDAgIBAQAA\/\/\/+\/v39\/fz8+\/v7+\/v8\/P3+\/v\/\/AAABAQICAwMDBAQEBQUFBQUEAwMCAQEAAP\/\/\/v7+\/f38\/Pz8+\/v7+\/z9\/f7+\/wAAAQEBAgIDAwMEBAQFBQUFBAMDAgIBAQAA\/\/\/+\/v39\/fz8\/Pz7+\/v8\/f3+\/v\/\/AAABAQICAgMDAwQEBAUFBQQEAwICAQEAAP\/\/\/\/7+\/f39\/Pz8\/Pv7\/Pz9\/v7\/\/wAAAQEBAgICAwMDBAQEBQUEBAMDAgEBAQAA\/\/\/+\/v79\/f38\/Pz8+\/z8\/f3+\/v\/\/AAABAQECAgMDAwMEBAQEBQQDAwICAQEAAP\/\/\/\/7+\/v39\/fz8\/Pz8\/P39\/v7\/\/wAAAAEBAgICAwMDAwQEBAQEAwMCAgEBAAAA\/\/\/+\/v7+\/f39\/Pz8\/Pz9\/f7+\/\/\/\/AAABAQECAgIDAwMDBAQEBAMDAgIBAQEAAP\/\/\/\/7+\/v39\/f38\/Pz8\/f3+\/v7\/\/wAAAAEBAgICAgMDAwMEBAQEAwMCAgEBAAAA\/\/\/\/\/v7+\/f39\/f38\/Pz9\/f7+\/\/8AAAABAQECAgICAwMDAwQEBAMDAgIBAQAAAP\/\/\/\/7+\/v79\/f39\/fz8\/f3+\/v\/\/\/wAAAQEBAQICAgMDAwMDBAQDAwICAQEBAAAA\/\/\/\/\/v7+\/v39\/f39\/P39\/v7\/\/\/8AAAABAQECAgICAwMDAwMDAwMCAgEBAQAAAP\/\/\/\/\/+\/v7+\/f39\/f39\/f7+\/v\/\/AAAAAQEBAQICAgIDAwMDAwMDAgICAQEAAAAA\/\/\/\/\/v7+\/v79\/f39\/f3+\/v7\/\/\/8AAAABAQEBAgICAgMDAwMDAwICAgEBAQAAAP\/\/\/\/\/+\/v7+\/v39\/f39\/v7+\/\/\/\/AAAAAQEBAQICAgICAwMDAwMCAgIBAQEAAAD\/\/\/\/\/\/v7+\/v7+\/f39\/f7+\/v\/\/\/wAAAAABAQEBAgICAgIDAwMDAgICAQEBAAAAAP\/\/\/\/\/+\/v7+\/v79\/f3+\/v7\/\/\/8AAAAAAQEBAQICAgICAgMDAwICAgEBAQAAAAD\/\/\/\/\/\/\/3+\/v7+\/v39\/gIBAgICAwICAQEAAP\/9+\/n29\/n8\/wEEBwkMDhASDgsIBAH\/\/Pn39PLz9vn8\/wIFBwoMDhANCgcDAP77+Pbz8fH1+Pv+AQQGCQsNDw0JBgMA\/fv49vPx8PT3+v0AAwUICgwODQkGAwD++\/j29PHw8\/f6\/f8CBQcKDA4NCgYEAf77+fb08vDz9vn8\/wIEBwkLDg0KBwQB\/vz59\/Xy8PP2+fz\/AgQHCQsNDgoHBAL\/\/Pr39fPx8\/b5\/P8BBAYJCw0OCwgFAv\/9+vj29PHz9vn8\/wEEBggLDQ8LCAUDAP37+Pb08vP2+fz+AQQGCAoNDwwJBgMA\/vv59\/Xy8\/b5\/P4BAwYICgwODAkGAwH+\/Pn39fPy9vn7\/gEDBggKDA4MCQYEAf78+vj18\/L2+Pv+AAMFCAoMDgwJBwQB\/\/z6+Pb08vX4+\/4AAwUHCQsNDQoHBAL\/\/fr49vTy9fj7\/QACBQcJCw0NCgcEAv\/9+\/n29PP1+Pv9AAIEBwkLDQ0KBwUCAP37+ff18\/X4+v0AAgQGCQoMDQoHBQIA\/fv59\/Xz9fj6\/f8CBAYICgwNCggFAgD++\/n39fP19\/r9\/wEEBggKDA0KCAUDAP78+vj29PX3+vz\/AQMGCAoLDQoIBQMA\/vz6+Pb09Pf6\/P8BAwUHCQsNCggFAwD+\/Pr49vT09\/r8\/wEDBQcJCw0LCAUDAf78+vj29fT3+vz+AQMFBwkLDAsIBQMB\/vz6+Pb19Pf5\/P4AAwUHCQoMCwgGAwH\/\/fv59\/X09\/n8\/gACBAYICgwLCAYDAf\/9+\/n39fT3+fz+AAIEBggKDAsIBgMB\/\/37+ff19Pf5\/P4AAgQGCAoLCggGAwH\/\/fv59\/b09\/n7\/gACBAYICgsLCAYDAf\/9+\/n39vT3+fv+AAIEBggJCwsIBgMB\/\/37+fj29Pf5+\/4AAgQGBwkLCwgGBAH\/\/fv6+Pb19\/n7\/gACBAUHCQoLCAYEAf\/9\/Pr49vX3+fv9AAIDBQcJCgsIBgQB\/\/78+vj29ff5+\/3\/AQMFBwkKCwgGBAIA\/vz6+Pf19\/n7\/f8BAwUHCAoLCAYEAgD+\/Pr49\/X3+fv9\/wEDBQcICgsIBgQCAP78+vn39ff5+\/3\/AQMFBwgKCwgGBAIA\/vz6+ff29\/n7\/f8BAwUGCAoLCAYEAgD+\/Pr59\/b3+fv9\/wEDBQYICQsIBgQCAP78+vn39vf5+\/3\/AQMFBggJCggGBAIA\/vz7+ff29\/n7\/f8BAwUGCAkKCAYEAgD+\/Pv5+Pb3+fv9\/wEDBAYICQoIBgQCAP78+\/n49vf5+\/3\/AQMEBgcJCggGBAIA\/vz7+fj29\/n7\/f8BAwQGBwkKBwUEAgD+\/Pv5+Pf4+vz9\/wEDBAYHCQoHBQMCAP78+\/n49\/j6\/P3\/AQMEBgcJCgcFAwIA\/v37+fj3+Pr8\/v8BAwQGBwkKBwUDAgD+\/fv6+Pf4+vz+\/wEDBAYHCAkHBQMBAP79+\/r49\/j6\/P7\/AQMEBgcICQcFAwEA\/v37+vj3+Pr8\/v8BAwQGBwgJBwUDAQD+\/fv6+Pf4+vz+AAEDBAYHCAkHBQMBAP79+\/r49\/n6\/P4AAQMEBgcICQcFAwEA\/v37+vj3+fr8\/gABAwQGBwgJBgUDAQD+\/fv6+ff5+\/z+AAEDBAYHCAgGBAMBAP79+\/r59\/n7\/P4AAQMEBQcICAYEAwH\/\/v37+vn3+fv9\/gABAwQFBwgIBgQDAf\/+\/Pv6+ff5+\/3+AAEDBAUHCAgGBAIB\/\/78+\/r5+Pn7\/f4AAQMEBQcICAYEAgH\/\/vz7+vn4+vv9\/wABAwQFBwgHBQQCAf\/+\/Pv6+fj6+\/3\/AAIDBAUHCAcFBAIB\/\/78+\/r5+Pr8\/f8AAgMEBQcIBwUEAgD\/\/vz7+vn4+vz9\/wACAwQGBwgHBQMCAP\/+\/Pv6+fj6\/P3\/AAIDBAYHCAYFAwIA\/\/78+\/r5+Pr8\/v8AAgMEBgcIBgUDAgD\/\/fz7+vn5+\/z+\/wECAwQGBwgGBAMBAP\/9\/Pv6+fn7\/P7\/AQIDBAYHCAYEAwEA\/\/38+\/r5+fv+\/QABAgMEBgcIBgQDAQD\/\/fz7+vn6+\/3+AAECAwUGBwcFBAIBAP79\/Pv6+fr7\/f4AAQIDBQYHBwUEAgEA\/v38+\/r5+vz9\/gABAgQFBgcHBQQCAf\/+\/fz7+vn6\/P3\/AAECBAUFBwcFAwIB\/\/79\/Pv6+fv8\/f8AAQMEBQYHBgUDAgH\/\/v38+\/r5+\/z+\/wABAwQFBgcGBAMCAP\/+\/fz7+vn7\/P7\/AAIDBAUGBwYEAwIA\/\/79\/Pv6+fv9\/v8AAgMEBQYHBQQDAQD+\/v38+\/r6+\/3+\/wECAwQFBgcFBAIBAP\/+\/fz7+vr8\/f4AAQIDBAUGBwUEAgEA\/\/78+\/r6+vz9\/gABAgMEBQYGBQMCAQD\/\/fz7+vr7\/P3\/AAECAwQFBgYEAwIBAP79\/Pv6+vv8\/v8AAQIDBAUGBgQDAgH\/\/v38+\/r6+\/z+\/wABAgMEBQYFBAMCAP\/+\/fz7+vr8\/f7\/AAECAwQFBgUEAwEA\/\/79\/Pv6+vz9\/v8AAgMEBAUGBQQCAQD\/\/v38+\/r6\/P3+AAECAwQFBQYFAwIBAP\/+\/fz7+vv8\/f8AAQIDBAUFBgQDAgEA\/\/79\/Pv6+\/z+\/wABAgMEBQYGBAMCAQD\/\/v38+\/r7\/f7\/AAECAwQFBgUEAwIA\/\/79\/Pz7+vz9\/v8AAQIDBAUGBQQDAQD\/\/v38\/Pv6\/P3+\/wABAgMEBQYFAwIBAP\/+\/fz7+\/v8\/f7\/AQICAwQFBgQDAgEA\/\/79\/Pv7+\/z+\/wABAgMDBAUGBAMCAQD\/\/v38+\/r7\/f7\/AAECAwQEBQUEAwIBAP\/+\/fz7+vz9\/v8AAQIDBAQFBQMDAQD\/\/v79\/Pv7\/P3+\/wABAgMEBQUFAwIBAP\/+\/f38+\/v8\/f7\/AAECAwQFBQQDAgEA\/\/79\/Pz7+\/3+\/wABAQIDBAUFBAMCAQD\/\/v38\/Pv7\/f7\/AAECAgMEBQUEAwIBAP\/+\/fz7+\/z9\/v8AAQIDAwQFBQMCAQD\/\/\/79\/Pv7\/P3+\/wABAgMEBAUEAwIBAP\/+\/v38+\/v8\/f7\/AAECAwQEBQQDAgEA\/\/79\/fz7+\/3+\/wAAAQIDBAQFBAMCAQD\/\/v39\/Pv8\/f7\/AAEBAgMEBAUEAwIBAP\/+\/f38+\/z9\/v8AAQICAwQFBQMCAQEA\/\/79\/Pz7\/P3+\/wABAgIDBAUEAwIBAP\/\/\/v38\/Pv8\/f7\/AAECAwMEBQQDAgEA\/\/\/+\/fz8+\/3+\/\/8AAQMCAwQFBAMCAQD\/\/v79\/Pz7\/f7\/AAABAgMDBAUEAwIBAP\/+\/v38\/Pz9\/v8AAQECAwMEBQMCAgEA\/\/7+\/fz7\/P3+\/wABAQIDBAQEAwIBAQD\/\/v39\/Pv8\/f7\/AAEBAgMEBAQDAgEAAP\/+\/f38\/P3+\/v8AAQICAwQEBAMCAQAA\/\/79\/fz8\/f7+\/wABAgIDBAQEAwIBAP\/\/\/v39\/Pz9\/v\/\/AAECAgMEBAQDAgEA\/\/\/+\/f38\/P3+\/wAAAQICAwQEAwICAQD\/\/\/79\/fz8\/f7\/AAABAgMDBAQDAgEBAP\/+\/v39\/Pz9\/v8AAQECAwMEBAMCAQEA\/\/7+\/fz8\/P7+\/wABAQIDAwQEAwIBAAD\/\/v79\/Pz9\/v7\/AAEBAgMDBAQDAgEAAP\/+\/v38\/P3+\/v8AAQECAwMEBAMCAQAA\/\/7+\/fz8\/f7\/\/wABAQIDAwQEAwIBAP\/\/\/v79\/Pz9\/v\/\/AAECAgMDBAQCAgEA\/\/\/+\/v38\/P3+\/wAAAQICAwMEAwICAQD\/\/\/79\/fz8\/f7\/AAABAgIDAwQDAgIBAP\/\/\/v39\/Pz9\/v8AAAECAgMDBAMCAQEA\/\/\/+\/f38\/P7+\/wAAAQICAwMEAwIBAQD\/\/\/79\/fz9\/v7\/AAABAgIDAwQDAgEBAP\/\/\/v39\/P3+\/v8AAQECAgMDBAMCAQAA\/\/\/+\/f38\/f7\/\/wABAQICAwMEAwIBAAD\/\/\/79\/fz9\/v\/\/AAEBAgIDAwQDAgEAAP\/+\/v39\/P3+\/\/8AAQECAgMDAwICAQAA\/\/7+\/f38\/f7\/\/wABAQICAwMDAgIBAAD\/\/v79\/fz9\/v\/\/AAEBAgIDAwMCAgEAAP\/+\/v39\/P3+\/\/8AAQECAgMDAwICAQAA\/\/7+\/f39\/f7\/AAABAQICAwMDAgEBAAD\/\/v79\/f39\/v8AAAEBAgIDAwMCAQEAAP\/+\/v39\/f7+\/wAAAQECAgMDAwIBAQAA\/\/7+\/f39\/v7\/AAABAQICAwMDAgEBAP\/\/\/v79\/f3+\/v8AAAEBAgIDAwMCAQEA\/\/\/+\/v39\/f7+\/wAAAQECAgMDAwIBAQD\/\/\/7+\/f39\/v7\/AAABAQICAwMDAgEBAP\/\/\/v79\/f3+\/v8AAAEBAgIDAwMCAQEA\/\/\/+\/v39\/f7\/\/wAAAQECAgMDAwIBAQD\/\/\/7+\/f39\/v\/\/AAABAQICAwMDAgEBAP\/\/\/v79\/f3+\/\/8AAAEBAgIDAwMCAQEA\/\/\/+\/v39\/f7\/\/wAAAQECAgMDAwIBAQD\/\/\/7+\/f39\/v\/\/AAABAQICAwMCAgEBAP\/\/\/v79\/f3+\/\/8AAAEBAgIDAwICAQEA\/\/\/+\/v39\/f7\/\/wAAAQECAgMDAgIBAQD\/\/\/7+\/v39\/v\/\/AAABAQICAwMCAgEBAP\/\/\/v7+\/f3+\/\/8AAAEBAgIDAwICAQEA\/\/\/+\/v79\/f7\/\/wAAAQECAgMDAgIBAQD\/\/\/7+\/v39\/v\/\/AAABAQICAwMCAgEBAP\/\/\/v7+\/f3+\/\/8AAAEBAgIDAwICAQEA\/\/\/\/\/v79\/f7\/\/wAAAQECAgMDAgIBAQD\/\/\/\/+\/v39\/v\/\/AAABAQICAwMCAgEBAAD\/\/\/7+\/f3+\/\/8AAAEBAgICAwICAQEAAP\/\/\/v79\/f7\/\/wAAAQECAgIDAgIBAQAA\/\/\/+\/v39\/v\/\/AAABAQICAgMCAgEBAAD\/\/\/7+\/f3+\/\/8AAAEBAgICAwICAQEAAP\/\/\/v79\/f7\/\/wAAAQECAgIDAgIBAQAAAP7+\/v39\/v\/\/AAABAQICAgMCAgEBAAD\/\/\/7+\/v3+\/\/8AAAEBAQICAwICAQEAAP\/\/\/v7+\/f7\/\/wAAAQEBAgIDAgIBAQAA\/\/\/+\/v79\/v\/\/AAABAQECAgMCAgEBAAD\/\/\/7+\/v3+\/\/8AAAEBAQICAwMCAQEAAP\/\/\/v7+\/f7\/\/wAAAAEBAgICAwIBAQAA\/\/\/+\/v79\/v\/\/AAAAAQECAgICAgEBAAD\/\/\/7+\/v3+\/\/8AAAABAQICAgICAQEAAP\/\/\/\/7+\/v7\/\/wAAAAEBAgICAgIBAQAA\/\/\/\/\/v7+\/v\/\/AAAAAQEBAgICAgEBAAD\/\/\/\/+\/v7+\/\/8AAAABAQECAgICAQEAAAD\/\/\/7+\/v7\/\/wAAAAEBAQICAgIBAQAAAP\/\/\/\/7+\/v\/\/AAAAAQEBAgICAQEBAAAA\/\/\/\/\/v7+\/\/\/\/AAABAQEBAgIBAQEAAAD\/\/\/\/+\/v7\/\/wAAAAEBAQECAgEBAQAAAP\/\/\/\/\/+\/v\/\/\/wAAAAEBAQICAQEBAAAA\/\/\/\/\/\/7+\/\/\/\/AAAAAQEBAQIBAQEAAAD\/\/\/\/\/\/v7\/\/\/8AAAABAQEBAgEBAQAAAAD\/\/\/\/\/\/v\/\/\/wAAAAEBAQECAQEBAAAAAP\/\/\/\/\/+\/\/\/\/AAAAAQEBAQEBAQEAAAAA\/\/\/\/\/\/\/\/\/\/8AAAAAAQEBAQEBAQAAAAD\/\/\/\/\/\/\/\/\/\/wAAAAABAQEBAQEBAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAEBAQEBAQEAAAAAAP\/\/\/\/\/\/\/\/8AAAAAAQEBAQEBAQAAAAAA\/\/\/\/\/\/\/\/\/wAAAAAAAQEBAQEBAAAAAAD\/\/\/\/\/\/\/\/\/AAAAAAABAQEBAQEBAAAAAP\/\/\/\/\/\/\/\/8AAAAAAAEBAQEBAQAAAAAAAP\/\/\/\/\/\/\/wAAAAAAAQEBAQEBAAAAAAAA\/\/\/\/\/\/\/\/AAAAAAAAAQEBAQEAAAAAAAD\/\/\/\/\/\/\/8AAAAAAAABAQEBAQAAAAAAAAD\/\/\/\/\/\/wAAAAAAAAAAAAABAQECBAYGBwcGBQQDAgEA\/v38+\/r5+Pf29fT19\/n6\/P3\/AAECBAUGBwgJCgoLDA0NDg0MCggHBQMCAf\/+\/fz7+vn49\/b19PPz8\/X2+Pn7\/P7\/AAEDBAUGBwgICQoLCwwNCwkHBgQDAgD\/\/v38+vn5+Pf29fT08\/L09vf5+vz9\/gABAgMEBQYHCAkJCgsLDAsJCAYFAwIBAP79\/Pv6+fj49\/b19fTz9Pb3+fr8\/f7\/AQIDBAUGBwcICQoKCwwLCggHBQQDAQD\/\/v38+\/r5+Pj39vX19PT29\/n6\/P3+\/wACAwQFBQYHCAkJCgsLDAoJBwYFAwIBAP\/+\/fz7+vn4+Pf29vX09vf5+vv9\/v8AAQIDBAUGBwcICQoKCwsKCQgGBQQCAQD\/\/v38+\/v6+fj49\/b29fb3+f78\/P7\/AAECAwQFBQYHCAgJCgoLCwkIBwUEAwIBAP\/+\/fz7+vr5+Pj39vb19\/j6+\/z9\/v8AAQIDBAUGBgcICAkKCgsJCAcGBAMCAQD\/\/v38\/Pv6+fn4+Pf29vf4+fv8\/f7\/AAECAwQEBQYHBwgJCQoKCggHBgUEAwEAAP\/+\/fz7+\/r5+fj39\/b2+Pn6+\/3+\/wABAQIDBAUFBgcHCAgJCQoJBwYFBAMCAQD\/\/v39\/Pv6+vn5+Pf39vj5+vv8\/f7\/AAECAwMEBQYGBwcICAkJCQgGBQQDAgEA\/\/\/+\/fz8+\/r6+fj49\/f3+Pr7\/P3+\/wABAQIDBAQFBgYHBwgICQkIBwYEAwICAQD\/\/v39\/Pv7+vn5+Pj49\/j5+vv8\/f7\/AAECAgMEBQUGBgcHCAgJCAcGBQQDAgEA\/\/\/+\/fz8+\/r6+fn4+Pj4+fr7\/P3+\/wAAAQIDAwQFBQYGBwcICAgHBgUEAwIBAQD\/\/v39\/Pz7+vr5+fj4+Pn6+\/z9\/v7\/AAECAgMEBAUFBgYHBwgIBwYFBAMDAgEA\/\/\/+\/f38+\/v6+vn5+Pj4+fr7\/P3+\/wAAAQIDAwQEBQUGBgcHCAgHBgUEAwIBAAD\/\/v79\/Pz7+\/r6+fn5+Pn6+\/z9\/v\/\/AAEBAgMDBAQFBQYGBwcIBwYFBAMCAgEA\/\/\/+\/f38\/Pv7+vr5+fn5+vv8\/P3+\/wAAAQICAwMEBQUGBgYHBwcGBQQEAwIBAAD\/\/v79\/fz8+\/v6+vn5+fn6+\/z9\/v\/\/AAEBAgMDBAQFBQYGBgcHBwYFBAMCAgEA\/\/\/+\/v39\/Pz7+\/r6+fn5+vv8\/f3+\/wAAAQICAwMEBAUFBgYGBwcGBQQEAwIBAQD\/\/\/79\/fz8+\/v7+vr5+fr7+\/z9\/v\/\/AAEBAgIDAwQEBQUGBgYHBwUFBAMCAgEAAP\/+\/v39\/Pz7+\/v6+vr5+vv8\/f3+\/wAAAQECAgMDBAQFBQYGBgcGBQQDAwIBAQD\/\/\/7+\/f38\/Pv7+\/r6+vr7\/Pz9\/v\/\/AAABAgIDAwQEBAUFBQYGBgUFBAMCAgEAAP\/\/\/v79\/fz8+\/v7+vr6+vv8\/f3+\/\/8AAQECAgMDBAQEBQUFBgYGBQQDAwIBAQAA\/\/7+\/f39\/Pz7+\/v6+vr7\/Pz9\/v7\/AAABAQICAwMEBAQFBQUGBgUEBAMCAgEAAP\/\/\/v79\/f38\/Pv7+\/r6+vv8\/f3+\/\/8AAAEBAgIDAwQEBAUFBQYGBQQDAwIBAQAA\/\/\/+\/v39\/Pz8+\/v7+vr7\/Pz9\/v7\/AAABAQICAgMDBAQEBQUFBgUEBAMCAgEBAAD\/\/\/7+\/f38\/Pz7+\/v7+\/v8\/f3+\/\/8AAAEBAgIDAwMEBAQFBQUGBQQDAwICAQAA\/\/\/+\/v79\/fz8\/Pv7+\/v7\/Pz9\/v7\/\/wAAAQECAgMDAwQEBAUFBQUEBAMDAgEBAAD\/\/\/7+\/v39\/Pz8+\/v7+\/v8\/f3+\/\/8AAAEBAQICAwMDBAQEBQUFBQQDAwICAQEAAP\/\/\/v79\/f38\/Pz8+\/v7\/Pz9\/v7\/\/wAAAQECAgIDAwMEBAQFBQUEBAMDAgEBAAD\/\/\/\/+\/v39\/fz8\/Pz7+\/v8\/f3+\/v\/\/AAABAQICAgMDAwQEBAUFBQQEAwICAQEAAP\/\/\/\/7+\/f39\/Pz8\/Pv7\/Pz9\/f7\/\/wAAAAEBAgICAwMDBAQEBQUFBAMDAgIBAQAA\/\/\/+\/v79\/f38\/Pz8+\/v8\/f3+\/v\/\/AAABAQECAgIDAwMEBAQEBQQEAwICAQEAAAD\/\/\/7+\/v39\/fz8\/Pz7+\/z9\/f7+\/\/8AAAEBAQICAgMDAwQEBAQFBAMDAgIBAQAA\/\/\/\/\/v7+\/f39\/fz8\/Pz8\/f3+\/v\/\/\/wAAAQEBAgICAwMDAwQEBAQEAwMCAgEBAAD\/\/\/\/+\/v79\/f39\/Pz8\/Pz9\/f7+\/\/8AAAABAQECAgIDAwMDBAQEBAMDAgIBAQEAAP\/\/\/\/7+\/v79\/f39\/Pz8\/f3+\/v\/\/\/wAAAAEBAQICAgIDAwMDBAQEAwICAgEBAAAA\/\/\/\/\/v7+\/v39\/f39\/Pz9\/f7+\/\/\/\/AAAAAQEBAgICAgMDAwMDBAMDAgIBAQEAAAD\/\/\/\/\/\/v7+\/v39\/f39\/f3+\/v7\/\/wAAAAEBAQECAQICAgMDAwMDAwICAgEBAQAAAP\/\/\/\/\/+\/v7+\/f39\/f39\/f7+\/\/\/\/AAAAAQEBAQICAgICAwMDAwMDAgICAQEBAAAA\/\/\/\/\/\/7+\/v7+\/f39\/f3+\/v7\/\/\/8AAAABAQEBAQICAgICAwMDAwICAgEBAQAAAAD\/\/\/\/\/\/v7+\/v7+\/f39\/f7+\/\/\/\/\/wAAAAEBAQEBAgICAgICAwMDAgICAQEBAAAAAP\/\/\/\/\/\/\/v7+\/v7+\/f3+\/v7\/\/\/8AAAAAAQEBAQECAgICAgICAwMCAgEBAQEAAAAA\/\/\/\/\/\/\/+\/v7+\/v7+\/f7+\/\/\/\/\/wAAAAAAAQEBAQICAgICAgICAgICAQEBAQAAAAD\/\/\/\/\/\/\/7+\/v7+\/v7+\/v7\/\/\/\/\/AAAAAAEBAQEBAQICAgICAgICAgEBAQEAAAAAAP\/\/\/\/\/\/\/\/7+\/v7+\/v7+\/\/8AAQEBAQEBAQEBAgMFBggJCgsLDA0NDQ0MCwkIBwYFBAMCAQAA\/\/79+Pv7+\/r5+fj49\/f39vb19fX19PT09PPz8\/Pz8\/Py8\/T29\/j5+vv8\/f7\/AAABAgMDBAQFBgYHBwgICAkRCgoKCgsLCwsLDAwMDAwMDAwMCgkIBwYFBAMCAQEA\/\/7+\/fz8+\/n8+vn4+Pj39\/f29vb19fX19PT09PT09PPz8\/T19\/j5+vv8\/f3+\/wABAQIDAwQFBQYGBwcHCAgJCQsKCgoKCwsLCwsMDAwMDAwMCwoJCAcGBQQDAgIBAP\/\/\/v39\/Pz7+\/r6+fn4+Pj39\/f29vb29vH19fX19fX19PT19vj4+vr7\/P3+\/wAAAQICAwMEBQUGBgcHBwgICAkJCQoKCgoKCwsLCwsLCAwMDAsKCQgHBgUEAwICAQD\/\/\/7+\/fz8+\/v6+vr5+fn4+Pj39\/f39vb29vb29fX19fX19vf4+fr7\/P39\/v8AAAECAgMDBAQFBQYGBwcHCAgICAkJCQkKCgoKCgoLCwsLCwsKCQgHBgUEBAMCAQEA\/\/\/+\/v39\/Pn7+\/r6+vn5+fj4+Pj39\/f69vb29vb29vb29vb3+Pn6+\/z9\/f7\/AAABAQICAwQEBAUFBgYGBwcHCAgICAkJCQkKCQoKCgoKCgoKCQgHBwYFBAMDAgEBAP\/\/\/v79\/fz8+\/v7+vr6+fn5+Pj4+Pj39\/j39\/f39vb29vb3+Pn6+vr8\/f7+\/\/8AAQECAgMDBAQFBQUGBgYHBwcHCAgICAgICQkJCQkJCQkJCQkIBwYFBQQDAgIBAAD\/\/\/7+\/f39\/Pz7+\/v6+vr6+fn5+fn4+Pj4+Pj49\/f39\/f3+Pn5+vv8\/P3+\/v\/\/AAABAQICAwMEBAQFBQUFBgYGBgcHBwcHBwgICAgICAgICAgIBwYFBQQDAwICAQEAAP\/\/\/v79\/f38\/Pz7+\/v7+vr6+vr5+fn5+fn5+fn4+Pj4+Pn5+vv7\/P39\/v7\/\/wAAAQECAgIDAwMEBAQFBQUFBQYGBgYGBgcHBwcHBwcHBwcHBwYGBQQEAwMCAgEBAAD\/\/\/7+\/v39\/f38\/Pz8+\/v7+\/v6+vr6+vr6+vn5+fn5+fn5+vv7\/Pz9\/f7+\/\/8AAAEBAQICAgMDAwMEBAQEBQUFBQUFBQYGBgYGBgYGBgYGBgYGBQQEAwMCAgEBAQAA\/\/\/\/\/v7+\/v39\/f38\/Pz8\/Pv7+\/v7+\/v7+vr6+vr6+vr6+vv7\/Pz9\/f7+\/\/\/\/AAAAAQEBAgICAgMDAwMEBAQEBAQFBQUFBQUFBQUHBQUGBQUGBQQEAwMBAgIBAQEAAAD\/\/\/\/+\/v7+\/f39\/f38\/Pz8\/Pz8\/Pv7+\/v7+\/v7+\/v7+\/v7\/Pz9\/f3+\/v\/\/\/wAAAAEBAQECAgICAwMDAwMDBAQEBAQEBAQEBAUFBQUFBQUFBQUEBAMDAgICAQEBAAAA\/\/\/\/\/\/7+\/v7+\/f39\/f39\/f39\/v\/\/\/\/\/\/\/\/\/\/AAABAQIDAwQEBQUFBQYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBwUFBAMDAgIBAAD\/\/\/7+\/v39\/Pz8+\/v7+vr6+vn5+fn5+Pj4+Pj4+Pj39\/f39\/f39\/f39\/f39\/f29\/f39\/f39\/f4+Pj4+Pj4+Pj4+Pj5+fn5+fn5+fn6+vr6+vr6+vv7+\/z9\/f7+\/wAAAQECAgMDAwQEBQUFBgYGBgcHBwcICAgICAkJCQkJCQkJCQkJCgoLCQoKCgoKCgoKCgoJCQkJCQkJCQkJCQkJCQkICAgICQcICAgIBwcHBwcECAYHBgYGBQQEAwICAQEAAP\/\/\/\/7+\/f39\/Pz8+\/v7+\/v6+vr6+vn5+fn5+fn4+Pj4+Pj4+Pj4+Pj4+Pj4+ff4+Pj4+Pj4+Pj4+Pj4+fn5+fn5+fn5+fn5+vr6+vr6+vr6+vv7+\/38\/f3+\/v\/\/AAABAQICAgMDAwQEBAQFBQUFBQYGBgYGBwcHBwcHBwcHCAcICAgICAgICAgICAgICAgICAgIBwcHBwcHBwcHBwcHBwcHBgUHBgYGBgYGBgYFBQUFBQUEBAMDAgIBAQAA\/\/\/9\/v\/9\/v39\/fz8\/Pz7+\/v7+\/r6+vr6+vr6+fn5+vr4+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+vr6+vf6+vr6+vr6+\/v7+\/v7+\/v7\/Pz9\/f7+\/\/\/\/AAABAQECAgIDAwMDAwQEBAQFBQUFBQUFBgYGBgYGBgYGBgYGBwcHBwcHBwcHBwcHBwcGBgYGBggGBgYGBgYGBgYGBgUFBQUFBQUFBQUFBQUEBAQEBAQEAwMCAgEBAQAAAP\/\/\/f\/+\/v79\/f39\/fz8\/Pz8\/Pv7+\/v7+\/v7+\/v6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vr6+vv7+\/v7+\/v7+\/v7+\/v7+\/z9+\/v8\/Pz8\/Pz8\/Pz8\/f39\/v7+\/\/\/\/AAABAgEBAQICAgICAwMDAwMDBAQEBAQEBAQEBAQFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEBAQEBAQDBAQEBAQEBAQEBAMDAwMDAwMDAwICAgEBAQAAAAD\/\/\/\/\/\/\/7+\/v7+\/v39\/f39\/f39\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz9\/f39\/f38\/f39\/f39\/f39\/f7+\/v\/\/AP\/\/AAAAAAEBAQEBAQICAgICAgICAwMDAwMDAwMDAwMEBAMDBAQEBAQEBAQEBAQDBAQEBAQEBAQDAwMDAwMEAwMDAwMDAwMDAwMDAwMDAwMDAwMCAgICAgICAgIBAQEBAQEAAAAA\/\/\/\/\/\/\/\/\/\/7+\/v7+\/v7+\/v7+\/v39\/f39\/f39\/f39\/f39\/f39\/f39\/f39\/f39\/v79\/f39\/f39\/f39\/f39\/f39\/f39\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/wIEBAMDAwMDAwMDBAUGBgYHBwcICAgICAgICAYJCAgICAgICAcHBwcHCAYHBwYGBgUEBAMDAgEBAAD\/\/\/7+\/v39\/Pz8+\/v7+\/r6+vr6+fn5+fn3+fj4+Pj4+Pj4+Pj4+Pf4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+fn5+fn5+fn5+fn6+vr6+vr2\/Pr7+\/v8\/P39\/v\/\/AgABAQICAgMDBAQEBQYFBQYGBgYHBwcHBwcHCAgICAgICAgJCQkJCQkJCQkJCQkJCQkJCQkJCQkICAgKCAgICAgICAgIBwcHBwcHBwcHBwYGBgYGBgYGBgUEBAMDAgIBAQAA\/\/\/\/\/v79\/f39\/Pz\/+\/v7+\/v6+vr6+vr5+fn5+fn5\/Pf5+Pj4+Pj4+Pj4+Pj4+Pj4+Pn5+fn5+fn5+fn5+fn5+fn5+vr6+vr6+vr6+vr7+\/n7\/Pv7\/Pz9\/f7+\/\/8AAAEBAQICAgMDAwQGAwQEBQUFBQUGBgYFBgYGBgYHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQQEAwMCAgIBAQAAAP\/\/\/\/7+\/v39\/f38\/Pz8\/Pv7+\/v7+\/v6+vr6+vr6+vr6+vr6+vn5+fn5+fn5+fn5+fr6+vn6+vr6+vr6+vr6+vr6+vr7+\/v7+\/v8+\/v7+\/v7\/Pz8\/P39\/f7+\/\/\/\/AAAAAQEBAgICAQMDAwMEBAQEBAQFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgUGBgUFBQUFBQUFBQUFBQUFBQUFBAQEBAQEBQQEBAQDAwMCAgEBAQAAAAD\/\/\/\/+\/v7+\/v39\/P39\/fz8\/Pz8\/Pz8\/Pv7+\/v7+\/v7+\/v7+\/v7+vv7+\/v7+\/v7+\/v6+\/v7+\/v7+\/v7+\/v7\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/P39\/f39\/f39\/v7+\/\/\/\/\/wAAAAABAQEBAgICAgICAgMDAwMDAwMEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBQUEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQDAwQEAwMDAwMDAwMDAwMDAwICAgIBAQEAAAAAAP\/\/\/v\/\/\/\/7+\/v7+\/v7+\/f39\/f39\/f39\/f39\/Pz9\/f38\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/Pz8\/f39\/f39\/f39\/f39\/f39\/f39\/f39\/f39\/f3+\/v7+\/v7+\/v7+\/\/\/\/\/wAAAAAAAAABAQEBAQECAgICAgICAgICAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAgICAgICAgICAgICAgICAgICAgEBAQEBAAAAAAAA\/\/\/\/\/\/\/\/\/\/\/\/\/\/7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/f39\/f39\/f39\/f39\/f39\/f39\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/gIDAgIDAwMDAwMDBAYHCAoLCwwNDQ0ODg0MCgkIBwYFBAMCAQEA\/\/79\/fz7+\/r6+fn4+Pf39\/b29vX19fT09PT09PP28\/Pz8\/X29\/j5+vv8\/f7\/AAABAgMDBAQFBgsHBwcICAkJCQoKCgoLCwsLCwwMDAwMDAwMCwoJCAcGBQQDAgEBAAP\/\/v38\/Pv7+vr5+fj6+Pf39\/b29vb19fX18\/X09PT09PT09fb3+Pn6+\/z9\/v\/\/AAECAgMDBAQFBQYGBwcICAgJCQkJCgoKCgoLCwsLCwsLCwsLCgkIBwYFBAMDAgABAP\/+\/v39\/Pz7+\/r6+fn5+Pj4+Pf39\/f29vb29vb29fX19fX29\/j5+vv8\/P3+\/\/8AAQECAwMEBAUFBgYGBwcHCAwICAkJCQkJCgoKCgoLCgoLCwsKCQgHBgUHBAMCAQEA\/\/\/+\/v39\/Pz7+\/v6+vr5+fj7+Pj4+Pf39\/f39\/f29vb29\/b3+Pn6+\/z9\/f7\/AAABAQICAwMEBAUFBQYGBgcHBwgICAgICQkJCQkJCQsKCgoKCgkIBwcGBQQDAwIAAQAA\/\/7+\/f39\/Pz7+\/v2+vr6+fn5+fj4+Pj4+Pf39\/f39\/f39\/j4+fr7\/Pz9\/v7\/AAABAQICAwMEBAQFCQUFBgYGBwcHBwgICAgIBQoJCQkJCQkJCQgHBgYFBAMDAgIBAAD\/\/\/7+\/P39\/Pz8+\/v7+vr6+vn5+fn5+Pj4+Pj4+Pj4+Pj3+Pn5+vv8\/P3+\/v\/\/AAABAQICAwMDBAQEBQUFBQYHBgYGBwcHBwcHBwcICAcJBwgIBwcGBQUEAwMCAv0CAAD\/\/\/7+\/v39Afv8\/Pv7+\/v7+vr6+vr5+vn5+fn5+fn5+fn5+fr7+\/z7\/v7+\/\/8AAAEBAf4DAgMDAwQEBAQFBQUFBQUGBgYGBgYGBgYGBwcGBwcHBgUFBAQDAwICAQEAAP\/\/\/\/7+\/v79\/P38\/Pz8\/Pv7+\/v7+\/v5+\/r7+vr6+vr6+vr6+\/v8\/Pz9\/v7\/\/wAAAAEBAQICAgMDAwMDBAQEBAUEBAUFBQUFBQUFBQUFCAUGBQYFBQQEAwMCAgEBAQAAAP\/\/\/\/7+\/\/7+\/f39\/f38\/P77\/Pz8+\/v7+\/v7+\/v6+\/v7+\/v7\/Pz9\/f7+\/v\/\/\/wAAAAEBAQECAgICAgMDAwMDAwQEBAQEBAQEBAQEBAQFBQQEBQUEBAMDAwICAQEBAAAAAP\/\/\/\/\/+\/v7+\/v79\/f39\/f39\/Pz8\/Pz9\/Pz8\/Pz8\/Pz8\/Pz8\/f39\/v7+\/\/\/\/AAAAAAEBAQECAgICAgICAwMDAwMCAwMDAwQEBAQEBAQEBQMEBAQDAwMCAgIBAQEBAAAAAP\/\/\/\/\/\/\/v7+\/v7+\/v39\/f39\/f39\/f39\/f38\/f38\/Pz8\/P39\/f7+\/v7\/\/\/8AAAAAAAEBAQEBAQICAgICAgICAwMCAwMDAwMDAwMDAwMDAwMDAwMDAgICAgIBAQEAAAAAAP\/+\/\/\/\/AAAAAQEBAgQFBgYGBgUEAgEA\/v0C+\/r49\/b19PPz8vH09ff8+\/z+\/wACAwQGBwgRCwsMDA0ODw8NCwkDBQQDAQD+\/fz6+fj09vX08\/Py8fHz9fcA+\/z9\/gABAgQFBgcICQoLDAwNDg4MCggHBQMGAf\/+\/fv6+fj39vX19PPx8fL09ff5+vz9\/wABAwMFBgcICQoLCwwNDQ0LCQMGBQMCAf\/+\/fz7+vv49\/b19PTz8vP19\/j6+\/3+\/wECAwQFBgcICAoLCwwNDQwLCQgGBQP6AP\/\/\/fz7+vn49\/b29fT08\/T29\/n6\/P3+AAECBAQFCAcICQkKCwwMDQwKBwcGBAMCAP\/+\/fz7+vn3+Pf29fX08\/X2+Pn7\/P3+AP8CAwQFBgcICAkKCwsMDAsJCAcFBAMBAP\/+\/fv7+vn5+Pf29vX08\/b3+Pr7\/P7\/AAEHAgQEBgcHCAkKCgsLDAcJCAYFBAIBAP\/++Pz7+\/n5+Pj39vb19fb4+fr8\/f7\/AAECAwQFBQcHCAkJCgsLDAoHBwYFAwIBAP\/+\/fz7+\/r5+fj39\/b19ff4+fv8\/f0AAAECAwQFBgYHCAgJCQoKCwkIBwUEAwIBAP\/+\/fz7+\/r5+fj39\/b29ff0+vv8\/f7\/AAECAwUEBgYHCAgJCQoKCwoGBwUEAwIBAP\/+\/fz\/+fr5+fj39\/b29vf5+vv8+\/4AAAECAwQFBQQHBwgJCQkKCggHBgX+BAIBAP\/+\/fz8+\/r6\/fj49\/f29\/j5+vr9\/f8AAAECAwQEBQYHBgcICAkJCQgGBQQDAgEAAP\/+\/f38+\/v6+vn5+Pj79vn6+\/z9\/v\/\/AAECAwMEBQUFBgcHCAgICAcGBQQAAwEBAP\/+\/v38\/Pv7+vr5+fj4+Pr6+\/z9\/v8AAAEDAgMEBAUFBgYHCgYIBwYFBAQDAgEAAP\/+\/v38\/Pv7+\/r6+fn5+fr5\/Pz9\/v8AAAECAgMDBAQFBQYGBgcHBwYDBQMDAQEAAP\/+\/v39\/Pz8+\/v6+vr5+fv7\/P3\/\/v8AAAEBAgMDBAQEBQYFBgYGBgUEBAMCAQEAAP\/+\/v39\/fz8+\/v7+vr6+\/v8\/P3+\/\/8AAQEBAwIDAwQEBAUFBQYFBgUEAwMCAQEAAP\/+\/v\/9\/fz8\/Pv7+\/r6+\/v8\/f3+\/\/8AAAEBAgIDAQQEBAQFBQUGBQQEAwICAQEAAP\/\/\/v7+\/f38\/Pz8+\/v7+\/z8\/f\/+\/v8AAAEBAgICAwMDBAQEBQUFBQQDAwICAQEA\/\/\/\/\/v7+\/f39\/Pv8\/fv7+\/z9\/f7+\/\/8AAAABAgICAwMDAwQEBAQFBQMCAwIBAQEAAP\/\/\/\/7+\/v39\/fz8\/Pz8\/P39\/v7\/\/\/8AAAEBAQICAgMDAwMEBAQEBAMDAgIBAQEAAP\/\/\/\/7+\/v39\/f39\/Pz8\/P39\/v7\/\/gABAAEBAQICAgIDAwMDAgQEBAMDAgIBAQAAAP\/\/\/v\/+\/v7+\/f39\/f38\/f3+\/\/\/\/\/wAAAAEBAQEBAgMCAgMDAwMDAwMCAgEBAQAAAP\/\/\/\/\/+\/v7+\/v39\/f39\/f3+\/v\/\/\/wAAAAEBAQECAgICAgMDAwMDAgICAgEBAQAAAAD\/\/\/\/\/\/v7+\/v79\/f39\/f7+\/v\/\/\/wAAAAABAQEBAgICAgIDAwMDAwICAgEBAQAAAAAA\/\/\/\/\/v7+\/v7+\/v39\/f7+\/v\/\/\/wAAAAABAQEBAQICAgICAgMDAgICAQEBAQAAAAD\/\/\/\/\/\/\/7+\/v7+\/v79\/v7+\/\/\/\/\/wAAAAABAQEBAgEBAgICAgICAgICAQEBAQAAAAD\/\/\/\/\/\/\/\/+\/v\/+\/v7+\/v7+\/\/\/\/\/wAAAAAAAQEBAQEBAgICAgICAgIBAQEBAAAAAAD\/\/\/\/\/\/\/\/\/\/v7+\/v7+\/v\/\/\/\/\/\/AAAAAAAAAQEBAQEBAQICAgICAgIBAQEBAAAAAAAA\/\/\/\/\/\/\/\/\/\/7+\/\/7+\/v7\/\/\/\/\/AAAAAAABAAEBAQEBAQEBAgICAgEBAQEBAAAAAAAA\/\/\/\/\/\/\/\/\/\/7\/\/\/7+\/v\/\/\/\/\/\/AAAAAAAAAAEBAQEBAQEBAQICAgEBAQEAAAAAAAAAAP\/\/\/\/\/\/\/\/7\/\/\/\/+\/\/\/\/\/\/\/\/AAAAAAAAAAEBAQEBAQEBAQEBAgEBAQEBAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAABAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEAAAAAAAAAAP8AAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/8AAAAAAAAAAAAAAQABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/wAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAA\/\/\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAEAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/\/\/\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAP\/\/\/\/8AAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\/\/\/\/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+\/Pr39\/n8\/gADBQMJCQ0PDwsJBgMBAfz69\/Xz9Pf6\/P\/\/BAkICgwNDQoHBQIA\/fv59\/Xz8vb4+\/4AAgT\/CAsMDQoHBAL\/\/fv59\/Tz8vX4+\/0AAgUGCAcLDQoHBAL\/\/fv59\/Tz8vb4+\/3\/AgQGCAcLDAkHBAIA\/fv59\/r18PX4+v3\/AQMFBwsKDgkHBAIA\/vz6+Pb08\/L4+v3\/AQMFBwkLDAoHBQAA\/vz6+Pb18\/b4+\/4A\/wMFBwkKDAoHBQUB\/vz6+ff19Pb4+\/0A\/wEECQkKDAoHBQMB\/\/37+ff29Pb4+\/3\/AQMECAcKDAoIBQMB\/\/36+\/z29fb4+\/3\/AQMFBggLCQoGBwMB\/\/38+vj39fj3\/P3\/AQMFBggJCwoIBwMB\/v78+vn39vb4+\/0AAAIEBggJCwsIBgQCAAD6+vn39vb4+v3\/AAP\/BgcJCgsIBgQCAP79\/vf49vb4+vz+AAIEBQcICgsHAgYCAP\/9+\/r49\/b5+vz\/\/wMDBQcICQsIBgQCAP\/9+vr49\/b4+vz+AAIDBQYICQoIBgQCAf\/9+\/r59\/b4+vz+AAEDBQYKCAoIBgQCAf\/9\/Pr5+PX5+\/z+AAEDBAYHCAoIBgUFAP\/+\/Pv5+Pf4+\/79AAEDBAUHCAkIBgQDAf\/7\/Pv5+Pf4+vz+\/wECBAUGCAkJBgQDAf\/+\/fv6+vb3+vz9\/wECBAUGCAgIBgUDAQD+\/fv6+fj4\/fv9\/gACAwUGBwgJBgUDAP4A\/Pz6+fj3+vv9\/\/0DAwQGBwgJBgUDAQD\/\/fz6+\/j4+vv9\/gABAwQFBwgIBwYCAgD\/\/fz7+vj4+vv9\/gABAwQFBgcIBwQDAgD\/\/fz7+vn3+vv9\/gABAgQFBgcIBwYDAAT+\/vz7+vn4+fv9\/gIAAgMFBgcIBwUDAgAB\/fz8+vn4+fv8\/v8BAgMDBgYIBwUEAgH\/\/v38+\/r5+Pv8\/v8BAgMEBgYGBwUEAgEA\/v38+vr5+fv8\/v8AAgMEBQQJBwUEAgEA\/v38+\/z5+fr9\/f8AAQMEBQYHBwQEAwIA\/\/38+\/r5+fv8\/AH\/AQIDBQUGBwUDAgEA\/v79+\/r6+fv8\/\/0BAQIDBAUGBwUEAwEA\/\/79+\/v5+fr8\/f8AAQIDBAIIBgYEAwIA\/\/79\/Pv6+fz7\/f4AAQIDBAUGBwYEAwEA\/\/79\/Pv6+fv5\/v7\/AQIDBAUGBggEAwIB\/\/79\/Pv6+vr8\/f0AAQEDAwQFBgYEAgICAP7+\/fz7+vr8\/f7+AQICAwQFBgYEAwIBAfz+\/fz7+vr8\/f7\/AAECBAQFBgYEAwIBAP\/8\/\/z7+vr8\/f7\/AAECAwQDCAYEAwIBAP\/+\/fz7+\/r8\/P3\/AAECAwQFBQYFBgIBAP\/+\/fz8+\/r7\/f0BAAABAwMEBQYFAwIBAP\/+\/v38+vr7\/P3\/AAAAAwUEBQYFAwIBAP\/+\/\/z8+\/r7\/P3+\/wEBAgMEBQUFBAMCAf4B+\/38+\/v7\/P3+\/wACAAQEBAUFBAMCAQD\/\/v39+\/v7\/P3+\/wABAgMEAgcFBAMCAQD\/\/P39\/Pv7\/P3+\/wABAgMFAQYGAwMCAQD\/\/v39+\/v7\/P3+\/wABAgIDBAUFBAMCAQD\/\/v7++\/v7\/P3+\/wABAgIDBgUEBAMCAQD\/\/\/79\/Pv6\/P3+\/wABAQIDBAUDBgECAgAA\/\/79\/fz7\/P3\/\/gAAAQIDAwQFBQMCAQEA\/\/79\/fz7+\/wA\/\/8AAQIDAwQFBQQCAQEA\/\/79\/fz7+\/3+\/gABAQICAwQEBQMCAgH\/\/\/\/9\/fz8+\/z+\/v8AAQECAwQEBQQCAgP+\/gD+\/P38+\/z9\/v4AAAECAwMEBQQDAgMA\/v7+\/f38+\/z9\/v\/\/AgECAwMEBAQDAgEAAP3+\/v38\/Pz9\/v8AAQMBAQMEBAQDAgEAAP\/8APz8\/Pz9\/v8AAAEDAQUCBAQDAgEAAP\/+\/v39+vz9\/v\/\/AAECAgMFAwMDAgEBAP\/+\/v39\/vr+\/f4AAAECAgMDBAUDAQIBAP\/\/\/v39\/Pz8\/f\/\/AQEBAgMDBAQDAgACAf\/\/\/v39\/Pz9\/f\/+AQEBAgIDBAQEAQMBAAD\/\/v79\/fz9\/\/4AAAABAgIDAwQEAwIC\/wH\/\/v79\/fz8\/v\/\/\/wABAQIDAwQEAwIAAAD\/\/v79\/fz9\/f7\/\/wABAQIDAwQEAwMB\/\/8A\/\/7+\/fz9\/f0AAP8BAQICAwMEAwICAP7\/\/\/7+\/f38\/f79\/wIBAQICAwMEAwIBAAIB\/f7+\/f38\/f7\/\/wEAAQECAwMDAwIBAQABAP79\/f39\/f7\/\/wAAAQACBAMDAwIBAQAA\/\/\/\/\/\/39\/P7\/\/wAAAQECAQIEAwIBAQAAAP\/9\/v79\/f7\/\/wAAAQABAgMDAwIBAQAAAP\/\/\/\/79\/f7\/\/wAAAAECAgIDAwIBAQAA\/\/\/\/\/v39\/f7+\/\/8AAAECAQICAwIBAQEA\/\/\/\/\/\/7+\/f7\/\/\/8AAAECAgEDAwICAQEAAP\/\/\/\/7+\/v7+\/\/8AAAECAQECAgICAQEAAP\/\/\/v7\/\/f3\/\/\/8AAAEAAQMBAgICAQEAAP\/\/AP79\/v7+\/\/8AAAEBAQIBAgMCAQEAAAAA\/v\/\/\/v7+\/\/\/\/\/wACAgECAgICAQABAQD\/\/\/\/+\/v7+AP\/\/AQABAQEBAgICAQEB\/\/8AAP\/+\/v7+\/\/\/\/AQAAAQEBAgICAQEAAAAA\/\/\/\/\/v7+\/\/8A\/wABAAEBAgICAQEAAAD\/\/\/\/\/\/\/7+\/\/8A\/wABAQEBAQIBAQEBAAAA\/\/\/\/\/\/7+\/v8A\/wABAQEBAQECAQAAAQD\/AP\/\/\/\/\/+\/\/\/\/\/wAAAQEBAQECAQEBAAAAAP\/\/\/\/\/\/\/wD\/\/wAAAAEBAQEBAQEBAQD\/AAD\/\/\/\/\/\/\/\/\/AAAAAAEBAQEBAQEAAQAAAAD\/\/\/\/+\/wD\/AAAAAAEBAQECAQABAQAAAAD\/\/\/\/\/\/v8AAAAAAAABAQEBAQEBAAAAAAD\/\/\/\/+\/\/8A\/wAAAAABAQEBAQEAAAEBAQEBAQAAAAIA\/vr4+vb3+vz\/BAALAwkMDhAOCwgGBfcG9AHu9PP09\/r9\/QMCFfkMDQ4OCggI9A78APv29PLy9fj7+QIG9wsJCgwNCgcJ8g\/89vr69PPx9fgD+P4JAQULCQsNCgcEBAf\/+vf39fPx9Pf\/+PsCBQQHCQsMCgcG9w\/5\/fr49vT16gf6+f4BAgUHCAoMEwkCAgUA\/Pr49vXy8QHoB\/oAAgQGCAoMEAMOAAf8\/fv59\/X48fru+wQAAgQGCAoNEPwPAQIA\/vz6+Pbz9fj3+QP\/AgQGCAkLCwT\/EP8B\/\/z7+ff26gLz\/\/QDAQQGB\/8XCwsKBQMB\/\/37+fbz\/ugI9\/3\/AQMFBwv\/GQQFBgQCAP78+vAA9PH4+\/3\/AQMFCAYGFAkNBAUBAP78APrv9\/rz\/Pz+AAIEBgkJAhj9CAQCAP78+wH6+O\/5+fz+AAIE\/g0MBQsIBwQCAP\/5APvvAvL4+fv9\/wEEBwb\/CwoJBwUD\/vj7BfIA9vf3+vz9A\/cIAw\/\/DAoKBwUD\/v4G8QXx+vb3+fv9+wsAAgMJCAoKBwUKB\/wA\/Pv5+Pf39\/76AQEBAwUGCAkLBgn7BfsC\/Pz6+ff0+gP9\/QEBAwQGBwwGDQEJ\/wH+\/fv6+fL5\/PT9\/f8BAwQFCQYCEwUDAwH\/\/v\/89fn29\/r7\/f8BAgQMAQoECwMFAwH\/\/vb\/8QPt+vn8\/f8BBgQEAAkLCAcFAwL9CfL+9vz49\/r7\/gEADPkEBgcICQYGCvj9Cvz8+\/n4+\/v89\/oCAQMEBgcEDwQECQQE\/\/78+\/b57fv3A\/8AAgMH+QcL\/wIFBAIB\/\/7\/+vb1APz7\/P7\/AQIEBgYCBg4FBAIBAAD7Bfz38P\/7\/P7\/\/\/8DBAwJCAgGBP4DAgD+Afr5+Pj6\/PYAAwUABgUGBwgFDv8AAf79\/Pv6+fb89fz9BQIDBAUG\/g0JCAYEAP\/+\/fv3A\/b1Af3+AAECA\/8MBwUIBQQCAQH4BPsB+fn6+\/3+\/wYCBAIABwcHBQQGA\/389\/\/8+vn5+v7\/BgABAgQFBgcGCwMG\/QD8\/vz7+P3\/8vr7AAABAgQDDAoJBwMFAQD\/\/vr8+\/z+8f\/+\/gABAv8BBQUDCQUDAgD\/+\/8D8\/n6+vv9\/v8G\/QMEAgcHBgQDAQL7\/v77+\/r5+vsB9gP+BP8DBQYGAgYCCgD7AP79\/Pv6+vgD\/vkBAQIDBAUGAAgIAQEA\/\/779vz1\/fr9\/v8ABQIKAgQBCAUEAwH7CPv+9\/77+fv8\/f0F+QIDCQQFBgUE\/AP8BP\/9\/Pz7+Pz6Afz\/AAIDAwQGAQkFAwIAAP7+\/P4B+Pj+\/P7\/AAECAgMEBAYIAgIBAP8C+f0C8vv8\/P7\/\/QT7BgUCBgYEAwX9A\/0AA\/v8+vr79wH8AgEAAwMEBQcDAQj9AAD+\/f389v\/\/+fcE\/gEBAgMFAgMLAAQEAf\/+\/v\/5\/vv6+PwB\/wABAgUCCQUGBQICAP8B\/fkA+vv6\/P3+\/\/sH\/AYDCAUGBAMBA\/\/9\/fv5\/\/v7\/Pn+BfwFAAUDBAUGBAAFAP4F\/P79\/Pv7+QP6BQD\/AgIDBAMBBwQGAAEA\/wL7\/fv9+vv+\/v\/7AwADBwYDBQMDAv0E\/gL4A\/r7+\/z9AQD9BPwFAwQFBQAFAwD7Bv3+\/fz6APn8AP4AAQICAwYBAwkB\/wIA\/\/8C\/Pz\/+\/r7\/v8AAAL\/CAEGAwUDAgEC\/gAA+AD7+\/v+APsE\/gX\/AwMEBAMBCP0F\/\/\/+\/fz9+\/\/6+wD\/AAEDAQYHAggDAgIBAAIA\/\/j\/\/Pv9\/f4A\/\/0F\/QQEBAUEAwME\/gH9APz9\/Pv9\/QH\/\/\/8CAgMDBAICBwEF\/wD\/\/v0B+fn+Afz\/\/wADAwT+BwMCAwIB\/\/0CAf0A+\/z8\/f7+\/QADA\/8EAwQEAAT9AQD+\/\/79\/fv\/\/\/\/6AfwCAQIBAwgEBwICAQD\/AP36\/\/v\/+\/7+\/wMCAQQBBQMEAwMCAf\/+\/QH+\/fz8+gH\/\/gP\/AAICAwADBgEGAwAA\/\/79+\/sA\/\/wA\/gAAAv4BAAMDBQMCAgMD\/vv\/\/fz8\/P36Av0DA\/4DAwQEAQUDBf\/\/AP\/+\/P35\/\/oAAf8AAQADAQQGBQIDAgEA\/\/\/\/\/P\/6\/fz+\/\/0EAf4EAQEEBAQBAgMAAfwC\/f79\/P35Av8B\/\/8CAgMDAwMGAv4CAv7\/\/v7\/+f77\/gH+AQEBBP8DAwUFAwIAAP\/9\/v3+\/\/38\/v79AAT\/AgADAwQDAAICAQAA\/f79\/PoA+gAA\/gABAQIDAQMEAQIAAf\/\/\/vz+\/vz8\/\/\/+AAD\/AgMBBgQAAwIBAAH9\/gD7Afn9\/f78\/gIB\/wUCAwMBAgEEAv8D\/v\/+\/v36AP0A\/wAAAgAABAMDAgMBAf8C\/\/\/9\/vz7\/v7\/\/wD\/Af4DAwMDAwED\/wIAAf\/+\/v77\/\/z\/\/QABAQED\/wQEAwQBAQAAAf79\/wD6\/v7+\/wABAf8AAwIDAwED\/\/8AAQH+\/v77\/v7\/\/gMAAAEBAQIABQAF\/wEAAP4A\/QD+\/f\/+\/\/\/\/AwIBAAEBAwICAAL+AAD+AP\/9\/v7\/\/gAC\/gACAQICAgIE\/wL+AAD\/\/v7+\/f8A\/P8AAAD\/AgIEAQMCAQICAP\/+\/\/7+\/v7\/\/v7\/A\/4BAgECAgT\/AAICAAD\/\/gH9\/\/79\/QD\/AAECAAQDAgICAQAAAQD+\/v\/+\/v79\/gD\/AP8BAQEBBAECAgICAAAAAP7+\/\/\/\/\/P\/\/\/wAAAgACAAMCAgEBAQAB\/\/4A\/v7+AP\/9AQAAAQECAAMAAgACAQAA\/gD+\/f\/+\/v\/\/\/wH\/AAECAgQCAgEAAgD+\/\/\/+\/\/7\/\/f7+AQD\/AQEBAwIBAwEDAQAAAP7\/\/v\/+\/v7\/\/wH\/\/wIBAQICAgEBAAEB\/gAA\/v\/+\/v0AAP8AAgABAQIBAwECAQAAAP8A\/\/7+\/\/7\/\/\/8AAv8BAAICAgIBAgD\/AQD+\/\/\/+\/\/\/+AP8AAAEBAQAAAgH\/AAIBAQECBgYNCQgHBgUK9vIB\/v\/8+wno+fUF5\/H68\/UDDQz58BEL+wIDB\/7yCQkXGAgMDBz9CwgTCgMHBQTxBgQC7\/0F\/vf26APjB+758fH09Pj+AP7q+wEBAwAEFRAAEPsNDP8SGgUICAkHBAHz9Qjy+AT5+Pn67uv97QLu8vH0CAPy9fv0\/QD3F\/4AFf4ECgkOEhwXBhEUCQgGCwIF\/\/EN\/PsCB+oB4wfw8fTs9e\/q+P\/8\/Pr8Bv70Bgv3\/gYHDgIK+gAAGwsPDAMNAhQB+QL6\/\/z4Cez9A+n59vTs5PMA+QH7+fru+PkMDAAPAwX9Bhj++xIcCQ0ZCg4L+AESAAIABgnu\/wfv9ff4\/PD85fPt\/fP39Qnu+fL4+gUCCfz5EgADHAYMCw0SFQ0OEwQGAwkL\/vECBAj5+fL5+vQG6Ov29PHr9gb9APsAAv0LBQgC\/AgIDQAGBQkBEgwLCwMBDAsE\/AH\/\/QryAfj1\/PX4+Ob5\/uzx8v\/7+gfy\/vr5Cv8EBxACDRYDDxEMDA4LDQkQAPwEAQz48\/\/49wL4+ADp9\/T26fn1+P3xAQb4+v4CAv0E\/P4LChAHCwQc\/xQFGA8BBwUAAfsI9vL5\/fsD9vTz+On79\/X0+v0BA\/L6AQAAAwb4Ewn4DwcLCfwMBgwNDQoIBRP+AwP1BP72\/\/31BOz\/+\/X58+35+fz\/8\/78\/v7\/BAYC\/gYFBP4LEw4BFQkMBQgUBAQR8gr+BQL39PcF+PX59AT0+fH28f72+fH59gkF+AoACfoNBAEBBgoJCwwLABMBE\/gJAwL9+gAB+\/r8\/PcC\/\/D9\/e\/3++v4\/QED9\/gA\/Qf8\/Qz\/DAUHCQoE\/xb9DAsJDPsGBwz0CvwA\/gTw+v4A8gT0+Pfz\/\/z78\/z5+gXvDfoA+gsA\/\/sBCBEFDgsIBAINCAoGAQIC\/vn7A\/v1\/f34+v3z9fL28u74+ATz\/Pr0\/wABAgwFC\/wN\/Q8JBxD+EwQOBQwIAwUC+\/n\/BPkE+v\/+A\/j+8vL\/8vf28\/X1+vwB\/v\/2AwwGAQAOAwgABw4HCw0GCgoOBwD6AAgD\/gb99v\/8AfT3+fn1\/\/Hy\/fzt\/vb4\/\/kI9AIEAwkMCQEIBgMSBwoHEA0IBQQHAgIG+f8B+\/sB+f3w9\/j+\/u0A8\/L2Afj1AAf7BPsK+Av+BQIIBAYLEgkRBAkHBQL+CQcF+fv\/AfUC8fr88\/z38\/YC+\/LzBPj7+fwGCAAFBgAFC\/0NAAcICggJEQ7\/CQYF\/wT9\/Qb3\/f8D+Pv8+P\/0AfD99PT77\/338\/\/9AAEA\/v4EAg8ACAoCBQYJCREGBw77DgUD\/gj9\/QT+9vX6+fr5+fEC9fHz+Pn9\/QD\/+wYF\/QMHAgsG\/wAO\/Qv\/EQASCwQDBQEK\/wj1B\/z6\/vr0\/vj89vf79\/zy\/f75A\/3+\/P8FA\/8EAv8FDAcEAQoHBQ0ICwUFBf8ECf4BAAL+9AD6+vr1\/\/389Pz59fv7\/\/H6BfkF\/fz+CAQACAYHBQoHBg0BDQgDBv7+CAcD\/gT6AfsA8vn4+Pv6\/vT0Af3zAPkD+gH4\/gIG\/QL6\/woM\/Q3\/CgoKBQ0MAAUGAwH6AgAA\/gH5APgD8\/T99\/z3+Pv5+PwD9\/0BA\/4CBwQA\/wYH\/AgDAgkCDQgHDQoBAAkG\/wUDA\/z9A\/f1\/fr79Pzw+vP59QED9\/kF\/\/4C\/P4CBwYGBAoGCgQDDQoECQYHAQkFBvv9\/QD\/\/vQD\/QD3+Pf0\/fP++fn6+wP6Afj6\/wMBBQQFCP8KBwUIBw4ECwUFAQL6B\/\/5BwP3\/\/7\/\/f3++\/r8+PL88v3\/9vv\/AAEA+P\/7B\/8FB\/4KDAEEBwcIBwMLBgEFCAEB\/\/\/5Af36\/\/b1Aff09gD39fv8+fv7+wAABf4CCP8HCAQDBQADCggGBwoICgsBBAEEAPkAA\/\/8+\/v5+vz7+vr19\/b49\/r4Af8AAP4BAwMJCAYECQUECAYFCwYGCAIHA\/wBAgD8\/wD++vn9+PX++\/\/2+PX49gH7\/fkBAgACBAAAAQMFAwIIBwoOBQcFAAwKAAAEBf0EAQD6Av30+v\/7+P\/69fv\/9vT\/+QD8\/Qf+Bf0CAf4EBAcEAwYMCAQGCgkFCPwDBvn+\/v\/\/\/vwA+vf9\/\/T5\/vjy\/fgA\/\/v5\/vv+\/vwBAv0EBgcBDAcDCAUJBQgGAQj\/AgEFAQQA+\/sF+QL7+Pb49fv8+PX++\/\/\/\/v8ABAUG\/QP+CQQBBQUECAUHBwsEAQcAAwH+AP0EAP4A\/Pj5\/Pz7APj89fj+\/fr5\/v\/+AAMABAQBBgMDBgcECQAGBggI\/wYBAgAB\/QL99\/\/+\/AD6\/fv6+\/oB+Pr\/\/fr5A\/4AAgH+\/gMCAwAABgUCBQgDCQkEBgMBBAb\/\/QD7AQD7\/f7\/+\/j7Afr1+AH7+foD+\/oFA\/4EAP8ABfsBBgUCBgj+CQsKBf8GAQEF\/gH9AAH7\/PkAAP\/29\/z3\/fv2+v38+AIA+wIB\/AAEAv8EBAgDBgkHBgUCBwUGBQIC\/AIBAf0C\/vn4+f76\/Pr8+fv5+wEAAPr6\/vwCA\/\/+BQYB\/wcBAQcHBQUDCQMDBwT+BAT9\/wH++wEA\/P8A+\/r++\/36\/f33+wD7\/fwB\/AAFAv4BBAEEBv8FCwcBBQMCCAEBAv8B\/v0A\/\/79\/f75+\/n7\/\/f5Aff+A\/n7\/wX9\/QEAAAMCBAUFAgMHBAQJCAMBBgUG\/wAAAAIC\/\/7\/+\/7\/\/P\/5+fn6+fj5\/P79\/\/8BAAACBQEBAgUDBQcDBgMFBwcDAgAHAAIB\/gAAAP\/\/\/\/n+APr6\/Pv8APf3AAD8\/AEAAf8C\/QIC\/v8HAQMFAgQCBwYHAgEEAgYDAQL+AQL+\/f7\/+vv\/\/fv8+\/z++P39\/f\/+\/QECAQL9\/gEEAgABBQAFCAcGAwQFAAIBAwH\/\/vz\/\/AH++fz++f7+\/Pz4+vz7\/QL\/AP7+Af8FBQEFBQMFBAX\/Av8D+\/ID8ff9Cu38CwIEGAkP\/CMMCQ0PBAb1D\/P\/8wTx8vjzDAfxEv4aDwoKGAUc9wkA8An58gXy4QsECPIHCgn+CQMcAgQI8ez49vv36\/z+8\/72CPz6DPABDQcTDP78DwoAAPbu+f\/v5e7w8w32FRD+FPwK\/wv6Fw378foF4+zs9APsB\/YH+AP8Fwv9IAgMDQMG+Ab37fHl5vH\/7\/8S+gv4FP4S\/h\/4EP8B9\/34A+X79e3wAv3wCPz\/EgkTBA8T\/w4DCQAF6\/n+9uvt\/Az6CAcJF\/QMBgEV\/g32CwP0AwDk9ezw\/wv8\/v0PDRISBxYKEwYDBe4L9Pv06eUDAf\/++\/oV\/RUEChsGFPUR+AD\/7u7q\/frl+\/n+Avb\/Eg4FEwgaAgX3EALw\/PX77QP0\/\/wD\/Ar4AgoADBMcCgMABPQE9vDz7+gC+\/4A+\/sRAhcAABMFDAYI\/An9+vr1+vP88\/327gQI\/RMIBAoGAw0O\/AcHAv7x+gLpAfH2\/wID9wf3DBMb+xMDCfsA++31+P\/o8wX39QsCAQsJCgobDQMIE\/EG\/\/7tAfPq9AD69QQL9\/8JAxIQDBIF9hTw9Pr59\/D+7O3\/+vMFBAH\/CAEZ\/hAIBwz6+fwFAPX\/8vL28gzuBQkNAxMPCxAM\/wkJ8\/L28fMB5\/7wBvoEAwEE\/wER\/g0XEPsD+wL7B\/EA++zx8vP\/\/gYABxUHAxETAQgD\/PsC8QTw8Prx7PcFAvgCDQgKBBQR+Q79Bvv49PT87\/YC9P\/1BP8KAv4PBwQKEwEH\/\/8F9fj++fz06\/b6\/\/8IAAYOD\/8RAgn9EQMA+vn89vT37\/76+\/oDBQwPEAsCBgsM+wsC8frzAury+vz89wr5AwwDARH\/BA0E\/gD59gn78vTt+\/f8+Pv6BBEEFQMNFQsLAPkL7wED7Pb58P70AQEABv8SCgQZCAsKB\/UK9gLzA+\/\/9+\/29AX7Cv78ABYNBxIKEAP+BvwB\/PDx7QDtBPwADQMDCA4HEAEFDAX7\/QP4\/\/jv+fH2Af0GBwIJCQQKEAMGBPgP\/PX++u8C6\/jt\/\/QE+wkFAgkODgb\/DfoF9wj0\/vf19vn0AvP+AQgCBgYSCA0KCv0HCP4B9vn79+\/5\/Pf5AgEE\/hIGAhIGBg8G+v35B\/j5+vfz+PcABfv\/BgcDDwwMCgQFAv8I9f327\/L96\/z5Afj+BQAMDg8LCwQKA\/oC8gb7+PL98\/r2Bfb8AQQNCQwNCAcH9\/z+APr9+vf09f7y+Qr9BQQJCw8CDwIGBgQD\/vn\/9\/r18\/P\/8gL6BAj\/DgMQEAEQ+Qj++QPx+\/r+7\/H7AvL+CQkFBAwOCgYEBwf3\/\/719vz39\/D79v75BgAKAAMJAwgPBfwDAPv++\/j\/+\/H8\/v0ABf0GBggJEwkHBAsBAgT6\/fr08\/3+8v\/8+P\/\/AgkJCAwMBQYD\/QD99gDv9PT1+\/v6\/Q34BwMLCgsEBAkDAv\/1A\/T7\/fL7AvT\/BAUDCAIKBgsQCAgCAf\/3Afv0+vjyAvz9BvsABQYHDQQLBAcA\/gID9\/v48vr19\/kD+wQBAgIIAggICgsCBP0B\/fr88\/nz9\/f7\/fwHBAYJBwkLCAMBAwP\/APj3\/Pj49\/T6\/QT+CAQICw0FCwb+AAX6+QIC9\/P4+vn\/\/gECAwMCDAgKCwMAA\/sB+fv9\/fD1\/PX2BP77\/ggDCQUKDQsGAAAC+wAA9\/b7+Pz0AfwD\/Qj7CwcGBQgCBf4BAAH8+Pj4+fn5+v4A\/gQGBwUKDQkHAQX\/\/fr\/+\/35+PT1\/Pv+\/wcBDQEHDAYM\/wP5B\/38Afn7\/fj1\/\/\/6CAL7BAcFCQoBBgUA\/P8A\/AD59\/j\/+fP+\/gIIAAcDABIEAgcAAwD9\/fv5\/\/fz\/v75\/QL8CQUIAwUJCQQGAwIE+vr79\/jz\/P39Af8EBQMGBA4IBgcFB\/r9\/vX8APP9+fb+Av8ABAMBAwcOBwMEBfoABPz6+vn69\/38\/QED\/wsGBAkFCAX\/\/wH9\/vr9+PX6\/P34\/QICAP8FCgcMBgT\/Cfv7A\/j7\/fb\/9fYAAP0BAAYGBwQICQUEAQIB\/fv8+Pf69\/v5\/f8CAQQCAQIFCgsDBAD8AgP89v\/29\/74+v7\/\/wYBAQcHBgkFCQb\/BP7++vn6+Pz6APsBAAMHBAMFCAQIA\/4BAP\/6+fj++\/j2\/P0CAgMCAwgIBwcDBQQC+gIA\/fz9+Pf7\/P4AAgT+\/goFBwgHAQL+Bvz9+QD39\/P3\/AD7AQME\/wsDBgkFBgMC+wH9+f37+vv3+v7\/\/wL9AQAHCQUFCAQA\/gH+Av789vr69\/0C\/gAEAv8DCAkHBf4BBwX+\/v38+Pz6+\/79AwIBAwQCBQgIBAQF\/\/3\/+wD++P329\/v7AAH+AgEGBwkHAwUDBAT+\/\/kA+Pj7\/Pr\/+\/wFBQMEBwcHBgYC\/gMAAf39+fv8+Pz7AgH8\/wQCBAYICAMBAAEBA\/v3+\/\/39vz6\/AH+AAMAAwQHCQkBAgH\/\/P39\/f36+P39Af8CAQQEAwQCBwcG\/gEDA\/34+\/359\/n+\/QACAQMCAwcEBAkGAAACAAT9+\/399\/n7\/wD9AgH7BAYGBQYBAwQDAP7+\/\/n7+\/f6\/\/v8AAIDAgQGBQcGAAEEAf79\/fz8+vr9\/fwB\/v0CAwMDBwYEBQUCA\/8A\/vz5+Pz5\/P8BAP0CBAQGAwUIBQQCAQP7\/v37+vr6+f3+Av4CBAMGBwIIBgADAQIC\/Pv8\/fv6+\/z+AP8BAgADBwQJBQQEAP8B\/fz++vn8+\/z+\/\/\/\/AgIAAgcHBwMCAgQA\/f79+Pv3+f39\/wH\/AgICBAYFBQEEBP\/\/Av78+vv5+fn+AQAAAP8CCAUGBwUDAwEB\/\/8A+\/r++\/76+gABAAIEAwIICAYCAQP\/\/AP8+f77+vr8\/QD\/AAQDBQcEBgYDAgIC\/gD7\/vv7\/Pn7\/v4C\/wMCAgQDBQMFAAcIBQgLBf0NAwP4\/v8C8gYG\/gL87\/bu++v06\/n8\/fcGAQf1BAEF\/xMLAv8RAQwMGQEJCw8FCf8QCAoJD\/P89P0CAPoD+fb3+eoA9\/Hr9vb\/9Ab8BwQDDAcGChMPBggVFAwGDQ4XBxIF\/PkJAgb9BwH+5\/sC\/fXn5e729OXv9QHv9P38\/wYBCgL5BAwIBQsPEgQKEQwICQQEBwUC+f4A+P\/4+vb6+PX+7\/nz\/Pf09wHy9fcF+vUCChAN\/AUUEQcICw8GFRILBxUJB\/0I\/gT+9\/r8+\/\/79fv+7u\/18+vo9f3y+QT59vsGCgT8BvgREAv6ERAGDQ4QEAgLCAAD\/vr+Bfv7\/\/wE+\/T39\/3u9O739f\/7+v8H9v0DCAUGEQkFCAcLBhYWAxMQChQBDAsADgQE+fn0BQP7+QH09e3y9vft+Pb6+foA7\/kD9wAKBgH+DAoEBwULDQ0QDQwOD\/wIAggA+PgC+f74\/vX69QP4+e7+7fHvAAUA\/QgDBPoMDAH4Bg8OBBAKAw4SEgQOAwMCBwID+wf8CP3wA\/Tp9fn98fny7\/n5\/Pr38wT8AgAE\/AP8EAoLCRUPAA4NDgoKBwQBDAcG8gP49gH8+vb+9fr07\/nx++79\/vn0AgH++gQCBAIHBAEGDQMDCRACFAYTBw4ICwMH+v4KBPn6+ffv+PL28ffy8fgD+QH2+PsF9wP\/BgUHDgULDAkEDgQICAkDDg0JDf\/6B\/4D9Pz39\/v6\/PTyAvP08e789PP5AwP\/Bgb5BQoEAwkNCAgGBxIHCQwTBgsJDv4B\/QH+\/fkH+\/cA9\/Py\/\/by7gDx8f35+P36+wMFAwUBBgcPAwz8DQ4LCwoHEQ0LAwgBBf0A\/\/n\/+Pv3AfT5+vf2\/PD97vT4+fn+\/wT5BvYABgQDCQUGBQoKAxQOBgwBCwADAwL4Cgj7+gMB+\/779vXy9Pnr9\/T5\/Pv6BPr3CP0CAQYCBAgIDAYIEwIPBwcEEQQJBAYCAwL5+gD3+fb5+fj3+fX09PTz+vj89\/8C\/QMCAfsJBAkHBwQIBRD\/DQgJAAcHAwMN+QT9\/wH+\/v\/0APf5+f\/r9PD68P39Afr7Af\/\/AwkEBwUECAQLCQQHEQcKAQsFCQAEBfr+\/vz99fv77v\/39vL38Pb49Pz8+\/r6BfwAAQcDAAQBCAgHCAUMDgoBDQgDBAQD\/QED\/fz2AfX59vn4\/fL39vXv9\/kA9f0F\/PwEAgX8CAcKBQwHBQUMCAsGEAYJ\/gMECfsEAP38\/\/339\/74+\/L59fj29Pj5Av32Af79BAQEAwoECQcHBAcLCgYKCwgHCQYGBvr\/\/\/38\/\/j++\/X79fT98\/X29vj9\/\/f+9AEAAfsFAAYKBQEJCAcMCgoPCgkABg0H+wX8Av79\/f39\/\/n7+fzx\/\/H18\/n4\/Pv5AAP7Af8HBv0ECwQICgcNBQwHBwkLBwUEAwAK\/v77A\/gC+fr5+Pr88P70+Pb59vz+\/P8E\/gT6AwMCCgoEBQYMBgsICwgJCAYFCAX5Av79BfgA+\/r6APb1+fP4+vT1\/vj4APr6AQQFAvkNAQUFCgULBwsHCwkICAcECAACAAEE+vkA9gD4+vf1+Pv58fj1+v78+QH8\/wH+AgL\/BgQGCA0BDQQLCgoGDQMGCgEBAfwD+v\/6BvP9+\/v6+PP+9Pj4+vX\/+vn7AAID\/AsCAQkEBQwFBAgPCAoMCwoACQIAAgQA+\/4A+\/76+f73+\/T2+Pb1+Pj6\/QD5AAEB\/gEFAv8GCQMCCQkGCAkICgkIA\/4JAAAF\/P4A\/fn8\/vX89vr2+ff48fz5\/vgA\/QT9A\/4B\/wkDBQYHBAsEBgoOCAYFCgMFAQH9B\/z9\/P\/7+vrz+\/n1\/vX29fr6\/Pb8\/v\/\/AAQFAwUEBQMJDf8HCQgIDAYMBgUAAwIC\/gEA+P78+vr5\/Pv39vny9vz1+QD\/\/fsA\/QQCB\/8GBQUHCQgGBgoBDgkKBAf+BfsFAwH9Bfr++\/\/3\/fT78\/r4+PH9+f0B\/PcAAgEDAQMEAwUIAQcICAgJBw8EAwQFAwcAAf\/8+wX7+v4B9vb4+\/j39fnyAvn5\/v\/9Af\/\/\/gcCBQEGBwYHCgcDDwkFCAEIAwECAQAC+\/3+\/\/37+vj8+\/f29Pn49vr7\/AABAfkF\/QUCAgYEBQoABggJBgsFBAgIAwUDBv\/+\/\/\/9\/\/33+vz5+Pn4+vX7+Pv5\/fr\/\/\/0ACAEC\/woCCAANAQkJDQEKCAQBBQAG\/wH\/\/AH9+f3+\/Pr\/9vr4\/Pb1+P\/3\/f79+gED\/wECAgQEAgQHCAYECwkFDAkBBgUEAv0DAvz+\/\/r6AvX5+fr6\/vP39f\/6\/Pr\/\/\/wEA\/sEBAICBgUFAggJAwkNBQYKAwUAAv8E\/gH9\/gD89\/35+fr89Pz0\/fr5\/AD5Av7\/\/wYABgEABQoFBQYIBwgFCgIFBQT9BQUC\/vz\/\/fwA\/Pf9+vn4+vb6+fj5+\/7\/+QIA\/wEB\/gEHAwUCAwYGBQkFBg4HBAUBAwMB\/wD+AAD5\/\/z4\/vr49\/z2+Pn5+\/r8\/\/z\/A\/wCBQQDBQEFBAcHCAMNBwgGBf4HBQT8Av7\/\/AD7\/f3\/9vz8+\/f+9Pr6+\/r7\/AP+\/AMA\/ggE\/wUHBAUEBggHCgYEBwn\/BgIA\/wP\/\/\/oB\/vv5\/\/r6\/fj1+\/r6+\/r6AP4B+\/4CAgEDBAAGBQUEBwkIAgcIBQcGAAAFAP8A\/f\/+\/fz6+\/z79v74+fz6+vz9\/\/r8AAEABQECAQoAAwYEAwgECAMKBQIBCAD\/Bf8AAAD\/+vr+\/Pv8+vj++\/X5+\/n+\/f37Af8A\/QQEAgEHAQUGBgIHBgsFBgQGAAn\/AAEA\/wH9\/P7+\/Pv3\/P73+\/z3+v77+\/z\/AP4AAQICBf8BBgQFBwMFCAYFBwMJA\/8DA\/0BAfv9AP3\/+P38+v379\/z7+fn++\/3+AP0AAwUAAAQFAAICAwMFAwMCBggJ\/woKBxIKABQMDgsIDQsCCwYECQb+BAH\/Av39+f\/6\/u3++fny\/\/D1+fvy+OwB7Prr+\/D88vLw++UB7vHr\/vf17AP5\/PYF9QUEAQAFBwYHAQgIBAkCAg8HCQoJBBALCwMLEwkFFQ4IDRgBDwsUCwwDDgMFAv4ACvv+Bfz+Af329wEF9ff9+vH8+PD4+PT28\/P69PTv7fj66\/X28\/P97fLzAPz59wb2\/f4D9QUAAvwJ\/hD\/CAALBhD6EAQMCREAEAoSCQwIFwYNCwgLDwsMDwcNCAoGBQUL\/gECCvwF+Pj9Avj9+\/z5+vIA7\/\/29e8B7\/z09fb19fvr8f3w9ffz7f368\/X7\/QL2APz9BQD8Agj9BwUDBgYKBQEMDwYJCwMMDA8DDwkPDAgIEgYWCQwOFgARBAYHCAAD+wkF+QEB9wAE9\/r\/+\/349\/35+\/j49ATx8\/b37\/jz9+399Pnm+PXz8\/vp\/vb6+P\/1CPcD+gECDvkDAwj7CAQGAQ8MBAILCQUNCwYPCwoLCQ0RCggLDBIPAwsRBAoIBgEJBgT7AQED+wD7+Af7+\/r5+f\/1+\/T5\/ffs+\/zw8ff19\/X67vPz9vPu+fH1APL5\/\/v9+vz8BP4H+v0JCPoLAwQED\/0M\/hQFBwESAxEGBgsSAxEFDwkRCQ8BDhMM\/REGCQYI\/AMDBv\/7\/AP6Av3z\/v369\/31\/fj38\/T5AO3z+vTz\/fD19fb58\/D69PH49fD7+\/X6APwB\/\/7\/\/wcF+QELAwUJAwINBwgIBhAKBQoIBwwIBA4RBRAHDAoRDwr\/EAwHAgv9CwMH+AT+CPr\/\/P\/5\/vP6\/v\/y\/\/T69v\/29\/AC9\/X1+fH7+e\/19vb28fP48\/358\/YI9vv+AP8D\/wT8BQgFBAgDBgkG\/ggJCQUIBg4FDAoFDREDCA0JCwsHCQgOEAEJCAYCCf79CAH\/\/\/wBAvv\/\/PgCAPD8\/fn49\/b59v\/18vj88\/n28\/MB8\/T0+fT68f34+f\/58QT+\/P8A+wMCBQD+BQgBAwf+CwsFBAkICgUJBQkKEf8IDg0DCwYMCw8GCgMJBgUDB\/4KA\/\/\/CP0B\/P77\/f\/\/8\/wD+fX7+fn8+PX2+fj38\/r39P719vz69PX19fj5\/PjzAgP4+gH\/AAT9\/wYCAwICCAYCCv8HDgn\/CgYKCAgLBgURCgEKCwkMCgYKDgkHBQMLAQYD\/gIJ\/Pz\/A\/wC\/AD3AP\/78AH6+\/j78QD5+vf49v3y+vX1+P3u9vn59Pj4+fj+\/fb4Bf38Af3\/BwL9AwQIBAIFBQMGBQAGDwQHCAcHCgkHAwsQBgUNBgkQBgYJBwgEAQYCAwL9+gcE+\/0A\/f7+\/Pn5\/\/\/5+vz3\/Pzz9fv5+fT3+fn2+PL1\/frv+vb4+fz1\/fsE\/f3+BfoG\/\/4ABAADAQcGBQMGAAkNAwAMCgYIBwUKCgsHCQsKBwoIAg0JAwMHBgcE\/Pn6\/vz9\/wD8\/AEF\/fkDBf8H\/v8LAwME\/wgGBAcCBA0F+AkHAAgDAQEGCgb5CAQCBwb2BgcD\/gD8Bfv\/+\/\/7BvL2\/gDzA\/f4+P\/7+O\/\/9\/f2++4E+\/T4+Pf69PX19\/f87fEE8vb59vQA9Prv+fz17wDw+v397\/v3\/PT78\/\/3+PT1+Qfw+\/74+QP1+vUAAPj2Afj9B\/f7AgIAAQACBQMEAPwRCv4GCQYLCQIGCgsICAgMBwwMAQQNCgULCgoOBwsFCxML\/hEKCg4KAg0NDQgLCA8GDgoACwsEBQ0JCwoJCQYKE\/4EDQwGDQEGCg4BCAgOAwcBB\/gIAvv7C\/oF+v4AAP0D8P4D+vv\/8vwB+\/L5+\/339fv09\/z37v399Pb49vj1\/vPx+wDz9\/X1+ADv9fn39vfz+\/T5\/vXvAfrz+Pj3+fj59vUB\/PX6+fYA+\/D4\/vn69\/r\/+gH+\/f4K\/fkDAv4EAQL+CAkF\/ggFBgkH+hAIBQIKBQ4DDQYICxP9BwoLBQoECggOCgUFCwcJDAUDFAoGCQcMCQcJCQgRCv8GEQMHCQYEDgYI\/gYJCQIM\/wkOBQULBAkDAgIBAwv69wUG+P3\/+\/8A+vn4\/Pz4+v31\/gL2+P77+Pb2+vv3\/PHyAvjw+\/f2+\/nx9\/f89fL4+vH\/+PX4\/vT38\/n5+\/b57\/sB9vD++Pv99vL7+\/v39vz79v\/39fz\/9\/b8+wL9\/fz9Awj1AQYAAQr5AwcJBQP\/DwEGBwMDCAUDBQsGDAkGBAoNCv0LDgYJCgALDwgHCAkNBgUMAgcKBQENCwkJBggKCA0DAQ4KBAkGAQ0LAQMICgkABQcDAwb\/AwwGAgQAAwMDAPz9CQD8APv7Bf30\/P7++\/b7\/vf7+vL7A\/X4+Pn6\/fX58v3++Pj98vr+9u76+\/zx9vn99Pn49P3\/8Pn39\/r78\/j4\/vz1+v73+\/3y9v769vj4APr4+\/v3BPv0\/QH9\/vr9AAMDAf4EBAAEBfwHCgL\/BAcJBAIIBwYOAf8MCQQHBAMMCggDBAsKAwkHAg4LBQUKCQsEBgcICgv\/Bg8IAgYCCgsJAgUECAUECAgDDggEBQsFBwAGBgYHBv0JCgD\/A\/8FBf39\/wAA+\/oC\/f0D\/PsC\/fv4+fv8+v329wD\/8Pb7+\/v88vn7+ff29wD2+\/v2+QH49ff5\/Pn2+fb5APjx+fr5\/fjz\/P75+PX8\/\/j6+\/n8APb2\/P3+\/fv7+v4A+PgB\/\/8A\/PsGAwH\/\/wUG\/QMBAgYH\/QMHBgYGBAcGCQb\/BQsFBAgCBw0GAwUIDAUBBwkHBwMCCQ0FCAgHCAoGBgALCwQCCQIKCgIEBwcKAgMHBwUF\/wIMCAEFBwYHBQICAgkG\/gED\/wQB+P0AAf76+wP9+\/v4\/QT79\/z7+\/\/4+Pr8APv2\/Pz4\/Pj0+fr5+Pb8\/vn3+Pb+\/\/T2\/fv6+vb4\/fz7+fn\/+vj89\/f8\/Pj4Bgj\/\/QIABAQCAAEFCwD9BwgGDPwEEQQGBwAOCAQLAwcTBfkOBwYMBQIIBg0H+wkHAQwF9wwLAQEA\/wf++wH9\/gjz9QT++QT4+QD8\/PjxAfn3+\/jvCPzx+vn5\/vP1+\/f4\/uz1BvL6+vf6AvP68vf+9u8B8fkC+e79+f349vUD9vn28\/0H8P3++f4D8fz3\/wH49gP5\/wn0+gQBAQP8BQgCBAL7Ewn7BwoFDwX+CAwICwcIDgYMDv0GDQkFCwUQDQYKBgkWCP4QCwgRBAERDAsKCAkQBA0J\/QwLAQYLBw8JBQoGCRL8AhAKBQ39BRAJAQkGDQUBAwT4CQH3\/wj6B\/j8AgD9AvD9Bvn6\/+\/\/BPb0\/Pr\/9\/D89fb+9e0A\/PX58\/T79fv18P0C8vj18\/0B6\/j6+Pr17\/\/1+f707wX39fn09vz2+Pf2Av7z\/Pn1A\/rt\/P\/6\/fX6Bfr\/APsADPv6Bf0ABP0CAgUMA\/wIBgMNAvsSCAQFBAcRAgkIBg0R+wgLBwYIAAsKCgwCAw4HCA0BBBYIBgkEDgwBCAoIEQj7CQ8CCAYBCQ0FCf0ECgcBDP0LDwQDCQILA\/wCAwAK+PcHBPj+\/PkE\/vf89vv89fr+9QEB9Pn++fz28f78+Pvw8gb28fz29wD18fn4+vbv+\/vyAPjz\/P30+fP2APv3+u\/8BPPz\/vj9APH0\/vr79fP\/\/fYA9fUA\/\/b7+v0H\/f3\/\/QYJ9AIH\/wUI9wYJBgUA\/hADBAgBAg0FAQgICA8GBgYIDgn7DA4FCgf+Dw4FBwUJEAUCDAIGDAICDggLCQQGDAUMAgAQCwEJAwIPCv4EBgoL\/gMKAgME\/QMMAgQB\/gUF\/wD6\/AoA+gD5\/Af59P7+\/\/zy+wD3+vjw\/wH0+ff4\/v3z+fT9Afn4\/fP7\/\/Hx\/Pr88\/L8\/\/T49fQC\/e\/69\/f9+fL7+f\/+9vv\/9v398PoA+vj49gP89vv5+gb79gAB\/QP5\/QQDBAH\/CAb\/AwT7CgkAAAQGDQMACAcHDv4BDQcECAEFDggHAwQOCgEJBgMQCgIGCAkNAQMIBwoJ\/QYQBQIFAAsNBAMEBAoFAAgGBA4HAgcIBgf9BAcFBgP6CAn9\/QD9BwT5\/f\/+Afn5Av3+Avv7Avv7+fb9\/vj79vgC\/vD3+PsA+fH7+\/j39PcB9\/v69\/wB9vb4+P778\/n3+gL38fr6+v\/19AD++fj0\/gH4+vv6Af\/0+P38Af74\/P3\/A\/j6Av8AAvv\/CAIA\/\/4IB\/0DAwMKBvwFCAYIBgMJBwgF\/QUKBAQHAQoNAgIFBw4F\/wcJBgoCAQsLBQgHBwoIBQUACwoCAwcCCwj\/AwgHCgEBCAcEBv4CDQYABQYJCAD\/AAEIBPoAAv4E\/fb9\/wD++PoE\/fr69\/4E+fj7+\/7\/9fj6\/AD79f38+fz29Pr6+fn1\/AD49\/j3AP\/y9\/37\/fn0+v78\/Pr5APr5\/Pb5\/fr4+goH\/AAEAQsGAQUGCRH+BQ4IEg77FhUIDwcCGgEEDfsJEfj5C\/kIA\/n6BPYI9PD++PEG7ewJ+e\/57Pv+7+796\/v\/4\/L\/6\/337vYB8P\/w8gj3+Ab1\/RP8+Qj\/Cg72CAwCDQv3ERQAEwkGGBADEAIOFQUFGP8UGwAHEwkTCP4LEvwJ\/fgQBvQJ+v4K\/u4E8QT+8fYC7gIA5Pf88\/zz6gP38fbt7Qfu6vvy9QTo8v38\/QP0BAb5CQLxCQECAQb7FwICCQEKGfwEEggNF\/oLFwkRDAcUEgMUBwAYCP8PAwgU\/gAJ\/wcK7gIM+gMA7AQH9Pr99gb27v7w8gHt6\/319ALk9fzy+vXk\/v3u+PbnCP7v\/P79CvX1CfkDCfj+EgAICPgGDQINAwAYDQIRAgcYDPsVCRATAAUaARUQAwcZ\/AwF+wgG+Qb7AA789gb2+wrv8AX6\/Pzq\/AXt\/fjt\/\/7q9vbs+\/Lp+PP0Aunu\/PP3A+r4C\/f+\/fQJCPcGAv4RBvQNAwIL\/v4QCAsO+wgVAg4P\/BMZBQ0LBBoM\/hMNChcA+xAH\/wv4\/A4AAALw\/wT3+wHuCv\/0+\/r0Bu\/t\/fj0AubzBPHy+ev3Au\/w9uv59uv++fMK+vUEAP0I9foQAQUF9gcV+QcL\/g8T+QoHCQ4F\/RYIBxcBBxcICg8BDRoHCwn8Ew\/2CAj9DATvBAP8AfL1Cfj5Auv4BvP1\/OwD\/vH68\/UE8egB8fED5vH98frz6v8A8vz+8QEG8wf\/AA\/9CAUACQ\/0AxEDBwf9CxAEDAj+GQ8BEAMOCggDEwoHFf4ODgMMAvkODQD\/BvUPAfsC+\/kJ9\/X9+vf47PUA9fb66\/oA9Pr26gb69vn39AH26\/z19gPr9AH2A\/nx\/Qv1BQP7BA\/9Df4IEQkHEAEKEP4AEgULCQEDFwARB\/8YEv4PDAkID\/wO\/wcQ9wcI\/f8G8voK+v3+7wUB9vv48gf97AH49Pny8f30+P3p+gHs+PTr+v\/19\/r0BALwBvv\/DPn4DgL\/A\/z9Ef0JBPkKEf4HCvwVDQcLCwYVBgMNCQcW+wYWBwQJ+wsLAf8I9QYH9gIE+wz7\/wAD9Qnu9wH4+fzs+gLv8vntAf3t+\/b19\/rs\/\/j0A+37Avj4\/fT5Cfv9AfUOCfUFAQINCfkMCAMH\/wARBAcM\/QwUAwMO\/hEPBwkKBRUN\/RAFAxD9\/g0DAAL4\/Az4\/gDyAgfw+\/\/5AAH3APf6A\/LvBPT3+\/LvBu749u\/4Aev59\/P8BOwC\/\/wE\/v4FAv8I9\/sQ\/v8J+gkO\/gAI\/w8M+wsKAwsI\/hAPARICChMHBw3\/DBAD\/gz5Cwr1BAL9CP\/1BQL5Afb0Cfr1\/vX8A\/Pz\/e4B\/\/L3\/PP\/+ez8+PMA8PAC+PX88fkI+PUG9AYF+P4H\/AwE\/wYM+xD+\/g4EAAz\/BREEAAz+DxH9BREFDg38DRADEQMEDQf\/CP36Dfv5A\/kBCAD99gAA+g3\/+gf9BxH1AgIABAHxBQf0+vruCvv0\/Ob4Aefj+ebx+\/Lu\/fIE\/vMD\/fwWA\/wdEAMRCwsZDAES+wsV8PkJ+v8B\/\/wF7wT06gbt6\/nu6wHz6fry+wju+QX7BhD4BxX\/EwoRGRcHFgoGGAP2EPUHD\/Hz+\/b7\/OvzAej18OX+\/Of79AAMAfIN+AcOAvgOAAsU+QUKBxMNARUSBQkG+RQH9Qf8AQrv8P\/28QHz7vjp9\/Lm\/\/P1+gT1FAf+CgIKGQMBFQwPGvsIFQILBggFC\/kI\/\/AQ9u4C+fwF8+r18PL+4u4A8f7+7gIL+AQICRcLAxEJCBsJARYPCBj3BAb\/AwTxAAjz\/f3qCf\/t+ff2AvDo\/+74CPbxCvv\/A\/UBA\/sNCPwZFQARDAwcEvwWAwcN+fgL9AEC++8G6\/rz6P3u6Pfz8AT+7QD8\/w\/4+RAGCA\/6Bhb+EA0MFBYBBwz+Ff\/1DAD+Dvbz\/Pr4BOjtA+v18eb4\/uv7+\/wMB\/EKAgYTAPwVDAgVAQYOBA8N+wsR+QYG9A0D8QT6\/Aj37AD97gLu6wDw8vrr\/fr1+wbyEgz5Cg8FFwkBFRINHf0EFQYCBwD+DPX3Au0H9uz9+PED+Of3+vEC7PII+AAD8\/wQ+AIIBRIR+wsNCxYLABgLBBb8+gkC\/QPz+gnw\/PzpAP\/o8\/fxAPrnAP72Cfb3DQEADfr+DQEFDP4TFQASDwITD\/cOBAAN+\/QLAPj+9PAG6\/f45vf15vP68gED7gYB\/w4B+hMLBhT+ARsHCQ8HDxP9Agv2CwPwAwP4CPfv\/gDwBevvA\/Hx\/Or4BfLyBvkKBvQHCP4TA\/0SEwQYBAYVCgQM\/AQP+AAG8AYF7Pn+9\/\/56gD76wLt7Ab59QD0\/Af6+Qv3Dg36Cw8BEw\/9ERYGFf8BEQb8Cfj6C\/nyBOwA\/ezy\/vD9+Of9\/+8G9vIJBPwE\/v0T\/QEOAAsU\/ggQCA8Q+g0NAAr\/9woC9wP48Qr19P3w+QDp7\/ry+gLq\/gT3CP72DAr+DAIBEgcCEwIMFwQFEAAIDPQEBfoG+\/EEAvEA9e4G9u\/57vYA7\/MF9AAJ8gII\/ggF+g4QAREHABkLBg8DBRP\/+gv6Agbw\/ATz\/\/3t\/QHs\/vHtAfrwAPX6Cfr1C\/cHDfj\/EAEMCf0OFP8WBwETDPwK\/\/4N\/PUH9P8E7vUA8vn96PcA7fz38AcB9AT++g4B+Q0ACBIAAxEFCxL9DBH\/DAT4Cwf4Af32CfrwBPP5Au\/vAPH1AO36BfQC\/PQIB\/kHBfsUCPwOBAoVAgQVBQUP+AMN+wIC9QMF8\/377wf47\/0="

{card:card1}
image:"%%IMG2AgABVgH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/AfgABAH\/AfQACgECAAcB\/wHoAAcBAgACAQIAAgEBAAIBAQACAQEAAgEBAAMB\/wHjAAIBAQACAQEAAgEBAAIBAQADAQEABAEBAAIBAQAEAQEAAwH\/Ad8AAgEBAAIBAQACAQEABAEBAAMBAQACAQEAAgEBAAIBAgAIAf8B2wAHAQEAAgEBAAIBAgACAQEAAgEBAAIBAQAHAQEAAwEBAAQB\/wHXAAMBAgACAQEAAgEBAAIBAQAEAQEABAEBAAQBAgAFAQEACAH\/AdQABgEBAAIBAQAEAQEAAgEBAAMBAgADAQIAEgH\/AdEAAwEBAAIBAQACAQEABAEBAAMBAQADAQEACgEBAAMBAQAMAf8BzgANAQEAAwEBAAEBAQAFAQEAAgEBAAIBAQAEAQEADwH\/AcwABgEBAAIBAQACAQEABAEBAAIBAQABAQEABQEBABkB\/wHKABABAQAEAQEAAQEBAAIBAQAdAf8ByAAKAQEABAEBAAYBAQABAQEABQEBAAIBAQAYAf8BxgARAQEABQEBAAEBAQAFAQEAGwH\/AcUADwEBAAoBAQAiAf8BwwAbAQEABgEBABwB\/wHCABsBAQAjAf8BwQAcAQEABwEBABwB\/wHAABwBAQAHAQEAHAH\/Ab8AHQEBACUB\/wG+AB0BAQAIAQEAHAH\/Ab0AHgEBAAgBAQAdAf8BvAAeAQEAJgH\/AbwAHgEBACcB\/wG6AB8BAQAJAQEAHQH\/AboAAQEBAB0BAQAJAQEAHQH\/AboAHwEBACcB\/wG6AB8BAQAKAQEAHAH\/AbkAHAEBAAMBAQAoAf8BuAABAQEAGgEBAAMBAQAoAf8BuAAgAQEADQEBABoB\/wG4AB0BAQACAQEACwEBABwB\/wG4AAMBAQAQAQEABgEBABABAQAcAf8BuAAbAQEAAgEBAA0BAQAGAQEAFQH\/AbkAEwEBAAYBAQAEAQEAFAEBAAYBAQAMAf8BuQAQAQEAAgEBAAcBAQACAQEADQEBAAkBAQARAf8BuAAUAQEAAwEBAAMBAQACAQEADQEBAAEBAQADAQEABQEBAAcBAQAIAf8BtwACAQEADAEBAAIBAQAHAQEABAEBAAYBAQAEAQEAAQEBAAMBAQADAQIABgEBAAwB\/wG3AAsBAQAIAQEABQEBAAQBAQAEAQEABAEBAAEBAQADAQEABAEBAAEBAQACAQEABQEBAAQBAQACAQEAAgH\/AbcACQEBAAYBAQACAQIAAwEBAAEBAQAEAQEABwEBAA4BAQACAQEADQEBAAIB\/wG3ABMBAQACAQEAAwEBAAQBAQADAQEAAQEBAAQBAQADAQIAAgEBAAcBAQACAQEAAgEBAAIBAQAGAf8BtwAKAQEAAwEBAAIBAQANAQEABQEBAAcBAwAHAQIAEgH\/AbYABgEBAAcBAQADAQEAAgEBAAIBAQAEAQEAAwEBAAUBAQABAQEAAgECAAEBAQADAQEABwECAAQBAQACAQEABgH\/AbYACQEBAAYBAQABAQEAAwEBAAMBAgABAQEABQECAAcBBAAIAQEABQEBAAcBAQADAf8BtwABAQEAAgEBAAIBAQAEAQEAAQEBAAcBAQACAQMAAQECAAUBAQADAQEABAEDAAIBAQAFAQEAAwEBAAMBAQAHAQEAAgH\/AbYAAQEBAAgBAQADAQEAAgEBAAMBAgACAQMAAQECAAEBAQADAQEAAgEBAAUBAQABAQEADAEBAAIBAQACAQEAAQEBAAYB\/wG2AAEBAQAGAQEABQEBAAIBAQADAQEAAwEDAAEBAgADAQEABAEBAAgBAQAIAQEAAwEBAAEBAQAGAQEABAH\/AbYAAQEBAAMBAQAFAQEABQEBAAQBAQACAQMABwEBAAEBAgAHAQEAAQEBAAgBAQADAQEAAQEBAAMBAQADAQEAAwH\/AbYAAQEBAAUBAQAFAQEAAwEBAAQBAQACAQEABgEBAAIBAQABAQEACgEBAAgBAQAFAQEABQEBAAEBAQABAQEAAQH\/AbYABAEBAAQBAQAHAQEABAEBAAIBAQADAQEABAEBAAEBAQALAQEACAEBAAQBAgACAQEABAEBAAMB\/wG2AAEBAQAIAQEAAgEBAAgBAQACAQEAAwEBAAQBAQANAQEADQECAAQBAQACAQEAAwH\/AbYAAQEBAAMBAQACAQEABAEBAAkBAQABAQEAAgEBAAQBAgAPAQgABQEBAAcBAQABAQEAAQH\/AbYAAQEBAAkBAQABAQEAAgEBAAYBAQABAQEAAgEBAAEBAQABAQEADwENAAQBAQADAQEAAwEBAAEB\/wG2AAEBAQACAQEAAgEBAAIBAgABAQEAAgEBAA4BAQAPAQMACAEFAAQBAQAIAf8BtwABAQEABwEBAAEBAQACAQEAAwEGABQBAQANAQMABgEBAAIBAQACAQEAAQH\/AbYAAQEBAAIBAQACAQEAAQEBAAEBAQADAQoAIwEBAAIBAQAFAQIAAgEBAAEB\/wG1AAIBAQAHAQEAAQEBAAIBBQAFAQMAIQEBAAIBAQABAQEAAgEBAAYB\/wG1AAIBAQADAQIABQEEAC8BAQAFAQEABQH\/AbUAAgEBAAgBBAAtAQEAAQEBAAEBAQACAQEAAgEBAAIBAQACAf8BtgABAQEAAgEBAAIBAQACAQMAFgEBAAwBAgAIAQEAAQEBAAIBAQAFAQIAAQEBAAEB\/wG3AAgBAQACAQEANQEBAAMBAQACAQEAAQH\/AbcAAgEBAAIBAQACAQEAAgEBABUBAQAcAQEABAEBAAIBAQACAf8BuAACAQEABgEBAAIBAQAIAQEAKAEBAAcBAQABAf8BugABAQEAAgEBAAMBAQABAQEAAQEBAAQBAQAQAQEAGgEBAAIBAQACAQEAAgH\/AbsAAQEBAAQBAQABAQEANAEBAAUBAQACAf8BuwABAQIAHQEBABwBAQADAQEAAQECAAEB\/wG7AAIBAQACAQEAAwEBABgBAQAcAQEABAEBAAEB\/wG8AAIBAQADAQEAAQEBADoBAQABAf8BvAACAQEABQEBABcBAQABAQEAGAEBAAUBAQABAf8BvgABAQEAAQEBADcBAQACAQEAAgEBAAIB\/wG9AAEBAQABAQEAAgEBAAIBAQAzAQEAAgEBAAEBAQABAf8BvgADAQEABAEBADABAQABAQEABQEBAAEB\/wG+AAEBAQABAQEAAgEBAAIBAQAvAQEABAEBAAEB\/wHCAAMBAQAEAQEALgEBAAEBAQAEAQIAAQH\/AcAAAQEBAAEBAgABAQEAAgEBAC0BAgADAQEAAQH\/AcMAAQECAAQBAQAvAQIAAgEBAAEBAgABAf8BwgABAQEAAgEBAAIBAQABAQEAKwECAAIBAQABAf8BxgABAQIAAgEBAAIBAQABAQEAFgEBAAEBAgABAQEADQEDAAMB\/wHIAAEBAgAGAQEADwEEABUBBAABAf8BygABAQMAAQEBAAMBAgAmAQQAAQH\/AcwAAQEDAAEBAQACAQQAIwH\/AdMAAQEDAAEBAQACAQQAIQH\/AdsAAgEFAB0B\/wHdAAIBBgAbAf8B6AAXAQIAAQH\/AekAEwECAAMB\/wHsAA8BAgAEAf8B6QACAQMACwECAAMBAQACAf8B6QAEAQMACAECAAYB\/wHqAAcBAwADAQIABAEBAAQB\/wHpAAMBAQAGAQMABAEBAAMBAQACAf8B6QAFAQEAAgEBAAUBAQAIAf8B6AABAQEAAgEBAA0BAQACAQEABgH\/AeUAAQEBAAQBAQACAQEAAgEBAAIBAQAHAQEAAgEBAAEB\/wHlAAMBAQAGAQEABgEBAAIBAQADAQEAAwH\/AeUABwEBAAUBAQAEAQEAAwEBAAUB\/wHlAA8BAQAMAf8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wH\/Af8B\/wHj"
script:"card1.0"
{widgets}
field1:{"type":"field","size":[99,17],"pos":[203,174],"locked":1,"font":"menu","show":"invert","value":"@theredbird__"}

{script:card1.0}
on view do
 
end
{end}

</script>
<a id='target' style='display:none'></a>
<img id='loader' style='display:none'/>
<input id='source' type='file' style='display:none'>
<canvas id='render' style='display:none'></canvas>
<canvas id='lrender' style='display:none'></canvas>
<canvas id='rrender' style='display:none'></canvas>
<canvas width='0' height='0' id='ltools'></canvas>
<canvas id='display'></canvas>
<canvas width='0' height='0' id='rtools'></canvas>
<style>
html,body{height:100%;}
body{background:black;margin:0;display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;user-select:none;}
body{touch-action:none;overflow:hidden;}
</style>
<script>
VERSION="1.8"
// lil: Learning in Layers

let allocs=0,calldepth=0
lmn  =x      =>(allocs++,{t:'num',v:isFinite(x)?+x:0}),   lin  =x=>x&&x.t=='num'
lms  =x      =>(allocs++,{t:'str',v:''+x }),              lis  =x=>x&&x.t=='str'
lml  =x      =>(allocs++,{t:'lst',v:x    }),              lil  =x=>x&&x.t=='lst'
lmd  =(k,v)  =>(allocs++,{t:'dic',k:k||[],v:v||[]}),      lid  =x=>x&&x.t=='dic'
lmt  =x      =>(allocs++,{t:'tab',v:x    }),              lit  =x=>x&&x.t=='tab'
lmi  =(f,n,x)=>(allocs++,{t:'int',f,n}),                  lii  =x=>x&&x.t=='int'
lmon =(n,a,b)=>(allocs++,{t:'on' ,n:n,a:a,b:b,c:null}),   lion =x=>x&&x.t=='on'
lmnat=f      =>(allocs++,{t:'nat',f:f}),                  linat=x=>x&&x.t=='nat'
lmblk=_      =>(allocs++,{t:'blk',b:[],locals:[]}),       liblk=x=>x&&x.t=='blk'
lmenv=p      =>{allocs++;const r={t:'env',v:{},p:p};r.local=(n,x)=>env_local(r,lms(n),x);return r}

NONE=lmn(0), ONE=lmn(1), seed=0x12345, max=Math.max, min=Math.min, abs=Math.abs
ISODATE=lms('%04i-%02i-%02iT%02i:%02i:%02iZ%n%m'), PARTS=['year','month','day','hour','minute','second'].map(lms)
clchar=x=>{const c=x.charCodeAt(0);return (c>=32&&c<=126)||(x=='\n')?x:' '}
clchars=x=>x.replace(/\r/g,'').replace(/[^ -~\n]/g,' ')
wnum=y=>{
 let w='',d='',s=y<0?(y=-y,'-'):'',i=Math.floor(y);while(i>0){w=(0|i%10)+w,i=i/10}
 y=Math.round((y-Math.floor(y))*1000000);for(let z=0;z<6;z++){d=(0|y%10)+d,y=y/10}
 return s+('0'+w).replace(/^(0+)(?=[^0])/,'')+('.'+d).replace(/(\.?0+)$/,'')
}
mod=(x,y)=>x-y*Math.floor(x/y)
range=x=>Array.from(Array(x)).map((_,i)=>i)
torect=t=>{const n=Object.values(t).reduce((x,y)=>max(x,lil(y)?count(y):1),0);Object.keys(t).map(k=>t[k]=take(n,lil(t[k])?t[k].v:[t[k]]))}
count=x=>lin(x)?1: lis(x)||lil(x)||lid(x)?x.v.length: lit(x)?(Object.values(x.v)[0]||[]).length:0
rows=x=>{const t=lt(x);return lml(range(count(t)).map(i=>lmd(Object.keys(t.v).map(lms),Object.values(t.v).map(x=>x[i]))))}
coltab=x=>{
 const n=lmn(x.v.reduce((x,y)=>max(x,lil(y)?count(y):1),0))
 return lmt(x.k.reduce((r,k,i)=>(r[ls(k)]=dyad.take(n,lil(x.v[i])?x.v[i]:lml([x.v[i]])).v,r),{}))
}
rowtab=x=>{
 const ok=[],t={};x.v.map(r=>r.k.map(k=>{if(!t[ls(k)])ok.push(k);t[ls(k)]=[]}))
 x.v.map(x=>Object.keys(t).map((k,i)=>t[k].push(dget(x,ok[i])||NONE)));return lmt(t)
}
listab=x=>{
 const m=x.v.reduce((r,x)=>max(r,count(x)),0);const t={};for(let z=0;z<m;z++)t['c'+z]=[]
 x.v.map(row=>{for(let z=0;z<m;z++)t['c'+z].push(z>=count(row)?NONE:row.v[z])});return lmt(t)
}
zip=(x,y,f)=>{const n=count(x),o=count(y)<n?take(n,y.v):y.v;return x.v.map((x,i)=>f(x,o[i%n]))}
match=(x,y)=>x==y?1: (x.t!=y.t)||(count(x)!=count(y))?0: (lin(x)||lis(y))?x.v==y.v:
          lil(x)?x.v.every((z,i)=>dyad['~'](z,y.v[i]).v): lit(x)?dyad['~'](rows(x),rows(y)).v:
          lid(x)?x.v.every((z,i)=>dyad['~'](z,y.v[i]).v&&dyad['~'](x.k[i],y.k[i]).v):0
splicet=(f,x,t)=>{const r={};Object.keys(t.v).map(k=>r[k]=f(x,t.v[k]));return lmt(r)}
splice=(f,x,y)=>lis(y)?lms(f(x,ll(y)).map(ls).join('')): lid(y)?lmd(f(x,y.k),f(x,y.v)): lit(y)?splicet(f,x,y): lml(f(x,ll(y)))
ina=(x,y)=>lmn(lis(y)?y.v.indexOf(ls(x))>=0: lil(y)?y.v.some(y=>match(x,y)): lid(y)?dget(y,x)!=undefined: lit(y)?ls(x)in y.v: x==y)
filter=(i,x,y)=>{
 x=lis(x)?monad.list(x):lml(ll(x))
 if(lid(y)){const r=lmd();y.k.forEach((k,v)=>i==lb(ina(k,x))&&dset(r,k,y.v[v]));return r}
 if(!lit(y))return lml(ll(y).filter(z=>i==lb(ina(z,x)))); const n=x.v.every(lin),nx=x.v.map(ln),ix=y.v[Object.keys(y.v)[0]]||[]
 if(n&&i){const r=dyad.take(NONE,y);nx.forEach(i=>{Object.keys(y.v).map(c=>{const v=y.v[c][i];if(v)r.v[c].push(v)})});return r}
 if(n&&!i){const r=dyad.take(NONE,y);ix.forEach((_,i)=>{if(nx.indexOf(i)<0)Object.keys(y.v).map(c=>r.v[c].push(y.v[c][i]))});return r}
 return lmt(Object.keys(y.v).reduce((t,k)=>(i==lb(ina(lms(k),x))&&(t[k]=y.v[k]),t),{}))
}
take=(x,y)=>{const n=y.length, s=x<0?mod(x,n):0; return range(abs(x)).map(z=>y[mod(z+s,n)])}
dkix=(dict,key)=>dict.k.findIndex(x=>match(key,x)), dget=(dict,key)=>dict.v[dkix(dict,key)]
dvix=(dict,val)=>dict.v.findIndex(x=>match(val,x)), dkey=(dict,val)=>dict.k[dvix(dict,val)]
dset=(d,k,v)=>{const i=d.k.findIndex(x=>match(x,k));if(i<0){d.k.push(k),d.v.push(v)}else{d.v[i]=v};return d}
union=(x,y)=>{const r=lmd(x.k.slice(0),x.v.slice(0));y.k.forEach(k=>dset(r,k,dget(y,k)));return r}
amend=(x,i,y)=>{
 if(lii(x))return x.f(x,i,y)
 if(!lis(x)&&!lil(x)&&!lid(x))return amend(lml([]),i,y)
 if((lil(x)||lis(x))&&(!lin(i)||(ln(i)<0||ln(i)>count(x))))return amend(ld(x),i,y)
 if(lil(x)){const r=lml(x.v.slice(0));r.v[ln(i)|0]=y;return r}
 return lid(x)?dset(lmd(x.k.slice(0),x.v.slice(0)),i,y): lis(x)?lms(ls(x).slice(0,ln(i))+ls(y)+ls(x).slice(1+ln(i))): lml([])
}
l_at=(x,y)=>{
 if(lii(x))return lis(y)&&y.v=='type'?lms(x.n): x.f(x,y)
 if(lit(x)&&lin(y))x=monad.rows(x); if((lis(x)||lil(x))&&!lin(y))x=ld(x)
 return lis(x)?lms(x.v[ln(y)|0]||''): lil(x)?x.v[ln(y)|0]||NONE: lid(x)?dget(x,y)||NONE: lit(x)&&lis(y)?lml(x.v[ls(y)]): NONE
}
amendv=(x,i,y,n,tla)=>{
 if(lii(x))tla.v=0;const f=monad.first(i[n]||NONE)
 return (!tla.v&&n+1 <i.length)?amendv(l_at(x,f),i,y,n+1,tla):
        (n+1<i.length)?amend(x,f,amendv(l_at(x,f),i,y,n+1,tla)): (n+1==i.length)?amend(x,f,y): y
}
lb=x=>lin(x)?x.v!=0: lis(x)?x.v!='': lil(x)||lid(x)?x.v.length: true
ln=x=>lin(x)?x.v: lis(x)?(isFinite(x.v)?+x.v:0): lil(x)||lid(x)?ln(x.v[0]): 0
ls=x=>lin(x)?wnum(x.v): lis(x)?x.v: lil(x)?x.v.map(ls).join(''): ''
ll=x=>lis(x)?x.v.split('').map(lms): lil(x)||lid(x)?x.v: lit(x)?rows(x).v: [x]
ld=x=>lid(x)?x:lit(x)?monad.cols(x):lil(x)||lis(x)?lmd(range(count(x)).map(lmn),lis(x)?ll(x):x.v):lmd()
lt=x=>lit(x)?x: lid(x)||lil(x)?lmt((lid(x)?['key','value']:['value']).reduce((t,k,i)=>(t[k]=x[k[0]],t),{})): lmt({value:ll(x)})
vm=f=>{const r=x=>lil(x)?lml(x.v.map(r)):f(x);return r}
vd=f=>{const r=(x,y)=>lil(x)&lil(y)?lml(zip(x,y,r)): lil(x)&!lil(y)? lml(x.v.map(x=>r(x,y))): !lil(x)&lil(y)?lml(y.v.map(y=>r(x,y))): f(x,y);return r}
vmnl=f=>{const r=x=>lil(x)?(ll(x).some(x=>!lin(x))?lml(x.v.map(r)):f(x)):f(x);return r}
tflip=x=>{
 const c=Object.keys(x.v), kk=c.indexOf('key')>-1?'key':c[0], k=(x.v[kk]||[]).map(ls), cc=c.filter(k=>k!=kk)
 return lmt(k.reduce((r,k,i)=>{r[k]=cc.map(c=>x.v[c][i]);return r},{key:cc.map(lms)}))
}
tcat=(x,y)=>{
 const r={}
 Object.keys(x.v).map(k=>r[k]=x.v[k].concat(y.v[k]?[]:range(count(y)).map(x=>NONE)))
 Object.keys(y.v).map(k=>r[k]=(x.v[k]?x.v[k]:range(count(x)).map(x=>NONE)).concat(y.v[k]))
 return lmt(r)
}
fjson=x=>lin(x)?wnum(x.v): lil(x)?`[${x.v.map(fjson).join(',')}]`:
         lis(x)?`"${x.v.split('').map(x=>({'\n':'\\n','\\':'\\\\','"':'\\"','/':'\\/'})[x]||x).join('')}"`:
         lid(x)?`{${x.k.map((k,i)=>`${fjson(lms(ls(k)))}:${fjson(x.v[i])}`).join(',')}}`:'null'
pjson=(y,h,n)=>{
 const si=h, hn=_=>m&&y[h]&&(n?h-si<n:1), hnn=x=>m&&h+x<=y.length&&(n?h+x-si<n:1)
 const jd=_=>{while(hn()&&/[0-9]/.test(y[h]))h++}, jm=x=>hn()&&y[h]==x?(h++,1):0, iw=_=>/[ \n]/.test(y[h]), ws=_=>{while(hn()&&iw())h++}
 const esc=e=>e=='n'?'\n': /[\\/"]/.test(e)?e: e=='u'&&hnn(4)?String.fromCharCode(parseInt(y.slice(h,h+=4),16)):' '
 let f=1, m=1, rec=_=>{
  const t={null:NONE,false:NONE,true:ONE};for(let k in t)if(hnn(k.length)&&y.slice(h,h+k.length)==k)return h+=k.length,t[k]
  if(jm('[')){const r=lml([]);while(f&&hn()){ws();if(jm(']'))break;r.v.push(rec()),ws(),jm(',')}return r}
  if(jm('{')){const r=lmd();while(f&&hn()){ws();if(jm('}'))break;const k=rec();ws(),jm(':');if(f)dset(r,k,rec());ws(),jm(',')}return r}
  if(jm('"')){let r='';while(f&&hn()&&!jm('"'))r+=hnn(2)&&jm('\\')?esc(y[h++]):y[h++];return lms(r)}
  const ns=h;jm('-'),jd(),jm('.'),jd();if(jm('e')||jm('E')){jm('-')||jm('+');jd();}return h<=ns?(f=0,NONE): lmn(+y.slice(ns,h))
 }, r=rec();return {value:r,index:h}
}
monad={
 '-':    vm(x=>lmn(-ln(x))),
 '!':    vm(x=>lb(x)?NONE:ONE),
 floor:  vm(x=>lmn(Math.floor(ln(x)))),
 cos:    vm(x=>lmn(Math.cos(ln(x)))),
 sin:    vm(x=>lmn(Math.sin(ln(x)))),
 tan:    vm(x=>lmn(Math.tan(ln(x)))),
 exp:    vm(x=>lmn(Math.exp(ln(x)))),
 ln:     vm(x=>lmn(Math.log(ln(x)))),
 sqrt:   vm(x=>lmn(Math.sqrt(ln(x)))),
 unit:   vm(x=>{const n=ln(x);return lml([lmn(Math.cos(n)),lmn(Math.sin(n))])}),
 mag:    vmnl(x=>lmn(Math.sqrt(ll(x).reduce((x,y)=>x+Math.pow(ln(y),2),0)))),
 heading:vmnl(x=>{const a=getpair(x);return lmn(Math.atan2(a.y,a.x))}),
 sum:    x=>ll(x).reduce(dyad['+'],NONE),
 raze:   x=>ll(x).slice(1).reduce(dyad[','],monad.first(x)),
 max:    x=>ll(x).slice(1).reduce(dyad['|'],monad.first(x)),
 min:    x=>ll(x).slice(1).reduce(dyad['&'],monad.first(x)),
 count:  x=>lmn(count(x)),
 first:  x=>lit(x)?monad.first(rows(x)): count(x)?ll(x)[0]: NONE,
 last:   x=>lit(x)?monad.last (rows(x)): count(x)?ll(x)[count(x)-1]: NONE,
 range:  x=>lml(lin(x)?range(max(0,0|ln(x))).map(lmn): ld(x).k),
 list:   x=>lml([x]),
 typeof: x=>lms(({num:"number",str:"string",lst:"list",dic:"dict",tab:"table",on:"function",nat:"function"})[x.t]||x.n||"interface"),
 flip:   x=>lit(x)?tflip(x):lml(range(ll(x).reduce((w,z)=>max(w,lil(z)?count(z):1),0)).map(i=>lml(ll(x).map(c=> !lil(c)?c: i<count(c)?c.v[i]: NONE)))),
 rows:   x=>rows(x),
 cols:   x=>{const t=lt(x).v;return lmd(Object.keys(t).map(lms),Object.values(t).map(lml))},
 table:  x=>lid(x)?coltab(x): lil(x)&&x.v.every(lid)?rowtab(x): lil(x)&&x.v.every(lil)?listab(x): lt(x),
 '@tab': t=>{t=lt(t);const r=lmt({});Object.keys(t.v).map(k=>r.v[k]=t.v[k].slice(0)),r.v.index=range(count(r)).map(lmn);return r},
}
dyad={
 '+':  vd((x,y)=>lmn(ln(x)+ln(y))),
 '-':  vd((x,y)=>lmn(ln(x)-ln(y))),
 '*':  vd((x,y)=>lmn(ln(x)*ln(y))),
 '/':  vd((x,y)=>lmn(ln(x)/ln(y))),
 '%':  vd((x,y)=>lmn(mod(ln(y),ln(x)))),
 '^':  vd((x,y)=>lmn(Math.pow(ln(x),ln(y)))),
 '<':  vd((x,y)=>lmn(lin(x)&&lin(y)?ln(x)< ln(y): ls(x)< ls(y))),
 '>':  vd((x,y)=>lmn(lin(x)&&lin(y)?ln(x)> ln(y): ls(x)> ls(y))),
 '=':  vd((x,y)=>lmn(lii(x)||lii(y)?x==y: lin(x)&&lin(y)?ln(x)==ln(y): ls(x)==ls(y))),
 '&':  vd((x,y)=>lin(x)||lin(y)?lmn(min(ln(x),ln(y))): lms(ls(x)<ls(y)?ls(x):ls(y))),
 '|':  vd((x,y)=>lin(x)||lin(y)?lmn(max(ln(x),ln(y))): lms(ls(x)>ls(y)?ls(x):ls(y))),
 split:(x,y)=>lml(ls(y).split(ls(x)).map(lms)),
 fuse: (x,y)=>lms(ll(y).map(ls).join(ls(x))),
 dict: (x,y)=>(y=ll(y),ll(x).reduce((d,x,i)=>dset(d,x,y[i]||NONE),lmd())),
 take: (x,y)=>lin(x)?splice(take,ln(x),y):filter(1,x,y),
 drop: (x,y)=>lin(x)?splice((x,y)=>x<0?y.slice(0,x):y.slice(x),ln(x),y):filter(0,x,y),
 limit:(x,y)=>count(y)>ln(x)?dyad.take(lmn(ln(x)),y):y,
 in:   (x,y)=>lil(x)?lml(x.v.map(x=>dyad.in(x,y))):ina(x,y),
 ',':  (x,y)=>lit(x)&&lit(y)?tcat(x,y): lid(x)?union(x,ld(y)):
                 lis(x)?dyad[','](lml([x]),y): lis(y)?dyad[','](x,lml([y])): lml(ll(x).concat(ll(y))),
 '~':  (x,y)=>match(x,y)?ONE:NONE,
 unless:(x,y)=>lin(y)&&ln(y)==0?x:y,
 join: (x,y)=>{ // natural join on tables.
  const f=x=>lin(x)?monad.range(x):lml(ll(x));if(!lit(x)||!lit(y))return lml(zip(f(x),f(y),dyad[',']))
  const a=x,b=y,ak=Object.keys(a.v),bk=Object.keys(b.v), ik=bk.filter(x=>ak.indexOf(x)>=0),dk=bk.filter(x=>ak.indexOf(x)<0)
  const r=lmt({}); ak.forEach(k=>r.v[k]=[]), dk.forEach(k=>r.v[k]=[])
  for(let ai=0;ai<count(a);ai++)for(let bi=0;bi<count(b);bi++)
   if(ik.every(k=>match(a.v[k][ai],b.v[k][bi])))ak.forEach(k=>r.v[k].push(a.v[k][ai])),dk.forEach(k=>r.v[k].push(b.v[k][bi]))
  return r
 },
 cross: (x,y)=>{ // cartesian join; force columns to be unique:
  const f=x=>lt(lin(x)?monad.range(x):lml(ll(x)));if(!lit(x)||!lit(y))return lml(rows(dyad.cross(f(x),f(y))).v.map(x=>lml(x.v)))
  const a=lt(x),b=lt(y), ak=Object.keys(a.v),bk=Object.keys(b.v), uk=bk.map(x=>ak.indexOf(x)>=0?x+'_':x)
  const r=lmt({}); ak.forEach(k=>r.v[k]=[]), uk.forEach(k=>r.v[k]=[])
  for(let bi=0;bi<count(b);bi++)for(let ai=0;ai<count(a);ai++)ak.forEach(k=>r.v[k].push(a.v[k][ai])),bk.forEach((k,i)=>r.v[uk[i]].push(b.v[k][bi]))
  return r
 },
 parse: (x,y)=>{
  if(lil(y))return lml(y.v.map(y=>dyad.parse(x,y)))
  x=ls(x),y=ls(y);let r=[],f=0,h=0,m=1;while(x[f]){
   if(x[f]!='%'){if(m&&x[f]==y[h]){h++}else{m=0}f++;continue}f++
   let n=0,d=0,v=null,si=h,sk=x[f]=='*'&&(f++,1),lf=x[f]=='-'&&(f++,1);if(x[f]=='0')f++
   const hn=_=>m&&y[h]&&(n?h-si<n:1), id=x=>/[0-9]/.test(x), ix=_=>/[0-9a-fA-F]/.test(y[h]), iw=_=>/[ \n]/.test(y[h])
   while(id(x[f]))n=n*10+(+x[f++]);x[f]=='.'&&f++
   while(id(x[f]))d=d*10+(+x[f++]);if(!x[f])break;const t=x[f++]
   if('%mnzsluaro'.indexOf(t)<0)while(hn()&&iw())h++
   if(t=='%'){if(m&&t==y[h]){h++}else{m=0}}
   else if(t=='m'){v=m?ONE:NONE}
   else if(t=='n'){v=lmn(h)}
   else if(t=='z'){v=m&&h==y.length?ONE:NONE}
   else if(t=='s'||t=='l'||t=='u'){v=lms('');while(hn()&&(n?1:y[h]!=x[f]))v.v+=y[h++];if(t=='l')v.v=v.v.toLowerCase();if(t=='u')v.v=v.v.toUpperCase()}
   else if(t=='a'){v=lml([]);while(hn()&&(n?1:y[h]!=x[f]))v.v.push(lmn(y[h++].charCodeAt(0)))}
   else if(t=='b'){v=/[tTyYx1]/.test(y[h])?ONE:NONE;while(hn()&&(n?1:y[h]!=x[f]))h++}
   else if(t=='i'){v=lmn(0);const s=(y[h]=='-')?(h++,-1):1;m&=id(y[h]);while(hn()&&id(y[h]))v.v=v.v*10+(+y[h++]);v.v*=s}
   else if(t=='h'||t=='H'){v=lmn(0),                       m&=ix();    while(hn()&&ix())v.v=v.v*16+parseInt(y[h++],16)}
   else if(t=='j'){if(m){const j=pjson(y,h,n);h=j.index,v=j.value}else{v=NONE}}
   else if(t=='f'||t=='c'){
    v=lmn(0);let p=10,s=(y[h]=='-')?(h++,-1):1; if(t=='c'&&m&&y[h]=='$')h++
    m&=id(y[h])||y[h]=='.';  while(hn()&&id(y[h]))v.v=v.v*10+(+y[h++])
    m&&hn()&&y[h]=='.'&&h++; while(hn()&&id(y[h]))v.v+=(+y[h++])/p,p*=10;v.v*=s
   }
   else if(t=='r'||t=='o'){
    let cc=x.slice(f,f+(d||1));v=lms(''),f+=(d||1);
    while(hn()){if(cc.indexOf(y[h])>=0==lf?1:0){if(n)m=0;break}v.v+=y[h++];if(t=='o')break;}if(!m)v.v='';
   }
   else if(t=='e'||t=='p'){
    const [dy,dm,dd,dh,dmi,ds,dl,dma]=ll(dyad.parse(ISODATE,lms(y.slice(h)))), l=ln(dl), d=new Date(y.slice(h,h+l))
    if(l&&ln(dma)){h+=l}else{m=0}; v=t=='e'?lmn(m?0|(d.getTime()/1000):0):lmd(PARTS,[dy,dm,dd,dh,dmi,ds])
   }else{m=0}while(n&&y[h]&&h-si<n)h++,m=0;if(!sk&&v!=null)r.push(v)
  }return r.length==1?r[0]:lml(r)
 },
 format: (x,y)=>{
  const frec=(i,x,y)=>{
   if(i>=count(x))return y
   const fuse=(count(x)-i)%2?0:1,r=lml(ll(lit(y)?rows(y):y).map(z=>dyad.format(x.v[i+fuse],frec(i+fuse+1,x,lit(y)?lml(ll(z)):z))))
   return fuse?dyad.fuse(x.v[i],r):r
  };if(lil(x))return frec(0,x,y)
  x=ls(x),y=lil(y)?y:monad.list(y);let r='',f=0,h=0;while(x[f]){
   if(x[f]!='%'){r+=x[f++];continue}f++
   let n=0,d=0,sk=x[f]=='*'&&(f++,1),lf=x[f]=='-'&&(f++,1),pz=x[f]=='0'&&(f++,1)
   const hn=_=>m&&y[h]&&(n?h-si<n:1), id=x=>/[0-9]/.test(x)
   while(id(x[f]))n=n*10+(+x[f++]);x[f]=='.'&&f++
   while(id(x[f]))d=d*10+(+x[f++]);if(!x[f])break;const t=x[f++]
   let o='', a=t=='%'?NONE: (!sk&&h<count(y))?y.v[h++]: 'sluro'.indexOf(t)>=0?lms(''):NONE
   if     (t=='%'){o='%'}
   else if(t=='s'){o=ls(a)}
   else if(t=='l'){o=ls(a).toLowerCase()}
   else if(t=='u'){o=ls(a).toUpperCase()}
   else if(t=='r'||t=='o'){o=ls(a),lf=1;d=max(1,d);while(d&&x[f])d--,f++;d=n;}
   else if(t=='a'){o=ll(a).map(x=>clchar(String.fromCharCode(ln(x)))).join('')}
   else if(t=='b'){o=lb(a)?'true':'false'}
   else if(t=='f'){o=d?ln(a).toFixed(min(100,d)):wnum(ln(a))}
   else if(t=='c'){const v=ln(a);o=(v<0?'-':'')+'$'+abs(v).toFixed(min(100,d)||2)}
   else if(t=='i'){o=''+Math.trunc(ln(a))}
   else if(t=='h'||t=='H'){o=ln(a).toString(16);if(t=='H')o=o.toUpperCase()}
   else if(t=='e'){o=new Date(ln(a)*1000).toISOString().split('.')[0]+'Z'}
   else if(t=='p'){const d=ld(a);o=dyad.format(ISODATE,lml(PARTS.map(x=>dget(d,x)))).v}
   else if(t=='j'){o=fjson(a);}
   let vn=o.length; if(d&&(t=='f'||t=='c'))d=0;if(d&&lf)vn=min(d,vn)
   if(n&&!lf)for(let z=0;z<n-vn;z++)r+=pz?'0':' '
   for(let z=d&&!lf?max(0,vn-d):0;z<vn;z++)r+=o[z]
   if(n&&lf)for(let z=0;z<n-vn;z++)r+=pz?'0':' '
  }return lms(r)
 },
}
merge=(vals,keys,widen)=>{
 const i=lms('@index');let ix=null
 if(!widen){ix=lml([]);vals.v.map((val,z)=>{dget(val,i).v.map(x=>ix.v.push(x))})}
 if(count(vals)==0)vals.v.push(keys.v.reduce((x,y)=>dset(x,y,lml([])),lmd()))
 let r=monad.raze(lml(vals.v.map(x=>monad.table(widen?x:dyad.drop(i,x)))))
 if(widen){ix=lml(r.v['@index']),r=dyad.drop(i,r)}
 return {r,ix}
}
n_uplevel=([a])=>{let i=2, e=getev(), r=null, name=ls(a); while(e&&i){r=e.v[name];if(r)i--;e=e.p};return r||NONE}
n_eval=([x,y])=>{
 y=y?ld(y):lmd();const yy=lmd(y.k.slice(0),y.v.slice(0)), r=lmd(['value','vars','error'].map(lms),[NONE,yy,lms('')])
 const feval=([r,x])=>{dset(r,lms('value'),x);const b=dget(r,lms('vars')), v=getev().v; Object.keys(v).map(k=>dset(b,lms(k),v[k]));return r}
 try{
  const prog=parse(x?ls(x):'')
  blk_opa(prog,op.BUND,2),blk_lit(prog,lmnat(feval)),blk_op(prog,op.SWAP),blk_op(prog,op.CALL)
  issue(env_bind(null,yy.k.map(ls),lml(yy.v)),prog)
 }catch(e){dset(r,lms('error'),lms(e.x))};return r
}
triad={
 '@sel': (orig,vals,keys)=>{const mv=merge(vals,keys,0);return count(keys)>1?mv.r: dyad.take(mv.ix,dyad.drop(lms('index'),orig))},
 '@ext': (orig,vals,keys)=>{
  const r=monad.cols(triad['@sel'](orig,vals,keys));if(r.v.every(x=>lil(x)&&count(x)==1))r.v=r.v.map(monad.first)
  return count(r)!=1||count(r.k[0])?r: monad.first(r)
 },
 '@upd': (orig,vals,keys)=>{
  orig=dyad.drop(lms('index'),orig);const mv=merge(vals,keys,1),r=mv.r,ix=mv.ix
  Object.keys(r.v).map(c=>{
   if(r.v[c]==ix)return;const ci=c in orig.v, col=range(count(orig)).map(z=>ci?orig.v[c][z]:NONE)
   orig.v[c]=col,ix.v.map((x,row)=>col[0|ln(x)]=r.v[c][row])
  });return orig
 },
 '@ins': (v,n,x)=>{
  const torect=t=>{
   const n=lmn(Object.values(t.v).reduce((x,y)=>max(x,y.length),0))
   Object.keys(t.v).map(k=>t.v[k]=ll(dyad.take(n,lml(t.v[k]))))
  }, r=monad.table(dyad.dict(n,v));torect(r);return lin(x)?r:dyad[','](lt(x),r)
 },
}

findop=(n,prims)=>Object.keys(prims).indexOf(n), as_enum=x=>x.split(',').reduce((x,y,i)=>{x[y]=i;return x},{})
let tnames=0;tempname=_=>lms(`@t${tnames++}`)
op=as_enum('JUMP,JUMPF,LIT,DROP,SWAP,OVER,BUND,OP1,OP2,OP3,GET,SET,LOC,AMEND,TAIL,CALL,BIND,ITER,EACH,NEXT,COL,QUERY,IPRE,IPOST')
oplens=   [ 3   ,3    ,3  ,1   ,1   ,1   ,3   ,3  ,3  ,3  ,3  ,3  ,3  ,3    ,1   ,1   ,1   ,1   ,3   ,3   ,1  ,3    ,3   ,3     ]
blk_addb=(x,n  )=>x.b.push(0xFF&n)
blk_here=(x    )=>x.b.length
blk_setb=(x,i,n)=>x.b[i]=0xFF&n
blk_getb=(x,i  )=>0xFF&x.b[i]
blk_adds=(x,n  )=>{blk_addb(x,n>>8),blk_addb(x,n)}
blk_sets=(x,i,n)=>{blk_setb(x,i,n>>8),blk_setb(x,i+1,n)}
blk_gets=(x,i  )=>0xFFFF&(blk_getb(x,i)<<8|blk_getb(x,i+1))
blk_op  =(x,o  )=>{blk_addb(x,o);if(o==op.COL)blk_addb(x,op.SWAP)}
blk_opa =(x,o,i)=>{blk_addb(x,o),blk_adds(x,i);return blk_here(x)-2}
blk_imm =(x,o,k)=>{let i=x.locals.findIndex(x=>match(x,k));if(i==-1)i=x.locals.length,x.locals.push(k);blk_opa(x,o,i)}
blk_op1 =(x,n)=>blk_opa(x,op.OP1,findop(n,monad))
blk_op2 =(x,n)=>blk_opa(x,op.OP2,findop(n,dyad ))
blk_op3 =(x,n)=>blk_opa(x,op.OP3,findop(n,triad))
blk_lit =(x,v)=>blk_imm(x,op.LIT,v)
blk_set =(x,n)=>blk_imm(x,op.SET,n)
blk_loc =(x,n)=>blk_imm(x,op.LOC,n)
blk_get =(x,n)=>blk_imm(x,op.GET,n)
blk_getimm=(x,i)=>x.locals[i]
blk_cat=(x,y)=>{
 let z=0,base=blk_here(x);while(z<blk_here(y)){
  const b=blk_getb(y,z);if(b==op.LIT||b==op.GET||b==op.SET||b==op.LOC||b==op.AMEND){blk_imm(x,b,blk_getimm(y,blk_gets(y,z+1)))}
  else if(b==op.JUMP||b==op.JUMPF||b==op.EACH||b==op.NEXT){blk_opa(x,b,blk_gets(y,z+1)+base)}
  else{for(let i=0;i<oplens[b];i++)blk_addb(x,blk_getb(y,z+i))}z+=oplens[b]
 }
}
blk_end=x=>{
 let z=0;while(z<blk_here(x)){
  let b=blk_getb(x,z);z+=oplens[b];if(b!=op.CALL)continue
  let t=1,i=z;while(i<blk_here(x)){if(blk_getb(x,i)!=op.JUMP){t=0;break}const a=blk_gets(x,i+1);if(a<=i){t=0;break}i=a}if(t)blk_setb(x,z-1,op.TAIL)
 }return x
}

parse=text=>{
 let i=0,r=0,c=0, tq=null // text index, row, column, token queued
 const er=x=>{throw {x,r,c,i,stack:new Error().stack}}
 const nc=_=>{const x=text[i++];x=='\n'?(r++,c=0):(c++);return x}
 const iw=_=>text[i]in{' ':1,'\t':1,'\n':1,'#':1}
 const sw=_=>{while(iw())if(text[i]=='#')while(i<text.length&&text[i]!='\n')nc();else nc()}
 const id=_=>0<='0123456789'.indexOf(text[i])
 //          !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
 const tcc=' s" sss ()ssssdsdddddddddd: sssn@nnnnnnnnnnnnnnnnnnnnnnnnnn[ ]sn nnnnnnnnnnnnnnnnnnnnnnnnnn s s'
 const ncc='                nnnnnnnnnn     n nnnnnnnnnnnnnnnnnnnnnnnnnn    n nnnnnnnnnnnnnnnnnnnnnnnnnn    '
 const mcc='     xx x xxxx x          x xxx x                          x  x                             x x'
 const esc={'\\':'\\','"':'"','n':'\n'}
 const ne=_=>{const e=nc();return e in esc?esc[e]: er(`Invalid escape character '\\${e}' in string.`)}
 const nw=x=>{let v=+x;    while(id())v=(v*10)+(+nc());  return v}
 const nf=_=>{let v=0,p=10;while(id())v+=(+nc())/p,p*=10;return v}
 const nn=(x,tr,tc,v,sign)=>{
  if(x=='.'&&!id())return{t:'.',r:tr,c:tc}
  if(x=='.')v=nf();else v=nw(x),v+=(text[i]=='.')?(nc(),nf()):0
  return {t:'number',v:sign*v,r:tr,c:tc}
 }
 const tok=_=>{
  const w=iw()||i==0||(mcc[text[i-1].charCodeAt(0)-32]=='x');sw();if(i>=text.length)return{t:'the end of the script'}
  const tr=r,tc=c, x=nc(), cc=tcc[x.charCodeAt(0)-32]; let v=0
  if(cc==' '||cc==undefined)er(`Invalid character '${x}'.`)
  if(x=='-'&&w&&tcc[(text[i]||'').charCodeAt(0)-32]=='d')return nn(nc(),r,c,v,-1)
  if(cc=='n'){let v=x; while(ncc[(text[i]||' ').charCodeAt(0)-32]=='n')v+=nc();return {t:'name',v,r:tr,c:tc}}
  if(cc=='"'){let v='',c;while(i<text.length&&(c=nc())!='"')v+=(c=='\\'?ne():clchar(c));return{t:'string',v,r:tr,c:tc}}
  return cc=='s'?{t:'symbol',v:x,r:tr,c:tc}: cc=='d'?nn(x,tr,tc,v,1):{t:x,r:tr,c:tc}
 }
 const peek=_=>{if(!tq)tq=tok();return tq}
 const hasnext=_=>peek().t!='the end of the script'
 const peek2=_=>{const pi=i,pr=r,pc=c,pq=tq;next();const v=peek();i=pi,r=pr,c=pc,tq=pq;return v}
 const next=_=>{if(tq){const r=tq;tq=null;return r};return tok()}
 const matchp=k=>peek().t=='name'&&peek().v==k
 const match=k=>matchp(k)?(next(),1):0
 const matchsp=k=>peek().t==k?(next(),1):0
 const expect=t=>peek().t==t?next().v:er(`Expected ${t}, but found ${peek().t}.`)
 const ident=n=>{
  const kw={for:1,while:1,on:1,if:1,else:1,end:1,do:1,select:1,extract:1,update:1,insert:1,into:1,from:1,where:1,by:1,
   orderby:1,asc:1,desc:1,each:1,send:1,with:1,local:1};return !(n in kw||n in monad||n in dyad)
 }
 const name=n=>{const r=expect('name');if(!ident(r)&&n!='member')er(`'${r}' is a keyword, and cannot be used for a ${n} name.`);return r}
 const names=(x,n)=>{const r=[];while(!match(x))r.push(name(n));return r}
 const quote=_=>{const r=lmblk();expr(r);blk_end(r);return r}
 const block=_=>{const r=lmblk();iblock(r);return r}
 const quotesub=_=>{let c=0,r=lmblk();while(hasnext()&&!matchsp(']'))expr(r),c++;blk_opa(r,op.BUND,c);return r}
 const quotedot=_=>{const r=lmblk();blk_lit(r,lml([lms(name('member'))]));return r}
 const iblock=r=>{let c=0;while(hasnext()){if(match('end')){if(!c)blk_lit(r,NONE);return}if(c)blk_op(r,op.DROP);expr(r),c++};er(`Expected 'end' for block.`)}
 const parsequery=(b,func,dcol)=>{
  const cols=lmd([],[]);while(!matchp('from')&&!matchp('where')&&!matchp('by')&&!matchp('orderby')){
   let set=peek2().t==':', lit=peek().t=='string', name=lms(lit?(set?peek().v:''):peek().t=='name'?peek().v:'')
   let get=ident(ls(name)), unique=ls(name).length&&dkix(cols,name)==-1; if(set&&lit&&!unique)next(),next()
   const x=set&&unique?(next(),next(),name): get&&unique&&dcol?name: lms(dcol?`c${cols.k.length}`: '')
   cols.k.push(x),cols.v.push(quote())
  }
  let cw,cb,co,dir=-1,index=lmblk();blk_get(index,lms('index'))
  if(match('where'  )){cw=quote()}else{cw=lmblk(),blk_lit(cw,ONE )}
  if(match('by'     )){cb=quote()}else{cb=lmblk(),blk_lit(cb,NONE)}
  if(match('orderby')){co=quote();dir=match('asc')?-1: match('desc')?1: er(`Expected 'asc' or 'desc'.`)}
  else{co=lmblk(),blk_get(co,lms('index'))}if(!match('from'))er(`Expected 'from'.`)
  expr(b),blk_op1(b,'@tab')
  blk_lit(b,cw),blk_op(b,op.COL),blk_lit(b,cb),blk_op(b,op.COL),blk_lit(b,co),blk_op(b,op.COL),blk_lit(b,lmn(dir)),blk_opa(b,op.QUERY,func=='@upd')
  const keys=lml(cols.k.concat([lms('@index')])),name=tempname()
  blk_op(b,op.ITER);const head=blk_here(b);blk_lit(b,[ls(name)]);const each=blk_opa(b,op.EACH,0);
   blk_lit(b,keys),blk_get(b,name),cols.v.map(x=>(blk_lit(b,x),blk_op(b,op.COL)))
   blk_lit(b,index),blk_op(b,op.COL),blk_op(b,op.DROP),blk_opa(b,op.BUND,count(keys)),blk_op2(b,'dict')
  blk_opa(b,op.NEXT,head),blk_sets(b,each,blk_here(b)),blk_lit(b,keys),blk_op3(b,func)
 }
 const parseindex=(b,name)=>{
  const i=[];while(peek().t in{'[':1,'.':1}){
   if(matchsp('['))i.push(quotesub())
   if(matchsp('.')){
    if(peek().t in{'[':1,'.':1}){
     i.map(v=>(blk_cat(b,v),blk_op(b,op.CALL)))
     blk_op(b,op.ITER);const head=blk_here(b);blk_lit(b,['x'])
     const each=blk_opa(b,op.EACH,0);blk_get(b,lms('x')),parseindex(b)
     blk_opa(b,op.NEXT,head),blk_sets(b,each,blk_here(b));return
    }else{i.push(quotedot())}
   }
  }
  if(matchsp(':')){
   i.map(v=>blk_cat(b,v)),blk_opa(b,op.BUND,i.length),blk_op(b,op.OVER)
   for(let z=0;z<i.length-1;z++)blk_opa(b,op.IPRE,z),blk_opa(b,op.IPOST,z);expr(b),blk_imm(b,op.AMEND,name||NONE)
  }else{i.map(v=>(blk_cat(b,v),blk_op(b,op.CALL)))}
 }
 const term=b=>{
  if(peek().t=='number'){blk_lit(b,lmn(next().v));return}
  if(peek().t=='string'){blk_lit(b,lms(next().v));return}
  if(match('if')){
   expr(b);let bail=blk_opa(b,op.JUMPF,0),e=0,c=0
   while(hasnext()&&!match('end')&&!(e=match('else'))){if(c)blk_op(b,op.DROP);expr(b),c++;}
   if(!c)blk_lit(b,NONE);const exit=blk_opa(b,op.JUMP,0);blk_sets(b,bail,blk_here(b))
   if(e){iblock(b)}else{blk_lit(b,NONE)}blk_sets(b,exit,blk_here(b));return
  }
  if(match('while')){
   blk_lit(b,NONE);const head=blk_here(b);expr(b);const cond=blk_opa(b,op.JUMPF,0)
   blk_op(b,op.DROP),iblock(b),blk_opa(b,op.JUMP,head),blk_sets(b,cond,blk_here(b));return
  }
  if(match('each')){
   const n=names('in','variable');expr(b),blk_op(b,op.ITER);const head=blk_here(b);blk_lit(b,n)
   const each=blk_opa(b,op.EACH,0);iblock(b),blk_opa(b,op.NEXT,head),blk_sets(b,each,blk_here(b));return
  }
  if(match('on')){
   const n=name('function'),a=names('do','argument')
   blk_lit(b,lmon(n,a,blk_end(block()))),blk_op(b,op.BIND);return
  }
  if(match('send')){
   blk_lit(b,lmnat(n_uplevel)),blk_lit(b,lml([lms(name('function'))])),blk_op(b,op.CALL)
   expect('['),blk_cat(b,quotesub()),blk_op(b,op.CALL);return
  }
  if(match('local')){const n=lms(name('variable'));expect(':'),expr(b),blk_loc(b,n);return}
  if(match('select' )){parsequery(b,'@sel',1);return}
  if(match('extract')){parsequery(b,'@ext',0);return}
  if(match('update' )){parsequery(b,'@upd',1);return}
  if(match('insert')){
   const n=lml([]);while(!match('into')){n.v.push(lms(peek().t=='string'?next().v:name('column'))),expect(':'),expr(b)}
   blk_opa(b,op.BUND,count(n)),blk_lit(b,n),expr(b),blk_op3(b,'@ins');return
  }
  if(matchsp('(')){if(matchsp(')')){blk_lit(b,lml([]));return}expr(b),expect(')');return}
  const s=peek().v;if(findop(s,monad)>=0&&peek().t in{'symbol':1,'name':1}){next(),expr(b),blk_op1(b,s);return}
  const n=lms(name('variable'));if(matchsp(':')){expr(b),blk_set(b,n);return}blk_get(b,n),parseindex(b,n)
 }
 const expr=b=>{
  term(b);if(peek().t in {'[':1,'.':1}){parseindex(b)}
  if(matchsp('@')){
   const temp=tempname(),names=['v','k','i'];blk_set(b,temp),blk_op(b,op.DROP)
   expr(b),blk_op(b,op.ITER);const head=blk_here(b);blk_lit(b,names);const each=blk_opa(b,op.EACH,0)
    blk_get(b,temp),names.map(n=>blk_get(b,lms(n))),blk_opa(b,op.BUND,3),blk_op(b,op.CALL)
   blk_opa(b,op.NEXT,head),blk_sets(b,each,blk_here(b));return
  }
  const s=peek().v;if(findop(s,dyad)>=0&&peek().t in {'symbol':1,'name':1}){next(),expr(b),blk_op2(b,s)}
 }
 const b=lmblk();if(hasnext())expr(b);while(hasnext())blk_op(b,op.DROP),expr(b)
 if(blk_here(b)==0)blk_lit(b,NONE);return b
}

env_local=(e,n,x)=>{e.v[ls(n)]=x}
env_getr =(e,n  )=>{const r=e.v[ls(n)];return r?r: e.p?env_getr(e.p,n): null}
env_setr =(e,n,x)=>{const r=e.v[ls(n)];return r?e.v[ls(n)]=x: e.p?env_setr(e.p,n,x): null}
env_get  =(e,n  )=>env_getr(e,n)||NONE
env_set  =(e,n,x)=>{const r=env_getr(e,n);r?env_setr(e,n,x):env_local(e,n,x)}
env_bind =(e,k,v)=>{const r=lmenv(e); k.map((a,i)=>env_local(r,lms(a),v.v[i]||NONE));return r}
const monadi=Object.values(monad), dyadi=Object.values(dyad), triadi=Object.values(triad), states=[]; let state=null
pushstate=env=>{if(state){states.push(state)};state={e:[env],p:[],t:[],pcs:[]}}
popstate =_=>{state=states.pop()}
halt     =_=>{state.p=[],state.t=[],state.e=[state.e[0]]}
running  =_=>state.t.length>0
getev    =_=>state.e  [state.e  .length-1]
getblock =_=>state.t  [state.t  .length-1]
getpc    =_=>state.pcs[state.pcs.length-1]
setpc    =x=>state.pcs[state.pcs.length-1]=x
issue    =(env,blk)=>(state.e.push(env),state.t.push(blk),state.pcs.push(0))
descope  =_=>(state.e.pop(),state.t.pop(),state.pcs.pop())
ret      =x=>state.p.push(x)
arg      =_=>state.p.pop()
docall=(f,a,tail)=>{
 if(linat(f)){ret(f.f(ll(a)));return}
 if(!lion(f)){ret(l_at(f,monad.first(a)));return}
 if(tail){descope()}issue(env_bind(f.c,f.a,a),f.b),calldepth=max(calldepth,state.e.length)
}
runop=_=>{
 const b=getblock();if(!liblk(b))ret(state.t.pop())
 const pc=getpc(),o=blk_getb(b,pc),imm=(oplens[o]==3?blk_gets(b,1+pc):0); setpc(pc+oplens[o])
 switch(o){
  case op.DROP :arg();break
  case op.SWAP :{const a=arg(),b=arg();ret(a),ret(b);break}
  case op.OVER :{const a=arg(),b=arg();ret(b),ret(a),ret(b);break}
  case op.JUMP :setpc(imm);break
  case op.JUMPF:if(!lb(arg()))setpc(imm);break
  case op.LIT  :ret(blk_getimm(b,imm));break
  case op.GET  :{ret(env_get(getev(),blk_getimm(b,imm)));break}
  case op.SET  :{const v=arg();env_set(getev(),blk_getimm(b,imm),v),ret(v);break}
  case op.LOC  :{const v=arg();env_local(getev(),blk_getimm(b,imm),v),ret(v);break}
  case op.BUND :{const r=[];for(let z=0;z<imm;z++)r.push(arg());r.reverse(),ret(lml(r));break}
  case op.OP1  :{                      ret(monadi[imm](arg()    ));break}
  case op.OP2  :{const         y=arg();ret(dyadi [imm](arg(),y  ));break}
  case op.OP3  :{const z=arg(),y=arg();ret(triadi[imm](arg(),y,z));break}
  case op.IPRE :{const s=arg(),i=arg();ret(i),docall(s,i.v[imm]);if(lion(s)||lii(s)||linat(s)){for(let z=0;z<=imm;z++)i.v[z]=null}break}
  case op.IPOST:{const s=arg(),i=arg(),r=arg();ret(i.v[imm]?r:s),ret(i),ret(s);break}
  case op.AMEND:{
   let v=arg(),r=arg(),i=ll(arg()),ro=arg(),n=blk_getimm(b,imm),t={v:1}
   if(i.length&&!i[0]){i=i.filter(x=>x),t.v=0}r=amendv(ro,i,v,0,t);if(t.v&&!lin(n))env_set(getev(),n,r);ret(r);break
  }
  case op.CALL : // fall through:
  case op.TAIL :{const a=arg(),f=arg();docall(f,a,o==op.TAIL);break}
  case op.BIND :{const f=arg(),r=lmon(f.n,f.a,f.b);r.c=getev(),env_local(getev(),lms(f.n),r),ret(r);break}
  case op.ITER :{const x=arg();ret(lil(x)?x:ld(x));ret(lid(x)?lmd():lml([]));break}
  case op.EACH :{
   const n=arg(),r=arg(),s=arg();if(count(r)==count(s)){setpc(imm),ret(r);break}
   const z=count(r), v=lml([s.v[z],lid(s)?s.k[z]:lmn(z),lmn(z)]);
   state.e.push(env_bind(getev(),n,v)),ret(s),ret(r);break
  }
  case op.NEXT :{const v=arg(),r=arg(),s=arg();state.e.pop();if(lid(r))r.k.push(s.k[r.v.length]);r.v.push(v),ret(s),ret(r),setpc(imm);break}
  case op.COL  :{
   const ex=arg(),t=arg(),n=Object.keys(t.v),v=lml(Object.values(t.v).map(lml));ret(t)
   n.push('column'),v.v.push(t),issue(env_bind(getev(),n,v),ex);break
  }
  case op.QUERY:{
   const order_dir=ln(arg()),t=arg(),ct=lmn(count(t))
   const o=dyad.take(ct,lml(ll(arg()))),b=dyad.take(ct,lml(ll(arg()))),w=dyad.take(ct,lml(ll(arg()))),r=monad.rows(t)
   const uk=r.v.reduce((x,_,z)=>{if(lb(w.v[z]))dset(x,b.v[z],b.v[z]);return x},lmd([],[]))
   const rows=uk.v.map((v,group)=>{ // sort groups, select rows
    const lex_list=(x,y,a,ix)=>{
     if(x.length<ix&&y.length<ix)return 0;const xv=x[ix]||NONE,yv=y[ix]||NONE
     return lex_less(xv,yv)?a: lex_more(xv,yv)?!a: lex_list(x,y,a,ix+1)
    }
    const lex_less=(a,b)=>lil(a)&&lil(b)? lex_list(a.v,b.v,1,0): lb(dyad['<'](a,b))
    const lex_more=(a,b)=>lil(a)&&lil(b)? lex_list(a.v,b.v,0,0): lb(dyad['>'](a,b))
    const gp=range(r.v.length).filter(x=>lb(w.v[x])&&match(v,b.v[x])).sort((a,b)=>{
     if(lex_less(o.v[a],o.v[b]))return  order_dir
     if(lex_more(o.v[a],o.v[b]))return -order_dir
     return a-b // produce a stable sort
    })
    return monad.table(lml(gp.map((v,z)=>{const t=r.v[v];dset(t,lms('gindex'),lmn(z)),dset(t,lms('group'),lmn(group));return t})))
   });if(rows.length==0&&!imm)rows.push(dyad.take(NONE,t))
   ret(t),ret(lml(rows));break
  }
 }while(running()&&getpc()>=blk_here(getblock()))descope()
}

fchar=x=>x=='I'?'i': x=='B'?'b': x=='L'?'s': x
n_writecsv=([x,y,d])=>{
 let r='', spec=y?ls(y).split(''):[];const t=lt(x), c=Object.keys(t.v).length; d=d?ls(d)[0]:','
 while(spec.length<c)spec.push('s')
 let n=0;spec.forEach((x,i)=>{if(x=='_')return;if(n)r+=d;n++;r+=Object.keys(t.v)[i]||`c${i+1}`})
 rows(t).v.forEach(row=>{
  r+='\n';let n=0, cols=Object.keys(row.v);spec.forEach((x,i)=>{
   if(x=='_')return;if(n)r+=d;n++
   const sv=dyad.format(lms('%'+fchar(x)),row.v[cols[i]]).v; r+=(/["\n]/.test(sv)||sv.indexOf(d)>=0?`"${sv.replace(/"/g,'""')}"`:sv)
  })
 });return lms(r)
}
n_readcsv=([x,y,d])=>{
 let i=0,n=0, spec=y&&lis(y)?ls(y):null, text=count(x)?ls(x):'', r=lmt({}); d=d?ls(d)[0]:','
 const nv=_=>{let r='';while(text[i]&&text[i]!='\n'&&text[i]!=d)r+=text[i++];return r}, match=x=>text[i]==x?(i++,1):0
 while(i<text.length&&text[i]!='\n'){
  while(match(' '));const v=nv();if(!spec||(n<spec.length&&spec[n]!='_'))r.v[v]=[];n++;if(match('\n'))break;while(match(' '));match(d)
 }
 while(spec&&n<spec.length){if(spec[n]!='_'){r.v['c'+n]=[]};n++}
 if(!spec)spec='s'.repeat(Object.keys(r.v).length)
 let slots=0,slot=0;spec.split('').map(z=>{if(z!='_')slots++;});slots=min(slots,Object.keys(r.v).length),n=0
 if(i>=text.length)return r;while(i<=text.length){
  while(match(' '));
  let val='';if(match('"')){while(text[i]){if(match('"')){if(match('"')){val+='"'}else{break}}else{val+=text[i++]}}}else{val=nv()}
  if(spec[n]&&spec[n]!='_'){
   const k=Object.keys(r.v)[slot], x=(val[0]||'').toLowerCase(), s=spec[n]
   let sign=1,o=0; if(val[o]=='-')sign=-1,o++;if(val[o]=='$')o++;
   r.v[k].push(dyad.parse(lms('%'+fchar(s)),lms(val))),slot++
  };n++
  if(i>=text.length||text[i]=='\n'){
   while(n<spec.length){const u=spec[n++];if(u!='_'&&slot<slots)r.v[Object.keys(r.v)[slot++]].push(u=='s'?lms(''):NONE);}
   if(text[i]=='\n'&&i==text.length-1)break;i++,n=0,slot=0
  }else{while(match(' '));match(d)}
 };return r
}
n_writexml=([x])=>{
 const esc=x=>{const e={'&':'amp',"'":'apos','"':'quot','<':'lt','>':'gt'};return ls(x).replace(/[&'"<>]/g,x=>e[x]?`&${e[x]};`:x)}
 const rec=(x,tab)=>{
  if(lil(x))return x.v.map(x=>rec(x,tab)).join(''); if(!lid(x))return esc(x)+(tab?'\n':'')
  const t=ls(dget(x,lms('tag'))||lms('')),a=ld(dget(x,lms('attr'))||lmd()),c=ll(dget(x,lms('children'))||lml([]))
  const r=`<${t}${a.k.map((k,i)=>` ${ls(k)}="${esc(a.v[i])}"`).join('')}${c.length?'':'/'}>\n`
  return c.length?`${r}${c.map(x=>(' '.repeat(tab+2))+rec(x,tab+2)).join('')}${' '.repeat(tab)}</${t}>\n`:r
 };return lms(rec(x,0))
}
n_readxml=([x])=>{
 let i=0,t=ls(x)
 const xm=x=>t[i]==x?(i++,1):0
 const xc=_=>{while(t[i]&&t[i]!='>')i++;i++}
 const xs=_=>{let w=0;while(/[ \n]/.test(t[i]))i++,w=1;return w}
 const xe=(a,b)=>t.slice(i,i+2+a.length)==`&${a};`?(i+=2+a.length,b):null
 const name=_=>{let r='';xs();while(t[i]&&!/[>/= \n]/.test(t[i]))r+=t[i++];xs();return r.toLowerCase()}
 const text=stop=>{
  let r='';while(t[i]&&!(stop==' '&&/[>/ \n]/.test(t[i]))){
   if(xs())r+=' ';if(stop==t[i])break;r+=xe('amp','&')||xe('apos',"'")||xe('quot','"')||xe('lt','<')||xe('gt','>')||xe('nbsp',' ')||t[i++]
  }if(/['"]/.test(stop)&&t[i])i++;return lms(r)
 }
 const rec=ctag=>{
  let r=[];while(t[i]){
   const w=xs();if(t.slice(i,i+9)=='<![CDATA['){i+=9;let c='';while(t[i]&&t.slice(i,i+3)!=']]>')c+=t[i++];i+=3;r.push(lms(c));continue}
   if(t[i]!='<'){if(w)i--;r.push(text('<'));continue}i++,xs()
   if(xm('!')||xm('?')){xc();continue};if(xm('/')){const n=name();xm('>');if(ctag==n)break;continue}
   const tag=lmd(),attr=lmd(),n=name();r.push(tag),dset(tag,lms('tag'),lms(n)),dset(tag,lms('attr'),attr),dset(tag,lms('children'),lml([]))
   while(t[i]&&!/[>/]/.test(t[i])){const n=lms(name());if(xm('=')){xs(),dset(attr,n,text(xm("'")?"'":xm('"')?'"':' '))}else{dset(attr,n,ONE)}}
   if(xm('/')){xc()}else{if(t[i])i++;dset(tag,lms('children'),rec(n))}
  }return lml(r)
 }
 return rec('')
}
n_random=z=>{
 const randint=x=>{let y=seed;y^=(y<<13),y^=(y>>>17),(y^=(y<<15));return mod(seed=y,x);} // xorshift32
 const randelt=x=>lin(x)?lmn(randint(ln(x))): count(x)<1?NONE: l_at(x,lmn(randint(count(x))))
 let x=z[0]||NONE;if(z.length<2)return randelt(x)
 const y=ln(z[1]);if(y>=0){const r=[];for(let z=0;z<y;z++)r.push(randelt(x));return lml(r);}
 x=lin(x)?monad.range(x).v:ll(x);const p=range(x.length),r=[]
 for(let i=x.length-1;i>0;i--){const j=randint(i+1),t=p[j];p[j]=p[i],p[i]=t}
 for(let z=0;z<abs(y);z++)r.push(x[p[z%x.length]]);return lml(r)
}
let frame_count=0,audio_playing=0
interface_system=lmi((self,i,x)=>{
 if(!i)return NONE
 if(x){if(lis(i)&&i.v=='seed'){seed=0|ln(x);return x}}
 if(lis(i)&&i.v=='version'  )return lms(VERSION)
 if(lis(i)&&i.v=='platform' )return lms('web')
 if(lis(i)&&i.v=='seed'     )return lmn(seed)
 if(lis(i)&&i.v=='playing'  )return lmn(audio_playing)
 if(lis(i)&&i.v=='frame'    )return lmn(frame_count)
 if(lis(i)&&i.v=='now'      )return lmn(0|(new Date().getTime()/1000))
 if(lis(i)&&i.v=='ms'       )return lmn(0|(Date.now()))
 if(lis(i)&&i.v=='workspace')return lmd(['allocs','depth'].map(lms),[allocs,calldepth].map(lmn))
 return x?x:NONE
},'system')
showt=(x,toplevel)=>{
 if(!toplevel)return `<TABLE...${Object.keys(x.v).length}>`;try{
 const w=Object.keys(x.v).map(k=>x.v[k].reduce((x,y)=>max(x,show(y).length+2),k.length+2))
 const s='+'+Object.keys(x.v).map((x,i)=>"-".repeat(w[i])).join('+')+'+'
 const v=(Object.values(x.v)[0]||[]).map((_,r)=>Object.keys(x.v).map(k=>' '+show(x.v[k][r])).map((f,i)=>f+(' '.repeat(max(0,w[i]-f.length)))))
          .map(x=>`|${x.join('|')}|`).join('\n')
 return `${s}\n|${Object.keys(x.v).map((x,i)=>` ${x+(' '.repeat(w[i]-x.length-2))} `).join('|')}|\n${s}${v.length?'\n'+v+'\n'+s:''}`
 }catch(err){console.log('cannot serialize',x);throw err}
}
show=(x,toplevel)=>linat(x)?'on native x do ... end':
  lil(x)?`(${x.v.map(x=>show(x)).join(',')})`: lit(x)?showt(x,toplevel): lion(x)?`on ${x.n}${x.a.map(x=>' '+x).join('')} do ... end`:
  lis(x)?`"${x.v.split('').map(x=>({'\n':'\\n','\\':'\\\\','"':'\\"'})[x]||x).join('')}"`:
  lin(x)?fjson(x):lid(x)?`{${x.k.map((k,i)=>`${show(k)}:${show(x.v[i])}`).join(',')}}`:
  lii(x)?`<${x.n}>`:`<INVALID ${x}>`

// dom + utilities

FORMAT_VERSION=1, RTEXT_END=2147483647, SFX_RATE=8000, FRAME_QUOTA=MODULE_QUOTA=10*4096, TRANS_QUOTA=2*4096, LOOP_QUOTA=1*4096, ANTS=255
sleep_frames=0, sleep_play=0, pending_popstate=0
DEFAULT_HANDLERS=`
on link x do go[x] end
on drag x do if !me.locked|me.draggable me.line[(pointer.prev-me.pos)/me.scale x] end end
on order x do if !me.locked me.value:select orderby me.value[x] asc from me.value end end
on navigate x do if x~"right" go["Next"] end if x~"left" go["Prev"] end end
on loop x do x end
`
DEFAULT_TRANSITIONS=`
transition[on SlideRight c a b t do  c.paste[a c.size*t,0   ] c.paste[b c.size*(t-1),0]      end]
transition[on SlideLeft  c a b t do  c.paste[a c.size*(-t),0] c.paste[b c.size*(1-t),0]      end]
transition[on SlideDown  c a b t do  c.paste[a c.size*0,t   ] c.paste[b c.size*0,t-1  ]      end]
transition[on SlideUp    c a b t do  c.paste[a c.size*0,-t  ] c.paste[b c.size*0,1-t  ]      end]
transition[on WipeRight  c a b t do  c.rect[0,0        c.size*t,1    ]          c.merge[a b] end]
transition[on WipeLeft   c a b t do  c.rect[0,0        c.size*(1-t),1]          c.merge[b a] end]
transition[on WipeDown   c a b t do  c.rect[0,0        c.size*1,t    ]          c.merge[a b] end]
transition[on WipeUp     c a b t do  c.rect[0,0        c.size*1,1-t  ]          c.merge[b a] end]
transition[on BoxIn      c a b t do  c.rect[c.size/2   c.size*t   "center"]     c.merge[a b] end]
transition[on BoxOut     c a b t do  c.rect[c.size/2   c.size*1-t "center"]     c.merge[b a] end]
`
FONTS={
 menu:'%%FNT0EA0BAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAwADAAMAAwADAAMAAAADAAMAAAAAAAAAAAwAAoACgAKA'+
 'AAAAAAAAAAAAAAAAAAAAAAAAACBIAEgB/ACQAJAD+AEgASAAAAAAAAAAAAAAABSAAcACoAOAA4ABwADgAOACoAHAAIAAA'+
 'AAAACW4AkgCUAGQACAAIABMAFIAkgCMAAAAAAAAACAAAeADMAM0AYQDOAMwAzADMAHgAAAAAAAAAAQAAgACAAIAAAAAAA'+
 'AAAAAAAAAAAAAAAAAAAAyAAQADAAMAAwADAAMAAwADAAEAAIAAAAAAAA4AAQABgAGAAYABgAGAAYABgAEAAgAAAAAAABQ'+
 'AAIACoAHAAqAAgAAAAAAAAAAAAAAAAAAAABQAAAAAAACAAIAD4ACAAIAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAD'+
 'AAMAAQACAAAAABQAAAAAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAADAAMAAAAAAAAAABQgACAAQ'+
 'ABAAIAAgAEAAQACAAIAAAAAAAAAABgAAeADMAMwAzADMAMwAzADMAHgAAAAAAAAABgAAMABwADAAMAAwADAAMAAwADAAA'+
 'AAAAAAABgAAeACMAAwADAAYADAAYADAAPwAAAAAAAAABgAA/AAYADAAeAAMAAwADACMAHgAAAAAAAAABwAADAAcACwATA'+
 'CMAP4ADAAMAAwAAAAAAAAABgAA/ADAAMAA+AAMAAwADACMAHgAAAAAAAAABgAAOABgAMAA+ADMAMwAzADMAHgAAAAAAAA'+
 'ABgAA/AAMAAwADAAYADAAMAAwADAAAAAAAAAABgAAeADMAMwAzAB4AMwAzADMAHgAAAAAAAAABgAAeADMAMwAzADMAHwA'+
 'DAAYAHAAAAAAAAAAAgAAAAAAAMAAwAAAAAAAAADAAMAAAAAAAAAAAgAAAAAAAMAAwAAAAAAAAADAAMAAQACAAAAABQAAA'+
 'AAYADAAYADAAGAAMAAYAAAAAAAAAAAABgAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAAAABQAAAADAAGAAMAAYADAAYADAAA'+
 'AAAAAAAAAABgAAeACMAAwAGAAwADAAAAAwADAAAAAAAAAACQAAAAA+AEEAnICkgKSAmwBAAD4AAAAAAAAABgAAeADMAMw'+
 'AzAD8AMwAzADMAMwAAAAAAAAABgAA+ADMAMwAzAD4AMwAzADMAPgAAAAAAAAABgAAeADEAMAAwADAAMAAwADEAHgAAAAA'+
 'AAAABgAA+ADMAMwAzADMAMwAzADMAPgAAAAAAAAABQAA+ADAAMAAwADwAMAAwADAAPgAAAAAAAAABQAA+ADAAMAAwADwA'+
 'MAAwADAAMAAAAAAAAAABgAAeADEAMAAwADcAMwAzADMAHgAAAAAAAAABgAAzADMAMwAzAD8AMwAzADMAMwAAAAAAAAAAg'+
 'AAwADAAMAAwADAAMAAwADAAMAAAAAAAAAABgAADAAMAAwADAAMAMwAzADMAHgAAAAAAAAABwAAxgDMANgA8ADgAPAA2AD'+
 'MAMYAAAAAAAAABQAAwADAAMAAwADAAMAAwADAAPgAAAAAAAAACgAAgEDAwOHA88C+wJzAiMCAwIDAAAAAAAAABwAAggDC'+
 'AOIA8gC6AJ4AjgCGAIIAAAAAAAAABgAAeADMAMwAzADMAMwAzADMAHgAAAAAAAAABgAA+ADMAMwAzAD4AMAAwADAAMAAA'+
 'AAAAAAABgAAeADMAMwAzADMAMwAzADMAHgADAAAAAAABgAA+ADMAMwAzAD4AMwAzADMAMwAAAAAAAAABQAAcADIAMAA4A'+
 'BwADgAGACYAHAAAAAAAAAABgAA/AAwADAAMAAwADAAMAAwADAAAAAAAAAABgAAzADMAMwAzADMAMwAzADMAHgAAAAAAAA'+
 'ABgAAzADMAMwAzADMAMwAzADIAPAAAAAAAAAACgAAzMDMwMzAzMDMwMzAzMDMgP8AAAAAAAAABgAAzADMAMwAzAB4AMwA'+
 'zADMAMwAAAAAAAAABgAAzADMAMwAzAB4ADAAMAAwADAAAAAAAAAABgAA/AAMAAwAGAAwAGAAwADAAPwAAAAAAAAAA+AAw'+
 'ADAAMAAwADAAMAAwADAAMAA4AAAAAAABYAAgABAAEAAIAAgABAAEAAIAAgAAAAAAAAAA+AAYABgAGAAYABgAGAAYABgAG'+
 'AA4AAAAAAABQAAIABQAIgAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAA4AAQAAgAAA'+
 'AAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAHgAjAB8AMwAzADMAHwAAAAAAAAABgAAwADAAPgAzADMAMwAzADMAPgAAAAA'+
 'AAAABQAAAAAAAHAAyADAAMAAwADIAHAAAAAAAAAABgAADAAMAHwAzADMAMwAzADMAHwAAAAAAAAABgAAAAAAAHgAzADMA'+
 'PwAwADEAHgAAAAAAAAABQAAOABgAPAAYABgAGAAYABgAGAAAAAAAAAABgAAAAAAAHwAzADMAMwAzADMAHwADACMAHgABg'+
 'AAwADAAPgAzADMAMwAzADMAMwAAAAAAAAAAgAAwAAAAMAAwADAAMAAwADAAMAAAAAAAAAABQAAGAAAABgAGAAYABgAGAA'+
 'YABgAGACYAHAABgAAwADAAMwA2ADwAOAA8ADYAMwAAAAAAAAAAgAAwADAAMAAwADAAMAAwADAAMAAAAAAAAAACgAAAAAA'+
 'AP+AzMDMwMzAzMDMwMzAAAAAAAAABgAAAAAAAPgAzADMAMwAzADMAMwAAAAAAAAABgAAAAAAAHgAzADMAMwAzADMAHgAA'+
 'AAAAAAABgAAAAAAAPgAzADMAMwAzADMAPgAwADAAAAABgAAAAAAAHwAzADMAMwAzADMAHwADAAMAAAABQAAAAAAANgA4A'+
 'DAAMAAwADAAMAAAAAAAAAABQAAAAAAAHAAyADgAHAAOACYAHAAAAAAAAAABAAAYABgAPAAYABgAGAAYABgADAAAAAAAAA'+
 'ABgAAAAAAAMwAzADMAMwAzADMAHwAAAAAAAAABgAAAAAAAMwAzADMAMwAzADIAPAAAAAAAAAACgAAAAAAAMzAzMDMwMzA'+
 'zMDMgP8AAAAAAAAABgAAAAAAAMwAzADMAHgAzADMAMwAAAAAAAAABgAAAAAAAMwAzADMAMwAzADMAHwADACMAHgABgAAA'+
 'AAAAPwADAAYADAAYADAAPwAAAAAAAAAAyAAQABAAEAAQACAAEAAQABAAEAAIAAAAAAAAYAAgACAAIAAgACAAIAAgACAAI'+
 'AAgAAAAAAAA4AAQABAAEAAQAAgAEAAQABAAEAAgAAAAAAABgAAAAAAAGQAmAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAA'+
 'AAAAAAAAAAADbANsAAAAAAAAA',
 body:'%%FNT0CAoBAQAAAAAAAAAAAAABAICAgICAAIAAAAMAoKAAAAAAAAAABQAAUPhQ+FAAAAAFIHCooHAoqHAgAAgAf5KUbhk'+
 'pRgAABzBIUCBUiJRiAAABAICAAAAAAAAAAAMgQICAgICAQCAAA4BAICAgICBAgAAFAFAg+CBQAAAAAAUAACAg+CAgAAAA'+
 'AgAAAAAAAABAQIAEAAAAAPAAAAAAAAEAAAAAAAAAgAAABBAQICBAQICAAAAFAHCIiIiIiHAAAAUAIGAgICAgIAAABQBwi'+
 'AgQIED4AAAFAPgQIHAIiHAAAAUAEDBQkPgQEAAABQD4gPAICIhwAAAFADBAgPCIiHAAAAUA+AgQECAgIAAABQBwiIhwiI'+
 'hwAAAFAHCIiHgIEGAAAAMAAABAAAAAQAAABAAAACAAAAAgIEAEAAAQIEAgEAAAAAUAAAD4APgAAAAABAAAQCAQIEAAAAA'+
 'FADBICBAgACAAAAcAOESaqqqcQDgABQAgIFBQ+IiIAAAFAPCIiPCIiPAAAAUAcIiAgICIcAAABQDgkIiIiJDgAAAEAPCA'+
 'gOCAgPAAAAQA8ICA4ICAgAAABQBwiICYiIhwAAAFAIiIiPiIiIgAAAIAQEBAQEBAQAAABQAICAgIiIhwAAAFAIiQoMCgk'+
 'IgAAAQAgICAgICA8AAABwCCxqqSgoKCAAAFAMjIqKiYmIgAAAUAcIiIiIiIcAAABQDwiIjwgICAAAAFAHCIiIiIqHAQAA'+
 'UA8IiI8KCQiAAABQBwiIBwCIhwAAAFAPggICAgICAAAAUAiIiIiIiIcAAABQCIiIhQUCAgAAAHAIKCVFQoKCgAAAUAiIh'+
 'QIFCIiAAABQCIiFAgICAgAAAEAPAQIECAgPAAAANgQEBAQEBAQGAABICAQEAgIBAQAAADYCAgICAgICBgAAMAQKAAAAAA'+
 'AAAABgAAAAAAAAD8AAACAIBAAAAAAAAAAAQAAABgEHCQcAAABACAgOCQkJDgAAAEAAAAYJCAkGAAAAQAEBBwkJCQcAAAB'+
 'AAAAGCQ8IBgAAAEADBA4EBAQEAAAAQAAABwkJCQcBBgBACAgOCQkJCQAAACAEAAwEBAQEAAAAMAIABgICAgICDABACAgJ'+
 'CgwKCQAAACAMBAQEBAQEAAAAcAAADskpKSkgAABAAAAOCQkJCQAAAEAAAAYJCQkGAAAAQAAADgkJCQ4ICABAAAAHCQkJB'+
 'wEBAEAAAAsMCAgIAAAAQAAABwgGAQ4AAAAwBAQOBAQEAgAAAEAAAAkJCQkHAAAAUAAACIUFAgIAAABwAAAIJUVCgoAAAF'+
 'AAAAiFAgUIgAAAQAAACQkJCQcBBgBAAAAPAgQIDwAAADIEBAQIBAQEAgAAGAgICAgICAgIAAA4BAQEAgQEBAgAAFAGiwA'+
 'AAAAAAAAAUAAAAAAAAAqAAA',
 mono:'%%FNT0CAsBBQAAAAAAAAAAAAAABQAAICAgICAAIAAABQAAUFBQAAAAAAAABQAAUPhQ+FAAAAAABQAgcKigcCiocCAABQA'+
 'ASKhQIFCokAAABQAAYJCgQKiQaAAABQAgICAAAAAAAAAABQAQICBAQEAgIBAABQAgEBAICAgQECAABQAAIKhwqCAAAAAA'+
 'BQAAACAg+CAgAAAABQAAAAAAAABgYCBABQAAAAAA+AAAAAAABQAAAAAAAAAwMAAABQgIEBAgIEBAgIAABQAAcIiYqMiIc'+
 'AAABQAAIGAgICAgIAAABQAAcIgIECBA+AAABQAAcIgIMAiIcAAABQAAEDBQkPgQEAAABQAA+IDwCAiIcAAABQAAcIDwiI'+
 'iIcAAABQAA+AgIECAgIAAABQAAcIiIcIiIcAAABQAAcIiIiHgIcAAABQAAADAwAAAwMAAABQAAAGBgAABgYCBABQAACBA'+
 'gQCAQCAAABQAAAAD4APgAAAAABQAAQCAQCBAgQAAABQAAcIgIECAAIAAABQBwiIio6LCAiHAABQAAcIiI+IiIiAAABQAA'+
 '8IiI8IiI8AAABQAAcIiAgICIcAAABQAA8IiIiIiI8AAABQAA+ICA8ICA+AAABQAA+ICA8ICAgAAABQAAcIiAmIiIcAAAB'+
 'QAAiIiI+IiIiAAABQAAICAgICAgIAAABQAACAgICIiIcAAABQAAiJCgwKCQiAAABQAAgICAgICA+AAABQAAiNioiIiIiA'+
 'AABQAAiMiomIiIiAAABQAAcIiIiIiIcAAABQAA8IiI8ICAgAAABQAAcIiIiIiIcAgABQAA8IiI8IiIiAAABQAAcIiAcAi'+
 'IcAAABQAA+CAgICAgIAAABQAAiIiIiIiIcAAABQAAiIiIUFAgIAAABQAAiIiIiKjYiAAABQAAiFAgICBQiAAABQAAiIiI'+
 'UCAgIAAABQAA+AgQIECA+AAABQAwICAgICAgIDAABYCAQEAgIBAQCAgABQAwEBAQEBAQEDAABQAgUIgAAAAAAAAABQAAA'+
 'AAAAAAA+AAABQBAIBAAAAAAAAAABQAAAAB4iIiYaAAABQAAgIDwiIiI8AAABQAAAABwiICAeAAABQAACAh4iIiIeAAABQ'+
 'AAAABwiPiAeAAABQAAGCBwICAgIAAABQAAAAB4iIiIeAhwBQAAgIDwiIiIiAAABQAAIAAgICAgIAAABQAAIAAgICAgICD'+
 'ABQAAgICQoOCQiAAABQAAICAgICAgMAAABQAAAADwqKioqAAABQAAAACwyIiIiAAABQAAAABwiIiIcAAABQAAAADwiIiI'+
 '8ICABQAAAAB4iIiIeAgIBQAAAACwyICAgAAABQAAAAB4gHAI8AAABQAAICB4ICAgGAAABQAAAACIiIiYaAAABQAAAACIi'+
 'FBQIAAABQAAAACoqKioUAAABQAAAACIUCBQiAAABQAAAACIiIiIeAhwBQAAAAD4ECBA+AAABQAYICAgwCAgIBgABSAgIC'+
 'AgICAgICAgBQDAICAgGCAgIMAABQAAaLAAAAAAAAAABQAAAAAAAAAAqAAA'
}
COLORS=[
 0xFFFFFFFF,0xFFFFFF00,0xFFFF6500,0xFFDC0000,0xFFFF0097,0xFF360097,0xFF0000CA,0xFF0097FF,
 0xFF00A800,0xFF006500,0xFF653600,0xFF976536,0xFFB9B9B9,0xFF868686,0xFF454545,0xFF000000,
]
BRUSHES=[
 0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00, 0x00,0x00,0x10,0x38,0x10,0x00,0x00,0x00,
 0x00,0x00,0x18,0x3C,0x3C,0x18,0x00,0x00, 0x00,0x38,0x7C,0x7C,0x7C,0x38,0x00,0x00,
 0x38,0x7C,0xFE,0xFE,0xFE,0x7C,0x38,0x00, 0x10,0x00,0x41,0x08,0x80,0x11,0x00,0x22,
 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00, 0x00,0x00,0x38,0x38,0x38,0x00,0x00,0x00,
 0x00,0x00,0x3C,0x3C,0x3C,0x3C,0x00,0x00, 0x00,0x7C,0x7C,0x7C,0x7C,0x7C,0x00,0x00,
 0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0x00, 0x20,0x0A,0x80,0x24,0x01,0x48,0x02,0x51,
 0x00,0x00,0x10,0x10,0x10,0x00,0x00,0x00, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,
 0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00, 0x00,0x00,0x08,0x10,0x20,0x00,0x00,0x00,
 0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x00, 0x02,0x00,0x08,0x00,0x20,0x00,0x80,0x00,
 0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0x00, 0x00,0x00,0x20,0x10,0x08,0x00,0x00,0x00,
 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00, 0x80,0x00,0x20,0x00,0x08,0x00,0x02,0x00,
]
ALIGN={left:0,center:1,right:2}
DEFAULT_ANIMS='[[13,9,5,1,5,9],[4,4,8,14,14,8],[18,18,20,19,19,20],[0,0,0,0,1,1,1,1]]'
DEFAULT_PATTERNS=
 '%%IMG0AAgA4AAAAAAAAAAA//////////+AgID/CAgI/yBAgMEiHAgQgAAIAIAACAD/d//d/3f/3XEiF49HInT4iFAgAgW'+
 'IiIiIACIAiAAiAHfdd9133XfdQIAACAQCACABAQOESDAMAogiiCKIIogiqlWqVapVqlWABEAIASACEMAMjbEwAxvYqgCq'+
 'AKoAqgD/Vf9V/1X/Vf8A/wD/AP8AqqqqqqqqqqpEiBEiRIgRIt27d+7du3fuQIABAgQIECC/f/79+/fv3wgAqgAIAIgAj'+
 '493mPj4d4mqAIgUIkGIALCwsL8Av7+w'

array_is   =x=>lii(x)&&x.n=='array'
image_is   =x=>lii(x)&&x.n=='image'
sound_is   =x=>lii(x)&&x.n=='sound'
font_is    =x=>lii(x)&&x.n=='font'
button_is  =x=>lii(x)&&x.n=='button'
field_is   =x=>lii(x)&&x.n=='field'
grid_is    =x=>lii(x)&&x.n=='grid'
slider_is  =x=>lii(x)&&x.n=='slider'
canvas_is  =x=>lii(x)&&x.n=='canvas'
deck_is    =x=>lii(x)&&x.n=='deck'
card_is    =x=>lii(x)&&x.n=='card'
patterns_is=x=>lii(x)&&x.n=='patterns'
module_is  =x=>lii(x)&&x.n=='module'
widget_is  =x=>lii(x)&&x.n in {button:1,field:1,grid:1,slider:1,canvas:1}
ikey  =(x,k)=>lis(x)&&x.v==k
ifield=(x,k)  =>x.f(x,lms(k))
iindex=(x,k,v)=>x.f(x,lmn(k),v)
iwrite=(x,k,v)=>x.f(x,k,v)
init_field=(dst,key,src)=>{const k=lms(key),v=dget(src,k);if(v)iwrite(dst,k,v)}
normalize_enum=(x,v)=>v in x?v:Object.keys(x)[0]
normalize_font=(x,v)=>ls(dkey(x,v)||x.k[dkix(x,v)]||lms('body'))
data_enc=x=>x[5]==undefined?-1:+x[5]
data_read=(type,x)=>(x.slice(0,2)!='%%'||x.slice(2,5)!=type)?null:new Uint8Array(atob(x.slice(6)).split('').map(x=>x.charCodeAt(0)))
data_write=(type,x)=>`%%${type}${btoa(Array.from(x).map(x=>String.fromCharCode(x)).join(''))}`
has_parent=x=>card_is(x)?card_is(ifield(x,'parent')): has_parent(x.card)
is_rooted=x=>card_is(x)?!x.dead: widget_is(x)?(is_rooted(x.card)&&!x.dead): 1
card_inherit=(self,key)=>!lii(self)?null: self[ls(key)]||card_inherit(ifield(self,'parent'),key)
widget_inherit=(self,key,def)=>{
 if(card_is(self))return card_inherit(self,key);if(!lii(self))return def;if(key in self)return self[key]
 const p=ifield(self.card,'parent');return lii(p)?widget_inherit(dget(p.widgets,ifield(self,'name')),key,def):def
}

ceil=Math.ceil, clamp=(a,x,b)=>x<a?a:x>b?b:x, sign=x=>x>0?1:-1
last=x=>x[x.length-1]
rect=(x,y,w,h)=>({x:x||0,y:y||0,w:w||0,h:h||0})
rpair=(a,b)=>rect(a.x,a.y,b.x,b.y)
rcopy=r=>rect(r.x,r.y,r.w,r.h)
rint=r=>rect(0|r.x,0|r.y,0|r.w,0|r.h)
radd=(a,b)=>rect(a.x+b.x,a.y+b.y,a.w+b.w,a.h+b.h)
rsub=(a,b)=>rect(a.x-b.x,a.y-b.y,a.w-b.w,a.h-b.h)
rmul=(a,n)=>rect(a.x*n,a.y*n,a.w*n,a.h*n)
inset=(r,n)=>rect(r.x+n,r.y+n,r.w-2*n,r.h-2*n)
rin=(r,p)=>p.x>=r.x&&p.y>=r.y&&p.x<r.x+r.w&&p.y<r.y+r.h           // point-in-rect
ron=(a,b)=>b.x+b.w>=a.x&&b.x<=a.x+a.w&&b.y+b.h>=a.y&&b.y<=a.y+a.h // rect-overlaps-rect
requ=(a,b)=>a.x==b.x&&a.y==b.y&&a.w==b.w&&a.h==b.h
rmax=(a,b)=>rect(max(a.x,b.x),max(a.y,b.y),max(a.w,b.w),max(a.h,b.h))
rmin=(a,b)=>rect(min(a.x,b.x),min(a.y,b.y),min(a.w,b.w),min(a.h,b.h))
rclip=(a,b)=>{const c=rmax(a,b);return rect(c.x,c.y,min(a.x+a.w,b.x+b.w)-c.x,min(a.y+a.h,b.y+b.h)-c.y)}
runion=(a,b)=>{const c=rmin(a,b);return rect(c.x,c.y,max(a.x+a.w,b.x+b.w)-c.x,max(a.y+a.h,b.y+b.h)-c.y)}
rcenter=(a,b)=>rint(rect(a.x+(a.w-b.x)/2,ceil(a.y+(a.h-b.y)/2.0),b.x,b.y))
rnorm=r=>{r=rcopy(r);if(r.w<0)r.w*=-1,r.x-=r.w;if(r.h<0)r.h*=-1,r.y-=r.h;return rint(r)}
rclamp=(a,b,c)=>rmin(rmax(a,b),c)
lmpair=r=>lml([lmn(r.x),lmn(r.y)])
getpair=x=>(!x||!lil(x))?rect(): rect(x.v.length>0?ln(x.v[0]):0, x.v.length>1?ln(x.v[1]):0)
getrect=x=>!x?rect(): rect(x[0],x[1],x[2],x[3])
getimage=x=>(!x||!image_is(x))? image_make(rect()): x
ukey=(dict,name,root,original)=>{
 if(original&&match(name,original))return name
 if(name&&lis(name)&&!dget(dict,name))return name
 let i=1;while(1){let n=lms(root+(i++));if(!dget(dict,n))return n}
}
uset=(dict,name,root,x)=>(dset(dict,ukey(dict,name,root),x),x)
reorder=(dict,a,b)=>{
 b=clamp(0,b,count(dict)-1);const k=dict.k[a],v=dict.v[a]
 if(b<a){for(let z=a;z>b;z--)dict.k[z]=dict.k[z-1],dict.v[z]=dict.v[z-1]}
 else   {for(let z=a;z<b;z++)dict.k[z]=dict.k[z+1],dict.v[z]=dict.v[z+1]}
 dict.k[b]=k,dict.v[b]=v
}
anchors={top_left:0,top_center:1,top_right:2,center_left:3,center:4,center_right:5,bottom_left:6,bottom_center:7,bottom_right:8}
anchor=(r,a)=>{
 if(a==undefined)return rint(r);a=anchors[ls(a)]||0
 if(a==1||a==4||a==7)r.x-=r.w/2; if(a==2||a==5||a==8)r.x-=r.w
 if(a==3||a==4||a==5)r.y-=r.h/2; if(a==6||a==7||a==8)r.y-=r.h
 return rint(r)
}
unpack_rect=(z,size)=>{
 let s=size||frame.image.size, v=rect(0,0,s.x,s.y)
 if(z.length>=1){const a=rint(getpair(z[0])),b=rint(getpair(z[1]));if(b.x<0)a.x+=1+b.x,b.x*=-1;if(b.y<0)a.y+=1+b.y,b.y*=-1;v=rpair(a,b)}
 return anchor(v,z[2])
}
unpack_poly=z=>{
 const r=[];z.map(x=>{if(lil(x)&&x.v.every(x=>!lin(x))){ll(x).map(x=>r.push(getpair(x)))}else{r.push(getpair(x))}})
 if(r.length==1)r.push(r[0]);return r
}
readcolor=(cr,cg,cb,grayscale)=>{
 if(grayscale){
  // perceptually weighted gray: http://entropymine.com/imageworsener/grayscale/
  const rf=0.2126*Math.pow(cr,2.2), gf=0.7152*Math.pow(cg,2.2), bf=0.0722*Math.pow(cb,2.2), gg=Math.pow(rf+gf+bf,1/2.2)
  return clamp(0,0|gg,255)
 }
 let ci=0,cd=1e20;for(let c=0;c<16;c++){
  const dr=abs(((COLORS[c]>>16)&0xFF)/256.0-cr/256.0),
     dg=abs(((COLORS[c]>> 8)&0xFF)/256.0-cg/256.0),
     db=abs(((COLORS[c]    )&0xFF)/256.0-cb/256.0),
     diff=(dr*dr)+(dg*dg)+(db*db)
  if(diff<cd)ci=c,cd=diff
 }if(ci==15)return 1;return ci+32
}

let frame=null
inclip=p=>rin(frame.clip,p)
gpix=p=>frame.image.pix[p.x+p.y*frame.image.size.x]
pix=(p,v)=>frame.image.pix[p.x+p.y*frame.image.size.x]=v
pal_pat=(pal,p,x,y)=>pal[(x%8)+(8*(y%8))+(8*8*p)]
draw_pattern=(pal,pix,pos)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,pos.x,pos.y)&1
draw_hline=(x0,x1,y,pattern)=>{
 if(y<frame.clip.y||y>frame.clip.y+frame.clip.h)return
 x0=max(frame.clip.x,x0),x1=min(frame.clip.x+frame.clip.w,x1);for(let z=x0;z<x1;z++)pix(rect(z,y),pattern)
}
draw_vline=(x,y0,y1,pattern)=>{
 if(x<frame.clip.x||x>frame.clip.x+frame.clip.w)return
 y0=max(frame.clip.y,y0),y1=min(frame.clip.y+frame.clip.h,y1);for(let z=y0;z<y1;z++)pix(rect(x,z),pattern)
}
draw_rect=(r,pattern)=>{r=rclip(r,frame.clip);for(let a=r.y;a<r.y+r.h;a++)for(let b=r.x;b<r.x+r.w;b++)pix(rect(b,a),pattern)}
draw_invert_raw=(pal,r)=>{r=rclip(r,frame.clip);for(let a=r.y;a<r.y+r.h;a++)for(let b=r.x;b<r.x+r.w;b++){const h=rect(b,a);pix(h,1^draw_pattern(pal,gpix(h),h))}}
draw_icon=(p,i,pattern)=>{const s=i.size;for(let a=0;a<s.y;a++)for(let b=0;b<s.x;b++){const h=rect(p.x+b,p.y+a);if(i.pix[b+(a*s.x)]&&inclip(h))pix(h,pattern)}}
draw_line=(r,brush,pattern)=>{
 r=rint(r);const bsh=(z,x,y)=>(BRUSHES[(z*8)+y]>>(7-x))&1
 let dx=abs(r.w-r.x), dy=-abs(r.h-r.y), err=dx+dy, sx=r.x<r.w ?1:-1, sy=r.y<r.h?1:-1;while(1){
  if(brush==0){if(inclip(r))pix(r,pattern)}
  else{for(let b=0;b<8;b++)for(let a=0;a<8;a++){const h=rect(r.x+a-3,r.y+b-3);if(bsh(brush,a,b)&&inclip(h))pix(h,pattern)}}
  if(r.x==r.w&&r.y==r.h)break;let e2=err*2; if(e2>=dy)err+=dy,r.x+=sx; if(e2<=dx)err+=dx,r.y+=sy
 }
}
draw_box=(r,brush,pattern)=>{
 const size=frame.image.size
 if(r.w==0||r.h==0||!ron(r,rect(0,0,size.x,size.y)))return
 if(r.y         >=0)draw_line(rect(r.x      ,r.y      ,r.x+r.w-1,r.y      ),brush,pattern)
 if(r.y+r.h<=size.y)draw_line(rect(r.x      ,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1),brush,pattern)
 if(r.x         >=0)draw_line(rect(r.x      ,r.y      ,r.x      ,r.y+r.h-1),brush,pattern)
 if(r.x+r.w<=size.x)draw_line(rect(r.x+r.w-1,r.y      ,r.x+r.w-1,r.y+r.h-1),brush,pattern)
}
draw_lines=(poly,brush,pattern)=>{for(let z=0;z<poly.length-1;z++)draw_line(rpair(poly[z],poly[z+1]),brush,pattern)}
poly_bounds=poly=>{
 const d=rect(frame.clip.x+frame.clip.w,frame.clip.y+frame.clip.h,frame.clip.x,frame.clip.y)
 for(let z=0;z<poly.length;z++){const p=poly[z];d.x=min(d.x,p.x),d.y=min(d.y,p.y),d.w=max(d.w,p.x),d.h=max(d.h,p.y)}
 d.w-=d.x,d.h-=d.y,d.w++,d.h++;return d
}
poly_in=(poly,pos)=>{
 let r=0;for(let i=0,j=poly.length-1;i<poly.length;i++){
  if(pos.x==poly[i].x&&pos.y==poly[i].y)return 1
  if(((poly[i].y>=pos.y)!=(poly[j].y>=pos.y))&&(pos.x<=(poly[j].x-poly[i].x)*(pos.y-poly[i].y)/(poly[j].y-poly[i].y)+poly[i].x))r^=1
  j=i
 }return r
}
draw_poly=(poly,pattern)=>{
 const r=rint(rclip(frame.clip,poly_bounds(poly)))
 for(let a=0;a<=r.h;a++)for(let b=0;b<=r.w;b++){const h=rect(b+r.x,a+r.y);if(poly_in(poly,h))pix(h,pattern)}
}
draw_fill=(r,pattern,ref)=>{
 if(!inclip(r))return
 const src=ref||frame.image, visited=new Uint8Array(src.size.x*src.size.y)
 const spix=p=>src.pix[p.x+p.y*src.size.x], source=spix(r), fringe=[r]
 const OFFSETS=[rect(-1,0),rect(0,-1),rect(1,0),rect(0,1)], stride=frame.image.size.x
 while(fringe.length){
  const here=fringe.pop();if(gpix(here)==pattern)continue;pix(here,pattern)
  for(let z=0;z<4;z++){
   const there=radd(here,OFFSETS[z]),ti=there.x+there.y*stride
   if(inclip(there)&&!visited[ti]&&spix(there)==source)fringe.push(there),visited[ti]=1
  }
 }
}
draw_char=(pos,font,c,pattern)=>{
 const iw=font_w(font),ih=font_h(font);
 for(let a=0;a<ih;a++)for(let b=0;b<iw;b++)if(font_gpix(font,c,b,a)){
  const h=rect(pos.x+b,pos.y+a);if(inclip(h))pix(h,pattern)
 }
}
draw_text=(pos,text,font,pattern)=>{
 const cursor=rect(pos.x,pos.y);for(let z=0;z<text.length;z++){
  const c=text[z];
  if(c!='\n'){draw_char(cursor,font,c,pattern),cursor.x+=font_gw(font,c)+font_sw(font)}
  else{cursor.x=pos.x,cursor.y+=font_h(font)}
 }
}
layout_plaintext=(text,font,align,mx)=>{
 let layout=[],lines=[],cursor=rect(0,0), lnl=_=>(cursor.x=0,cursor.y+=1), ws=x=>x=='\n'||x==' '
 const fh=font_h(font),fs=font_sw(font)
 for(let z=0;z<text.length;z++){
  let a=z,w=text[z]=='\n'?0:(font_gw(font,text[z])+fs)
  if(!ws(text[z]))while(text[z+1]&&!ws(text[z+1]))w+=font_gw(font,text[++z])+fs // find word
  if(cursor.x+w>=mx.x&&cursor.x>0)lnl() // word won't fit this line
  for(let i=a;i<=z;i++){                 // append word to line
   const c=text[i], size=rect(c=='\n'?0:font_gw(font,c)+fs,fh)
   if(c==' '&&cursor.x==0&&layout.length>0&&!ws(last(layout).char))size.x=0 // squish lead space after a soft-wrap
   if(cursor.x+size.x>=mx.x)lnl() // hard-break overlong words
   layout.push({pos:rpair(cursor,size),line:cursor.y,char:c,font,arg:NONE})
   if(c=='\n'){lnl()}else{cursor.x+=size.x}
   if(cursor.y>=(mx.y/fh)){
    layout=layout.slice(0,max(1,layout.length-3))
    layout[layout.length-1].c=ELLIPSIS,layout[layout.length-1].pos.w=font_gw(font,ELLIPSIS)+fs
    z=text.length-1;break
   }
  }
 }
 let y=0;for(let i=0,line=0;i<layout.length;i++,line++){
  let a=i;while(i<(layout.length-1)&&(layout[i+1].pos.y==line))i++          // find bounds of line
  let h=0;for(let z=a;z<=i;z++)h=max(h,layout[z].pos.h)                     // find height of line
  let w=(a&&a==i)?0:(layout[i].pos.x+layout[i].pos.w)                       // find width of line
  let x=align==ALIGN.center?0|((mx.x-w)/2): align==ALIGN.right?(mx.x-w): 0  // justify
  lines.push({pos:rect(x,y,w,h),range:rect(a,i)})
  for(let z=a;z<=i;z++){const g=layout[z].pos;g.y=y+0|((h-g.h)/2);g.x+=x;}if(i<layout.length-1)y+=h
 }return {size:rect(mx.x,y+fh),layout,lines}
}
layout_richtext=(deck,table,font,align,width)=>{
 const layout=[],lines=[],cursor=rect(0,0), lnl=_=>(cursor.x=0,cursor.y+=1), ws=x=>x=='\n'||x==' '
 const texts=table.v.text, fonts=table.v.font, args=table.v.arg
 for(let chunk=0;chunk<texts.length;chunk++){
  const f=dget(deck.fonts,fonts[chunk])||font, fh=font_h(f), fs=font_sw(f)
  if(image_is(args[chunk])){
   const size=args[chunk].size;if(cursor.x+size.x>=width&&cursor.x>0)lnl()
   layout.push({pos:rpair(cursor,size),line:cursor.y,char:'i',font:f,arg:args[chunk]})
   cursor.x+=size.x;continue
  }
  const t=ls(texts[chunk]);for(let z=0;z<t.length;z++){
   let a=z, w=t[z]=='\n'?0:(font_gw(f,t[z])+fs)
   if(!ws(t[z]))while(z+1<t.length&&!ws(t[z+1]))w+=font_gw(f,t[++z])+fs
   if(cursor.x+w>=width&&cursor.x>0)lnl()
   for(let i=a;i<=z;i++){
    const c=t[i], size=rect(c=='\n'?0:font_gw(f,c)+fs,fh)
    if(c==' '&&cursor.x==0&&layout.length>0&&!ws(last(layout).char))size.x=0
    if(cursor.x+size.x>=width)lnl()
    layout.push({pos:rpair(cursor,size),line:cursor.y,char:c,font:f,arg:args[chunk]})
    if(c=='\n'){lnl()}else{cursor.x+=size.x}
   }
  }
 }
 let y=0;for(let i=0,line=0;i<layout.length;i++,line++){
  let a=i;while(i<(layout.length-1)&&(layout[i+1].pos.y==line))i++            // find bounds of line
  let h=0;for(let z=a;z<=i;z++)h=max(h,layout[z].pos.h)                       // find height of line
  let w=(a&&a==i)?0:(layout[i].pos.x+layout[i].pos.w)                         // find width of line
  let x=align==ALIGN.center?0|((width-w)/2): align==ALIGN.right?(width-w): 0  // justify
  lines.push({pos:rect(x,y,w,h),range:rect(a,i)})
  for(let z=a;z<=i;z++){const g=layout[z].pos;g.y=y+0|((h-g.h)/2);g.x+=x;}y+=h
 }return {size:rect(width,y),layout,lines}
}
draw_text_wrap=(r,l,pattern)=>{
 r=rint(r);const oc=frame.clip;frame.clip=r;for(let z=0;z<l.layout.length;z++){
  const g=l.layout[z];if(g.pos.w<1)continue
  draw_char(radd(g.pos,r),g.font,g.char,pattern)
 }frame.clip=oc;
}
draw_text_rich=(r,l,pattern,opaque)=>{
 const oc=frame.clip;frame.clip=r;for(let z=0;z<l.layout.length;z++){
  const g=l.layout[z];if(g.pos.w<1)continue
  if(g.pos.y+g.pos.h<0||g.pos.y>r.h)continue; g.pos.x+=r.x, g.pos.y+=r.y
  if(lis(g.arg)&&count(g.arg))draw_hline(g.pos.x,g.pos.x+g.pos.w,g.pos.y+g.pos.h-1,19)
  if(image_is(g.arg)){image_paste(g.pos,frame.clip,g.arg,frame.image,opaque)}
  else{draw_char(g.pos,g.font,g.char,pattern)}
 }frame.clip=oc
}

pointer={f:(self,i,x)=>{
 if(ikey(i,'held' ))return lmn(self.held)
 if(ikey(i,'pos'  ))return lmpair(self.pos)
 if(ikey(i,'start'))return lmpair(self.start)
 if(ikey(i,'prev' ))return lmpair(self.prev)
 if(ikey(i,'end'  ))return lmpair(self.end)
 return x?x:NONE
},t:'int',n:'pointer',held:0,pos:rect(),start:rect(),prev:rect(),end:rect()}

keystore_read=x=>{
 let store=lmd();if(x)x.k.filter((k,i)=>!match(NONE,x.v[i])).map((k,i)=>dset(store,k,x.v[i]))
 return {f:(self,i,x)=>{
  i=ls(i);if(i=='keys')return monad.range(self.data)
  if(x){
   const f=lms('%j'),val=dyad.parse(f,dyad.format(f,x))
   if(match(NONE,val)){self.data=dyad.drop(lms(i),self.data)}else{dset(self.data,lms(i),val)}
   return x
  }else{return dget(self.data,lms(i))||NONE}
 },t:'int',n:'keystore',data:store}
}

module_read=(x,deck)=>{
 const ri=lmi((self,i,x)=>{
  if(x){
   if(ikey(i,'description'))return self.description=ls(x),x
   if(ikey(i,'name')){
    if(ls(x)=='')return x
    self.name=ls(ukey(self.deck.modules,lms(ls(x)),ls(x),lms(self.name)))
    self.deck.modules.k[dvix(self.deck.modules,self)]=self.name
    return x
   }
   if(ikey(i,'script')){
    self.script=ls(x),self.error='',self.value=lmd();try{
     const prog=parse(self.script),root=lmenv();primitives(root,deck),constants(root),root.local('data',self.data)
     pushstate(root),issue(root,prog);let q=MODULE_QUOTA;while(running()&&q>0)runop(),q--
     if(running()){self.error='initialization took too long.'}
     else{self.value=ld(arg())};popstate()
    }catch(e){self.error=e.x;return x}
   }
  }else{
   if(ikey(i,'name'       ))return lms(self.name)
   if(ikey(i,'data'       ))return self.data
   if(ikey(i,'description'))return lms(self.description||'')
   if(ikey(i,'script'     ))return lms(self.script||'')
   if(ikey(i,'error'      ))return lms(self.error||'')
   if(ikey(i,'value'      ))return self.value
  }return x?x:NONE
 },'module')
 const n=dget(x,lms('name'))
 ri.deck=deck
 ri.name=ls(ukey(deck.modules,n&&lis(n)&&count(n)==0?null:n,'module'))
 ri.data=keystore_read(dget(x,lms('data')))
 ri.value=lmd()
 init_field(ri,'description',x)
 init_field(ri,'script',x)
 return ri
}
module_write=x=>{
 const r=lmd()
 dset(r,lms('name'  ),ifield(x,'name'))
 dset(r,lms('data'  ),x.data.data)
 dset(r,lms('script'),ifield(x,'script'))
 if(x.description)dset(r,lms('description'),ifield(x,'description'))
 return r
}

const casts={u8:1,i8:1,u16b:2,u16l:2,i16b:2,i16l:2,u32b:4,u32l:4,i32b:4,i32l:4,char:1}
array_write=x=>data_write('DAT'+String.fromCharCode(48+Object.keys(casts).indexOf(x.cast)),x.data.slice(x.base,x.base+x.size))
array_read=x=>{const f=x.charCodeAt(5),d=data_read('DAT',x);return d?array_make(d.length,Object.keys(casts)[clamp(0,f-48,10)],0,d):array_make(0,'u8',0)}
n_array=([x,y])=>{if(lis(x))return array_read(ls(x));const size=ln(x),cast=y?normalize_enum(casts,ls(y)):'u8';return array_make(size,cast,0)}
array_make=(size,cast,base,buffer)=>{
 const offset=x=>({offset:ln(lil(x)?monad.first(x):x),len:lil(x)?max(0,ln(monad.last(x))):-1})
 const shift=(a,here)=>({here:0, size:a.size-here, base:a.base+here, cast:a.cast, data:a.data})
 const resize=(a,size)=>{
  if(a.slice)return;size=max(0,size);const old=a.data;a.data=new Uint8Array(size)
  for(let z=0;z<a.data.length;z++)a.data[z]=z>=old.length?0:old[z];a.size=size
 }
 const get_raw=(a,index)=>{
  const step=casts[a.cast];if(index<0||index>=(0|(a.size/step)))return 0
  const ix=a.base+step*index;if(ix<0||ix+step>a.data.length)return 0
  const ur=(i,s)=>a.data[ix+i]<<s
  const s8 =x=>x<<24>>24
  const s16=x=>x<<16>>16
  const u32=(a,b,c,d)=>(2*a)+(b|c|d)
  if(a.cast=='u8'  )return     ur(0,0)
  if(a.cast=='i8'  )return  s8(ur(0,0))
  if(a.cast=='u16b')return    (ur(0,8)|ur(1,0))
  if(a.cast=='u16l')return    (ur(1,8)|ur(0,0))
  if(a.cast=='i16b')return s16(ur(0,8)|ur(1,0))
  if(a.cast=='i16l')return s16(ur(1,8)|ur(0,0))
  if(a.cast=='u32b')return u32(ur(0,23),ur(1,16),ur(2,8),ur(3,0))
  if(a.cast=='u32l')return u32(ur(3,23),ur(2,16),ur(1,8),ur(0,0))
  if(a.cast=='i32b')return    (ur(0,24)|ur(1,16)|ur(2,8)|ur(3,0))
  if(a.cast=='i32l')return    (ur(3,24)|ur(2,16)|ur(1,8)|ur(0,0))
  return String.fromCharCode(ur(0,0))
 }
 const set_raw=(a,index,v)=>{
  if('string'==typeof v)v=v.charCodeAt(0)
  const step=casts[a.cast];if(index<0||index>=(0|(a.size/step)))return
  const ix=a.base+step*index;if(ix<0||ix+step>a.data.length)return
  const uw=(i,s)=>a.data[ix+i]=v>>>s
  if     (a.cast=='u16b'||a.cast=='i16b')uw(0,8),uw(1,0)
  else if(a.cast=='u16l'||a.cast=='i16l')uw(1,8),uw(0,0)
  else if(a.cast=='u32b'||a.cast=='i32b')uw(0,24),uw(1,16),uw(2,8),uw(3,0)
  else if(a.cast=='u32l'||a.cast=='i32l')uw(3,24),uw(2,16),uw(1,8),uw(0,0)
  else uw(0,0)
 }
 const get=(a,index,len)=>{
  if(a.cast=='char'&&len<0)len=1
  if(a.cast=='char'){let r='';for(let z=0;z<len;z++)r+=get_raw(a,index+z);return lms(clchars(r))}
  return len<0?lmn(get_raw(a,index)): lml(range(len).map(x=>lmn(get_raw(a,index+x))))
 }
 const set=(a,index,len,v)=>{
  if(len<0)len=1
  if(array_is(v)){for(let z=0;z<len;z++)set_raw(a,index+z,array_get_raw(b,z))}       // array copy
  else if(lis(v)){for(let z=0;z<len;z++)set_raw(a,index+z,z>=count(v)?0:v.v[z])}     // copy chars up to len
  else if(lil(v)){for(let z=0;z<len;z++)set_raw(a,index+z,z>=count(v)?0:ln(v.v[z]))} // copy numbers up to len
  else{const vv=ln(v);for(let z=0;z<len;z++)set_raw(a,index+z,vv)}                   // spread a number up to len
 }
 const slice=(a,z)=>{
  const o=offset(z[0]||NONE),cast=z[1]?normalize_enum(casts,ls(z[1])):a.cast, step=casts[cast];o.offset*=step
  if(o.len<0)o.len=0|((a.size-o.offset)/step);const r=array_make(o.len,cast,o.offset,a.data);r.slice=1;return r
 }
 const copy=(a,z)=>{
  const o=offset(z[0]||NONE),cast=z[1]?normalize_enum(casts,ls(z[1])):a.cast, step=casts[cast];o.offset*=step
  if(o.len<0)o.len=0|((src.size-o.offset)/step);const r=array_make(o.len,cast,0)
  for(let z=0;z<o.len;z++)set_raw(r,z,get_raw(shift(a,o.offset),z));return r
 }
 const struct_size=shape=>{
  if( lis(shape))return (casts[ls(shape)]||1)
  if( lil(shape))return (casts[ls(monad.first(shape))]||1)*max(0,ln(monad.last(shape)))
  if(!lid(shape))return 0
  let bit=0, r=0;shape.v.map(type=>{
   if(!lin(type)&&bit)bit=0,r++
   if(lin(type)){bit+=clamp(1,ln(type),31),r+=0|(bit/8),bit%=8}else{r+=struct_size(type)}
  });return r
 }
 const struct_read=(a,shape)=>{
  if(lis(shape)){a.cast=normalize_enum(casts,ls(shape));const r=get(shift(a,a.here),0,-1);a.here+=casts[a.cast];return r}
  if(lil(shape)){
   const n=max(0,ln(monad.last(shape)))
   a.cast=normalize_enum(casts,ls(monad.first(shape)));const r=get(shift(a,a.here),0,n);a.here+=n*casts[a.cast];return r
  }
  if(!lid(shape))return NONE;let bit=0,r=lmd();shape.v.map((type,i)=>{
   let v=NONE;if(!lin(type)&&bit)bit=0,a.here++
   if(lin(type)){
    let n=clamp(1,ln(type),31),t=0;a.cast='u8'
    while(n>0){t=(t<<1)|(1&(get_raw(a,a.here)>>(7-bit))),bit++,n--;if(bit==8)bit=0,a.here++}v=lmn(t)
   }else{v=struct_read(a,type)}dset(r,shape.k[i],v)
  });return r
 }
 const struct_write=(a,shape,value)=>{
  if(lis(shape)||lil(shape)){
   let n=lis(shape)?1:max(0,ln(monad.last(shape)));a.cast=normalize_enum(casts,ls(lis(shape)?shape:monad.first(shape)))
   set(shift(a,a.here),0,n,value),a.here+=n*casts[a.cast];return
  }if(!lid(shape))return
  let bit=0;shape.v.map((type,i)=>{
   let v=dget(value,shape.k[i])||NONE
   if(!lin(type)){if(bit)bit=0,a.here++;struct_write(a,type,v);return}
   let n=clamp(1,ln(type),31), t=ln(v),m=(1<<n)-1;t&=m,a.cast='u8';for(let z=0;z<n;z++){
    let pos=1<<(7-bit),dst=get_raw(a,a.here)&~pos
    set_raw(a,a.here,t&(1<<(n-1-z))?dst|pos:dst),bit++;if(bit==8)bit=0,a.here++
   }
  })
 }
 const struct=(a,z)=>{
  const oc=a.cast, shape=z[0]||NONE, value=z[1], size=struct_size(shape);if(value&&a.here+size>=a.size)resize(a,a.here+size)
  const r=value?(struct_write(a,shape,value),value):struct_read(a,shape);return a.cast=oc,r
 }
 const ri=lmi((self,i,x)=>{
  if(!lis(i)){const o=offset(i);if(x){set(self,o.offset,o.len,x);return x;}else{return get(self,o.offset,o.len);}}
  if(x){
   if(ikey(i,'size'))return resize(self,ln(x)*casts[self.cast]),x
   if(ikey(i,'cast'))return self.cast=normalize_enum(casts,ls(x)),x
   if(ikey(i,'here'))return self.here=max(0,ln(x)),x
  }else{
   if(ikey(i,'encoded'))return lms(array_write(self))
   if(ikey(i,'cast'   ))return lms(self.cast)
   if(ikey(i,'size'   ))return lmn(self.size/casts[self.cast])
   if(ikey(i,'here'   ))return lmn(self.here)
   if(ikey(i,'slice'  ))return lmnat(z=>slice (self,z))
   if(ikey(i,'copy'   ))return lmnat(z=>copy  (self,z))
   if(ikey(i,'struct' ))return lmnat(z=>struct(self,z))
  }return x?x:NONE
 },'array')
 ri.size=size*casts[cast],ri.here=0,ri.base=base,ri.cast=cast,ri.data=buffer||new Uint8Array(ri.size)
 return ri
}

image_copy=(i,r)=>{
 r=r?rint(r):rect(0,0,i.size.x,i.size.y);const c=image_make(rect(r.w,r.h)), clip=rect(0,0,i.size.x,i.size.y)
 for(let y=0;y<r.h;y++)for(let x=0;x<r.w;x++)c.pix[x+r.w*y]=rin(clip,rect(r.x+x,r.y+y))?i.pix[(r.x+x)+i.size.x*(r.y+y)]:0
 return c
}
image_paste=(r,clip,src,dst,opaque)=>{
 r=rint(r);const s=src.size,ds=dst.size
 for(let y=0;y<s.y;y++)for(let x=0;x<s.x;x++)if(rin(clip,rect(r.x+x,r.y+y))&&(opaque||src.pix[x+s.x*y]))dst.pix[r.x+x+ds.x*(r.y+y)]=src.pix[x+s.x*y]
}
image_paste_scaled=(r,clip,src,dst,opaque)=>{
 r=rint(r);if(r.w==0||r.h==0)return;const s=src.size,ds=dst.size;if(r.w==s.x&&r.h==s.y)return image_paste(r,clip,src,dst,opaque)
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  let sx=0|((b*1.0)/r.w)*s.x, sy=0|((a*1.0)/r.h)*s.y, c=src.pix[sx+sy*s.x]
  if((opaque||c!=0)&&rin(clip,rect(r.x+b,r.y+a)))dst.pix[r.x+b+ds.x*(r.y+a)]=c
 }
}
image_dither=i=>{
 const stride=2*i.size.x, m=[0,1,i.size.x-2,i.size.x-1,i.size.x,stride-1], e=new Float32Array(stride)
 for(let ei=0,z=0;z<i.pix.length;z++){
  const pix=((0xFF&i.pix[z])/256.0)+e[ei], col=pix>.5?1:0, err=(pix-col)/8.0
  e[ei]=0, ei=(ei+1)%stride; for(let x=0;x<6;x++)e[(ei+m[x])%stride]+=err; i.pix[z]=!col
 }
}
image_flip_h=i=>{const s=i.size;for(let z=0;z<s.y;z++){let a=z*s.x,b=(z+1)*s.x-1;while(a<b){let t=i.pix[a];i.pix[a]=i.pix[b];i.pix[b]=t;a++;b--}}}
image_flip_v=i=>{const s=i.size;for(let z=0;z<s.x;z++){let a=z,b=z+s.x*(s.y-1);while(a<b){let t=i.pix[a];i.pix[a]=i.pix[b];i.pix[b]=t;a+=s.x;b-=s.x}}}
image_flip=i=>{const s=i.size,r=image_make(rect(s.y,s.x));for(let a=0;a<s.y;a++)for(let b=0;b<s.x;b++)r.pix[a+s.y*b]=i.pix[b+s.x*a];i.pix=r.pix,i.size=r.size}
image_resize=(i,size)=>{
 const os=i.size,ob=i.pix;size=rint(rmax(size,rect()));if(requ(os,size))return i
 i.pix=new Uint8Array(size.x*size.y),i.size=size;
 for(let a=0;a<size.y;a++)for(let b=0;b<size.x;b++)i.pix[b+a*size.x]=a>=os.y||b>=os.x?0: ob[b+a*os.x];return i
}
image_make=size=>{
 const f=(self,i,x)=>{
  const s=self.size
  if(i&&lil(i)){ // read/write pixels
   const p=getpair(i),ib=p.x>=0&&p.y>=0&&p.x<s.x&&p.y<s.y
   if(x){if(ib)self.pix[p.x+p.y*s.x]=ln(x);return x}
   return ib?lmn(self.pix[p.x+p.y*s.x]):NONE
  }
  if(ikey(i,'encoded'))return lms(image_write(self))
  if(ikey(i,'size'))return x?(image_resize(self,getpair(x)),x): lmpair(self.size)
  if(ikey(i,'map'))return lmnat(([x,fill])=>{
   const m=new Uint8Array(256);for(let z=0;z<256;z++)m[z]=fill?ln(fill):z;x=ld(x)
   for(let z=0;z<x.k.length;z++)m[0xFF&ln(x.k[z])]=0xFF&ln(x.v[z])
   for(let z=0;z<self.pix.length;z++)self.pix[z]=m[self.pix[z]]
   return self
  })
  if(ikey(i,'transform'))return lmnat(([x])=>{
   if(x.v=='horiz')image_flip_h(self); if(x.v=='vert')image_flip_v(self); if(x.v=='flip')image_flip(self); if(x.v=='dither')image_dither(self)
   if(x.v=='left' )image_flip_h(self),image_flip(self); if(x.v=='right')image_flip(self),image_flip_h(self)
   return self
  })
  if(ikey(i,'copy'))return lmnat(z=>image_copy(self,unpack_rect(z,self.size)))
  if(ikey(i,'paste'))return lmnat(([img,pos,t])=>{
   img=getimage(img), pos=(pos?ll(pos):[]).map(ln); let solid=t?!lb(t):1, cl=rect(0,0,self.size.x,self.size.y); if(img==self)img=image_copy(img)
   image_paste_scaled(pos.length<=2?rect(pos[0],pos[1],img.size.x,img.size.y):getrect(pos),cl,img,self,solid)
  })
  return x?x:NONE
 };return {t:'int',f:f,n:'image',size:size,pix:new Uint8Array(size.x*size.y)}
}
image_read=x=>{
 const data=data_read('IMG',x);if(!data||data.length<4)return image_make(rect())
 const f=data_enc(x), w=(data[0]<<8)|data[1], h=(data[2]<<8)|data[3], r=image_make(rect(w,h))
 if(f==0&&data.length-4>=w*h/8){let s=ceil(w/8),o=0;for(let a=0;a<h;a++)for(let b=0;b<w;b++)r.pix[o++]=data[4+(0|b/8)+a*s]&(1<<(7-(b%8)))?1:0}
 if(f==1&&data.length-4>=w*h){r.pix=data.slice(4)}
 if(f==2){let i=4,o=0;while(i+2<=data.length){let p=data[i++],c=0xFF&data[i++];while(c&&o+1<=r.pix.length)c--,r.pix[o++]=p;}}
 return r
}
image_write=x=>{
 x=image_is(x)?x:image_make(rect());let f=0,s=x.size,t=[0xFF&(s.x>>8),0xFF&s.x,0xFF&(s.y>>8),0xFF&s.y],l=t.slice(0)
 for(let z=0;z<x.pix.length;){let c=0,p=x.pix[z];while(c<255&&z<x.pix.length&&x.pix[z]==p)c++,z++;l.push(p),l.push(c)}
 if(!x.pix.some(x=>x>1)&&(l.length>4+s.x*s.y/8)){
  f='0';let stride=8*ceil(s.x/8);for(let a=0;a<s.y;a++)for(let b=0;b<stride;b+=8)
  {let v=0;for(let i=0;i<8;i++)v=(v<<1)|(b+i>=s.x?0: x.pix[b+i+a*s.x]?1:0);t.push(v)}
 }else if(l.length>4+s.x*s.y){f='1';for(let z=0;z<s.x*s.y;z++)t.push(x.pix[z])}else{f='2',t=l}
 return data_write('IMG'+f,t)
}
n_image=([size])=>lis(size)?image_read(ls(size)):image_make(getpair(size))

sound_make=data=>{
 const sign_extend=x=>(x<<24>>24)
 const ri=lmi((self,i,x)=>{
  if(i&&lin(i)){ // read/write single samples
   return x?((self.data[ln(i)]=0xFF&ln(x)),x):lmn(sign_extend(self.data[ln(i)]))
  }
  if(i&&lil(i)){ // read/write ranges
   const n=getpair(i);n.y=max(0,n.y);if(x){
    const s=ll(x),dc=self.data.length,sc=s.length, r=new Uint8Array(clamp(0,(dc-n.y)+sc,10*SFX_RATE))
    for(let z=0;z<n.x         ;z++)r[z]=self.data[z]
    for(let z=0;z<sc          ;z++)r[n.x+z   ]=0xFF&ln(s[z])
    for(let z=0;z<dc-(n.x+n.y);z++)r[n.x+sc+z]=0xFF&self.data[n.x+n.y+z]
    return self.data=r,x
   }else{return lml(range(n.y).map(x=>lmn(sign_extend(self.data[x+n.x]))))}
  }
  if(ikey(i,'encoded'))return lms(sound_write(self))
  if(ikey(i,'size')){
   if(!x)return lmn(self.data.length)
   const n=clamp(0,ln(x),10*SFX_RATE),o=self.data;self.data=new Uint8Array(n)
   for(let z=0;z<o.length&&z<n;z++)self.data[z]=o[z];return x
  }
  if(ikey(i,'duration'))return lmn(self.data.length/SFX_RATE)
  return x?x:NONE
 },'sound')
 if(data&&data.length>10*SFX_RATE)data=data.slice(0,10*SFX_RATE)
 ri.data=data||new Uint8Array(0)
 return ri
}
sound_read=x=>sound_make((typeof x=='string')?data_read('SND',x):new Uint8Array(clamp(0,+x,10*SFX_RATE)))
sound_write=x=>data_write('SND0',x.data)
n_sound=([x])=>!x?sound_read(0): lis(x)?sound_read(ls(x)): lin(x)?sound_read(ln(x)): sound_make(Uint8Array.from(ll(x).map(ln)))

patterns_read=x=>{
 const set=(pal,p,x,y,v)=>pal[(x%8)+(8*(y%8))+(8*8*p)]=v
 const ri=lmi((self,i,x)=>{
  let r=null, t=i&&ln(i)?ln(i):0
  if(x){
   if(t>= 2&&t<=27&&image_is(x)){for(let a=0;a<8;a++)for(let b=0;b<8;b++)set(self.pal.pix,t,b,a,lb(iwrite(x,lmpair(rect(b,a)))))}
   if(t>=28&&t<=31){r=ll(x);if(r.length>8)r=r.slice(0,8);self.anim[t-28]=r.map(x=>{const f=clamp(0,ln(x),47);return f>=28&&f<=31?0:f});r=lml(r)}
   if(t>=32&&t<=47){COLORS[t-32]=0xFF000000|ln(x);r=x}
  }else{
   if(t>= 0&&t<=27){r=image_copy(self.pal,rect(0,t*8,8,8))}
   if(t>=28&&t<=31){r=lml(self.anim[t-28].map(lmn))}
   if(t>=32&&t<=47){r=lmn(0xFFFFFF&COLORS[t-32])}
  }return r?r:x?x:NONE
 },'patterns')
 ri.pal=image_resize(image_read(x.patterns?ls(x.patterns):DEFAULT_PATTERNS),rect(8,8*32))
 ri.anim=JSON.parse(DEFAULT_ANIMS);if(x.animations&&lil(x.animations))ll(x.animations).map((x,i)=>iindex(ri,28+i,x))
 return ri
}
patterns_write=x=>image_write(image_resize(image_copy(x.pal),rect(8,224)))
anims_write=x=>lml(x.anim.map(x=>lml(x.map(lmn))))

font_get=(i,f,v)=>{if(v!=undefined)f.pix[i]=v;return f.pix[i]}
font_w =(f,v)=>font_get(0,f,v)
font_h =(f,v)=>font_get(1,f,v)
font_sw=(f,v)=>font_get(2,f,v)
font_gs=(f,v)=>font_h(f)*ceil(font_w(f)/8)+1
font_gb=(f,c)=>3+(c.charCodeAt(0)-32)*font_gs(f)
font_gw=(f,c,v)=>font_get(font_gb(f,c),f,v)
font_pp=(f,c,x,y,v)=>font_get(font_gb(f,c)+1+y*ceil(font_w(f)/8)+Math.floor(x/8),f,v)
font_bit=(x,v)=>(v<<(7-(x%8)))
font_gpix=(f,c,x,y)=>((font_pp(f,c,x,y)&font_bit(x,1))?1:0)
font_spix=(f,c,x,y,v)=>font_pp(f,c,x,y,(font_pp(f,c,x,y)&~font_bit(x,1))|font_bit(x,v))
font_textsize=(f,t)=>{
 const cursor=rect(),size=rect(0,font_h(f))
 for(let z=0;t[z];z++){if(t[z]!='\n'){cursor.x+=font_gw(f,t[z])+font_sw(f),size.x=max(size.x,cursor.x)}else{cursor.x=0,size.y+=font_h(f)}}
 return size
}
font_read=s=>{
 const ri=lmi((self,i,x)=>{
  if(lin(i)||(lis(i)&&count(i)==1)){ // read/write glyphs
   let ix=lin(i)?ln(i): ls(i).charCodeAt(0)-32, c=String.fromCharCode(ix+32)
   if(x){
    if(!image_is(x))return x;font_gw(self,c,min(x.size.x,font_w(self)));const s=rect(font_gw(self,c),font_h(self))
    for(let a=s.y-1;a>=0;a--)for(let b=s.x-1;b>=0;b--)font_spix(self,c,b,a, b>=(x.size.x||a>=x.size.y)?0:x.pix[b+a*x.size.x]?1:0)
    return x
   }
   if(ix<0||ix>95)return image_make(rect())
   const s=rect(font_gw(self,c),font_h(self)),r=image_make(s)
   for(let a=s.y-1;a>=0;a--)for(let b=s.x-1;b>=0;b--)r.pix[b+a*s.x]=font_gpix(self,c,b,a)
   return r
  }
  if(x){
   if(ikey(i,'space'))return font_sw(self,ln(x)),x
   if(ikey(i,'size')){
    const r=font_read(getpair(x));iwrite(r,lms('space'),ifield(self,'space'))
    for(let z=0;z<96;z++)iindex(r,z,iindex(self,z));self.pix=r.pix;return x
   }
  }else{
   if(ikey(i,'size'))return lmpair(rect(font_w(self),font_h(self)))
   if(ikey(i,'space'))return lmn(font_sw(self))
   if(ikey(i,'textsize'))return lmnat(([x])=>lmpair(font_textsize(self,x?ls(x):'')))
  }return x?x:NONE
 },'font')
 if(typeof s=='string')ri.pix=data_read('FNT',s)
 if(!ri.pix){ri.pix=new Uint8Array(3+96*(1+s.y*ceil(s.x/8.0)));font_w(ri,s.x),font_h(ri,s.y),font_sw(ri,1)}
 return ri
}
font_write=x=>data_write('FNT0',x.pix)

rtext_len=tab=>tab.v.text.reduce((x,y)=>x+ls(y).length,0)
rtext_get=(tab,n)=>{let i=0;for(let z=0;z<tab.v.text.length;z++){i+=count(tab.v.text[z]);if(i>=n)return z}return -1}
rtext_getr=(tab,x)=>{let i=0;for(let z=0;z<tab.v.text.length;z++){const c=count(tab.v.text[z]);if(i+c>=x)return rect(i,i+c);i+=c}return rect(x,x)}
rtext_make=(t,f,a)=>{
 a=!a?'':image_is(a)?a:ls(a), f=!f?'':ls(f), t=image_is(a)?'i':!t?'':count(t)?ls(t):''
 return lmt({text:[lms(t)],font:[lms(f)],arg:[image_is(a)?a:lms(a)]})
}
rtext_cast=x=>{
 if(!x)x=lms('');if(lid(x))x=monad.table(x);if(!lit(x))return rtext_make(x)
 if(x.v.text&&x.v.font&&x.v.arg&&x.v.text.every((t,i)=>lis(t)&&lis(x.v.font[i])&&(image_is(x.v.arg[i])||lis(x.v.arg[i]))))return x
 let r={text:lml(x.v.text||[lms('')]),font:lml(x.v.font||[lms('')]),arg:lml(x.v.arg||[lms('')])};torect(r)
 r.text.map((_,z)=>{let i=image_is(r.arg[z]);r.text[z]=i?lms('i'):lms(ls(r.text[z])),r.font[z]=lms(ls(r.font[z])),r.arg[z]=i?r.arg[z]:lms(ls(r.arg[z]))})
 return lmt(r)
}
rtext_append=(tab,t,f,a)=>{
 if(image_is(a)){if(count(t)>1)t=lms('i');if(count(t)<1)return 0;}if(!count(t))return 0;
 if(tab.v.text.length&&match(f,last(tab.v.font))&&!image_is(a)&&match(a,last(tab.v.arg))){tab.v.text[tab.v.text.length-1]=lms(last(tab.v.text).v+t.v)}
 else{tab.v.text.push(t),tab.v.font.push(f),tab.v.arg.push(a)};return count(t)
}
rtext_appendr=(tab,row)=>{row.v.text.map((t,i)=>rtext_append(tab,t,row.v.font[i],row.v.arg[i]))}
rtext_string=(tab,pos)=>{
 pos=pos||rect(0,RTEXT_END);let r='',i=0,a=min(pos.x,pos.y),b=max(pos.x,pos.y)
 tab.v.text.map(s=>{for(let z=0;z<s.v.length;z++,i++)if(i>=a&&i<b)r+=s.v[z]});return lms(r)
}
rtext_is_plain=x=>{
 if(!lit(x)||!x.v.text||!x.v.font||!x.v.arg||x.v.text.length>1)return 0;if(x.v.text.length==0)return 1
 return ls(x.v.font[0])==''&&!image_is(x.v.arg[0])&&ls(x.v.arg[0])==''
}
rtext_is_image=x=>{
 let r=null,t=x.v.text,a=x.v.arg; // look for at least one image, and other spans must be only whitespace.
 for(let z=0;z<count(x);z++){if(image_is(a[z])){if(!r)r=a[z]}else if(ls(t[z]).trim()!=''){return null}}
 return r
}
rtext_span=(tab,pos)=>{
 let r=dyad.take(NONE,tab), i=0,c=0,a=min(pos.x,pos.y),b=max(pos.x,pos.y), partial=_=>{
  let rr='';for(let z=0;z<count(tab.v.text[c]);z++,i++)if(i>=a&&i<b)rr+=tab.v.text[c].v[z]
  rtext_append(r,lms(rr),tab.v.font[c],tab.v.arg[c]),c++
 }
 while(c<tab.v.text.length&&(i+count(tab.v.text[c]))<a)i+=count(tab.v.text[c++])                                      ;if(c<tab.v.text.length&&i<=a)partial()
 while(c<tab.v.text.length&&(i+count(tab.v.text[c]))<b)i+=rtext_append(r,tab.v.text[c],tab.v.font[c],tab.v.arg[c]),c++;if(c<tab.v.text.length&&i< b)partial()
 return r
}
rtext_splice=(tab,font,arg,text,cursor,endcursor)=>{
 const a=min(cursor.x,cursor.y),b=max(cursor.x,cursor.y),r=rtext_cast()
 rtext_appendr(r,rtext_span(tab,rect(0,a)))
 rtext_append (r,lms(text),font,arg)
 rtext_appendr(r,rtext_span(tab,rect(b,RTEXT_END)))
 endcursor.x=endcursor.y=a+text.length;return r
}
rtext_write=x=>{const r=monad.cols(x),arg=dget(r,lms('arg'));if(arg){arg.v=arg.v.map(x=>image_is(x)?lms(image_write(x)):x)};return r}
interface_rtext=lmi((self,i,x)=>{
 if(ikey(i,'end'   ))return lmn(RTEXT_END)
 if(ikey(i,'make'  ))return lmnat(([t,f,a])=>rtext_make(t,f,a))
 if(ikey(i,'len'   ))return lmnat(([t])=>lmn(rtext_len(rtext_cast(t))))
 if(ikey(i,'get'   ))return lmnat(([t,n])=>lmn(rtext_get(rtext_cast(t),n?ln(n):0)))
 if(ikey(i,'string'))return lmnat(([t,i])=>rtext_string(rtext_cast(t),i?getpair(i):undefined))
 if(ikey(i,'span'  ))return lmnat(([t,i])=>rtext_span  (rtext_cast(t),i?getpair(i):undefined))
 if(ikey(i,'cat'   ))return lmnat(x=>{let r=lmt({text:[],font:[],arg:[]});x.map(x=>rtext_appendr(r,rtext_cast(x)));return r})
 return x?x:NONE
},'rtext')
button_styles={round:1,rect:1,check:1,invisible:1}
button_read=(x,card)=>{
 const ri=lmi((self,i,x)=>{
  if(!is_rooted(self))return NONE
  if(x){
   if(ikey(i,'value'))return self.value=lb(x),x
   if(has_parent(self))return x
   if(ikey(i,'text' ))return self.text=ls(x),x
   if(ikey(i,'style'))return self.style=normalize_enum(button_styles,ls(x)),x
  }else{
   if(ikey(i,'value'))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'text' ))return lms(widget_inherit(self,ls(i),''))
   if(ikey(i,'style'))return lms(widget_inherit(self,ls(i),'round'))
   if(ikey(i,'size' ))return lmpair(widget_inherit(self,ls(i),rect(60,20)))
   if(ikey(i,'click')&&state.external)return lmnat(([x])=>fire_event(self,i,NONE))
  }return interface_widget(self,i,x)
 },'button');ri.card=card
 init_field(ri,'text' ,x)
 init_field(ri,'style',x)
 init_field(ri,'value',x)
 return ri
}
button_write=x=>{
 const r=lmd([lms('type')],[lms('button')])
 if(x.text)dset(r,lms('text' ),lms(x.text))
 if(x.style&&x.style!='round')dset(r,lms('style'),lms(x.style))
 if(x.value!=undefined)dset(r,lms('value'),lmn(x.value))
 return r
}
field_styles={rich:1,plain:1,code:1}
field_aligns={left:1,center:1,right:1}
field_read=(x,card)=>{
 const ri=lmi((self,i,x)=>{
  if(!is_rooted(self))return NONE
  if(x){
   if(ikey(i,'text'  ))return self.value=rtext_cast(lms(ls(x))),x
   if(ikey(i,'scroll'))return self.scroll=max(0,ln(x)),x
   if(ikey(i,'value' )){
    if(ls(ifield(self,'style'))!='rich'&&!rtext_is_plain(x))x=rtext_string(rtext_cast(x))
    return self.value=rtext_cast(x),x
   }
   if(has_parent(self))return x
   if(ikey(i,'border'   ))return self.border=lb(x),x
   if(ikey(i,'scrollbar'))return self.scrollbar=lb(x),x
   if(ikey(i,'style'    ))return self.style=normalize_enum(field_styles,ls(x)),iwrite(self,lms('value'),ifield(self,'value')),x
   if(ikey(i,'align'    ))return self.align=normalize_enum(field_aligns,ls(x)),x
  }else{
   if(ikey(i,'text'     )){const v=widget_inherit(self,'value');return v!=undefined?rtext_string(v):lms('')}
   if(ikey(i,'border'   ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'value'    ))return widget_inherit(self,ls(i))||rtext_cast()
   if(ikey(i,'scroll'   ))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'scrollbar'))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'style'    ))return lms(widget_inherit(self,ls(i),'rich'))
   if(ikey(i,'align'    ))return lms(widget_inherit(self,ls(i),'left'))
   if(ikey(i,'size'     ))return lmpair(widget_inherit(self,ls(i),rect(100,20)))
   if(ikey(i,'font'     ))return dget(self.card.deck.fonts,lms(widget_inherit(self,ls(i))||(widget_inherit(self,'style')=='code'?'mono':'body')))
   if(ikey(i,'run'   )&&state.external)return lmnat(([x])=>fire_event(self,i,lms(ls(x))))
   if(ikey(i,'link'  )&&state.external)return lmnat(([x])=>fire_event(self,i,lms(ls(x))))
   if(ikey(i,'change')&&state.external)return lmnat(([x])=>fire_event(self,i,lms(ls(x))))
  }return interface_widget(self,i,x)
 },'field');ri.card=card
 const rtext_read=x=>{
  if(lis(x))return x;x=ld(x)
  const a=dget(x,lms('arg'));if(a){dset(x,lms('arg'),lml(ll(a).map(a=>ls(a).startsWith('%%IMG')?image_read(ls(a)):lms(ls(a)))))}
  return rtext_cast(x)
 }
 {const k=lms('value'),v=dget(x,k);if(v)iwrite(ri,k,rtext_read(v))}
 init_field(ri,'border'   ,x)
 init_field(ri,'scrollbar',x)
 init_field(ri,'style'    ,x)
 init_field(ri,'align'    ,x)
 init_field(ri,'scroll'   ,x)
 return ri
}
field_write=x=>{
 const r=lmd([lms('type')],[lms('field')])
 if(x.border!=undefined)dset(r,lms('border'),lmn(x.border))
 if(x.scrollbar!=undefined)dset(r,lms('scrollbar'),lmn(x.scrollbar))
 if(x.style&&x.style!='rich')dset(r,lms('style'),lms(x.style))
 if(x.align&&x.align!='left')dset(r,lms('align'),lms(x.align))
 if(x.scroll)dset(r,lms('scroll'),lmn(x.scroll))
 if(x.value){if(rtext_is_plain(x.value)){const v=rtext_string(x.value);if(ls(v))dset(r,lms('value'),v)}else{dset(r,lms('value'),rtext_write(x.value))}}
 return r
}
slider_styles={horiz:1,vert:1,bar:1,compact:1}
slider_normalize=(self,n)=>{const i=getpair(ifield(self,'interval')),s=ln(ifield(self,'step'));return clamp(i.x,Math.round(n/s)*s,i.y)}
slider_read=(x,card)=>{
 const update=self=>iwrite(self,lms('value'),ifield(self,'value'))
 const ri=lmi((self,i,x)=>{
  if(!is_rooted(self))return NONE
  if(x){
   if(ikey(i,'value'   ))return self.value=slider_normalize(self,ln(x)),x
   if(has_parent(self))return x
   if(ikey(i,'step'    ))return self.step=max(0.000001,ln(x)),update(self),x
   if(ikey(i,'format'  ))return self.format=ls(x),x
   if(ikey(i,'style'   ))return self.style=normalize_enum(slider_styles,ls(x)),x
   if(ikey(i,'interval')){const v=getpair(x);return self.interval=rect(min(v.x,v.y),max(v.x,v.y)),update(self),x}
  }else{
   if(ikey(i,'value'   )){const v=getpair(ifield(self,'interval'));return lmn(widget_inherit(self,ls(i),clamp(v.x,0,v.y)))}
   if(ikey(i,'format'  ))return lms(widget_inherit(self,ls(i),'%f'))
   if(ikey(i,'step'    ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'interval'))return lmpair(widget_inherit(self,ls(i),rect(0,100)))
   if(ikey(i,'style'   ))return lms(widget_inherit(self,ls(i),'horiz'))
   if(ikey(i,'size'    ))return lmpair(widget_inherit(self,ls(i),rect(100,25)))
   if(ikey(i,'change'&&state.external))return lmnat(([x])=>fire_event(self,i,lmn(ln(x))))
  }return interface_widget(self,i,x)
 },'slider');ri.card=card
 init_field(ri,'interval',x)
 init_field(ri,'value'   ,x)
 init_field(ri,'step'    ,x)
 init_field(ri,'format'  ,x)
 init_field(ri,'style'   ,x)
 return ri
}
slider_write=x=>{
 const r=lmd([lms('type')],[lms('slider')])
 if(x.interval)dset(r,lms('interval'),lmpair(x.interval))
 if(x.value!=undefined&&x.value!=0)dset(r,lms('value'),lmn(x.value))
 if(x.step!=undefined&&x.step!=1)dset(r,lms('step'),lmn(x.step))
 if(x.format&&x.format!='%f')dset(r,lms('format'),lms(x.format))
 if(x.style&&x.style!='horiz')dset(r,lms('style'),lms(x.style))
 return r
}
grid_read=(x,card)=>{
 const ints=(x,n)=>{const r=[];for(let z=0;z<n&&z<x.length;z++)r.push(ln(x[z]));return r}
 const ri=lmi((self,i,x)=>{
  if(!is_rooted(self))return NONE
  if(x){
   if(ikey(i,'value'    ))return self.value=lt(x),x
   if(ikey(i,'scroll'   ))return self.scroll=max(0,ln(x)),x
   if(ikey(i,'row'      ))return self.row=max(-1,ln(x)),x
   if(has_parent(self))return x
   if(ikey(i,'scrollbar'))return self.scrollbar=lb(x),x
   if(ikey(i,'headers'  ))return self.headers=lb(x),x
   if(ikey(i,'lines'    ))return self.lines=lb(x),x
   if(ikey(i,'widths'   ))return self.widths=ints(ll(x),255),x
   if(ikey(i,'format'   ))return self.format=ls(x),x
  }else{
   if(ikey(i,'value'    ))return widget_inherit(self,ls(i),lmt({}))
   if(ikey(i,'scroll'   ))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'scrollbar'))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'headers'  ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'lines'    ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'widths'   ))return lml((widget_inherit(self,ls(i),[])).map(lmn))
   if(ikey(i,'format'   ))return lms(widget_inherit(self,ls(i),''))
   if(ikey(i,'size'     ))return lmpair(widget_inherit(self,ls(i),rect(100,50)))
   if(ikey(i,'row'      ))return lmn(clamp(-1,widget_inherit(self,ls(i),-1),count(ifield(self,'value'))-1))
   if(ikey(i,'rowvalue' )){const r=ln(ifield(self,'row')),v=ifield(self,'value');return r<0||r>=count(v)?lmd():l_at(v,lmn(r))}
   if(ikey(i,'click' )&&state.external)return lmnat(([x])=>fire_event(self,i,lmn(clamp(-1,ln(x),count(ifield(self,'value'))))))
   if(ikey(i,'order' )&&state.external)return lmnat(([x])=>fire_event(self,i,lms(ls(x))))
   if(ikey(i,'change')&&state.external)return lmnat(([x])=>fire_event(self,i,lt(x)))
  }return interface_widget(self,i,x)
 },'grid');ri.card=card
 init_field(ri,'scrollbar',x)
 init_field(ri,'headers'  ,x)
 init_field(ri,'lines'    ,x)
 init_field(ri,'widths'   ,x)
 init_field(ri,'format'   ,x)
 init_field(ri,'scroll'   ,x)
 init_field(ri,'row'      ,x)
 {const k=lms('value'),v=dget(x,k);if(v)iwrite(ri,k,monad.table(v))}
 return ri
}
grid_write=x=>{
 const r=lmd([lms('type')],[lms('grid')])
 if(x.scrollbar!=undefined)dset(r,lms('scrollbar'),lmn(x.scrollbar))
 if(x.headers!=undefined)dset(r,lms('headers'),lmn(x.headers))
 if(x.lines!=undefined)dset(r,lms('lines'),lmn(x.lines))
 if(x.widths)dset(r,lms('widths'),lml(x.widths.map(lmn)))
 if(x.format)dset(r,lms('format'),lms(x.format))
 if(x.value)dset(r,lms('value'),monad.cols(x.value))
 if(x.scroll)dset(r,lms('scroll'),lmn(x.scroll))
 if(x.row!=undefined&&x.row!=-1)dset(r,lms('row'),lmn(x.row))
 return r
}
canvas_clip=(canvas,z)=>{
 const i=canvas_image(canvas,1),s=i.size,w=rect(0,0,s.x,s.y);canvas.clip=!z||z.length<1?w:rint(rclip(w,unpack_rect(z,w)))
}
canvas_pick=canvas=>{
 canvas_image(canvas,1)
 if(!canvas.brush  )iwrite(canvas,lms('brush'  ),ifield(canvas,'brush'  ))
 if(!canvas.pattern)iwrite(canvas,lms('pattern'),ifield(canvas,'pattern'))
 if(!canvas.font   )iwrite(canvas,lms('font'   ),ifield(canvas,'font'   ))
 if(!canvas.clip   )canvas_clip(canvas)
 frame=canvas
}
canvas_image=(canvas,build)=>{
 if(canvas.image||!build)return canvas.image
 const i=widget_inherit(canvas,'image'),scale=card_is(canvas)?1.0:ln(ifield(canvas,'scale')),size=getpair(ifield(canvas,'size'))
 canvas.image=i?image_copy(i):image_make(rect(ceil(size.x/scale),ceil(size.y/scale))),canvas_clip(canvas);return canvas.image
}
canvas_resize=(canvas,size)=>{
 if(!canvas.image)return
 const scale=ln(ifield(canvas,'scale'));image_resize(canvas.image,rect(ceil(size.x/scale),ceil(size.y/scale))),canvas_clip(canvas)
}
canvas_read=(x,card)=>{
 const wid_pal=x=>x.card.deck.patterns.pal.pix
 const wid_rect=(x,z)=>rint(unpack_rect(z,canvas_image(x).size))
 const wid_crect=(x,z)=>rint(rclip(unpack_rect(z,canvas_image(x).size),frame.clip))
 const text=(t,pos,a)=>{
  const font=ifield(frame,'font')
  if(pos&&lil(pos)&&count(pos)>=4){
   a=anchors[ls(a)]||0;const r=rint(getrect(ll(pos).map(ln))), align=(a==0||a==3||a==6)?ALIGN.left:(a==2||a==5||a==8)?ALIGN.right:ALIGN.center
   const valign=s=>rect(align==ALIGN.left?0:align==ALIGN.right?r.w-s.x:0|((r.w-s.x)/2), y=(a==0||a==1||a==2)?0:(a==6||a==7||a==8)?r.h-s.y:0|((r.h-s.y)/2))
   const rbox=s=>{const a=valign(s);return rclip(rint(rect(r.x+a.x,r.y+a.y,s.x,s.y)),frame.clip)}
   if(lit(t)){const l=layout_richtext(frame.card.deck,t,font,align,r.w);draw_text_rich(rbox(l.size),l,frame.pattern,0)}
   else      {const l=layout_plaintext(ls(t),font,align,rect(r.w,r.h)) ;draw_text_wrap(rbox(l.size),l,frame.pattern  )}
  }else{
   if(lit(t)){const p=getpair(pos);return text(t,lml([p.x,p.y,RTEXT_END/1000,RTEXT_END].map(lmn)))}
   const p=anchor(rpair(getpair(pos),font_textsize(font,ls(t))),a)
   draw_text(p,ls(t),font,frame.pattern)
  }return NONE
 }
 const ri=lmi((self,i,x)=>{
  if(!is_rooted(self))return NONE
  if(x){
   if(ikey(i,'brush'    ))return self.brush  =0|clamp(0,ln(x), 23),x
   if(ikey(i,'pattern'  ))return self.pattern=0|clamp(0,ln(x),255),x
   if(ikey(i,'font'     ))return self.font=normalize_font(self.card.deck.fonts,x),x
   if(!lis(i)){const img=canvas_image(self,1);return img.f(img,i,x)}
   if(has_parent(self)||self.free)return x
   if(ikey(i,'border'   ))return self.border=lb(x),x
   if(ikey(i,'draggable'))return self.draggable=lb(x),x
   if(ikey(i,'lsize'    )){i=lms('size'),x=lmpair(rmul(getpair(x),ln(ifield(self,'scale'))))}
   if(ikey(i,'size'     )){canvas_resize(self,getpair(x))}
   if(ikey(i,'scale'    )){return self.scale=max(0.1,ln(x)),canvas_resize(self,getpair(ifield(self,'size'))),x}
  }else{
   if(!lis(i)){const img=canvas_image(self,0);return img?img.f(img,i,x):NONE}
   if(ikey(i,'border'   ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'draggable'))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'brush'    ))return lmn(widget_inherit(self,ls(i),0))
   if(ikey(i,'pattern'  ))return lmn(widget_inherit(self,ls(i),1))
   if(ikey(i,'size'     ))return lmpair(widget_inherit(self,ls(i),rect(100,100)))
   if(ikey(i,'scale'    ))return lmn(widget_inherit(self,ls(i),1.0))
   if(ikey(i,'lsize'    )){const s=getpair(ifield(self,'size')),z=ln(ifield(self,'scale'));return lmpair(rect(ceil(s.x/z),ceil(s.y/z)))}
   if(ikey(i,'clip'     ))return lmnat(z=>(canvas_clip(self,z),NONE))
   if(ikey(i,'clear'    ))return lmnat(z=>(canvas_pick(self),draw_rect(wid_crect(self,z),0            )           ,NONE))
   if(ikey(i,'rect'     ))return lmnat(z=>(canvas_pick(self),draw_rect(wid_crect(self,z),frame.pattern)           ,NONE))
   if(ikey(i,'invert'   ))return lmnat(z=>(canvas_pick(self),draw_invert_raw(wid_pal(self),wid_crect(self,z))     ,NONE))
   if(ikey(i,'box'      ))return lmnat(z=>(canvas_pick(self),draw_box(wid_rect(self,z),frame.brush,frame.pattern) ,NONE))
   if(ikey(i,'poly'     ))return lmnat(z=>(canvas_pick(self),draw_poly(unpack_poly(z),frame.pattern)              ,NONE))
   if(ikey(i,'line'     ))return lmnat(z=>(canvas_pick(self),draw_lines(unpack_poly(z),frame.brush,frame.pattern) ,NONE))
   if(ikey(i,'fill'     ))return lmnat(([pos])=>(canvas_pick(self),draw_fill(rint(getpair(pos)),self.pattern)     ,NONE))
   if(ikey(i,'copy'     ))return lmnat(z=>{const img=canvas_image(self,1);return image_copy(img,unpack_rect(z,img.size))})
   if(ikey(i,'paste'    ))return lmnat(([img,pos,t])=>{
    canvas_pick(self);const dst=canvas_image(self,1)
    img=getimage(img),pos=(pos?ll(pos):[]).map(ln); let solid=t?!lb(t):1
    image_paste_scaled(pos.length<=2?rect(pos[0],pos[1],img.size.x,img.size.y):getrect(pos),frame.clip,img,dst,solid)
    return NONE
   })
   if(ikey(i,'merge'))return lmnat(z=>{
    canvas_pick(self);if(lil(z[0]))z=ll(z[0]);const nice=x=>x&&image_is(x)&&x.size.x>0&&x.size.y>0, size=frame.image.size
    for(let y=0;y<size.y;y++)for(let x=0;x<size.x;x++){
     const h=rect(x,y);if(!inclip(h))continue;let p=gpix(h),c=0;
     if(nice(z[p])){const i=z[p];c=i.pix[(x%i.size.x)+(y%i.size.y)*i.size.x]}
     pix(h,c)
    }return NONE
   })
   if(ikey(i,'text'   ))return lmnat(([x,pos,a])=>(canvas_pick(self),text(x=lit(x)?rtext_cast(x):lms(ls(x)),pos,a)))
   if(ikey(i,'click'  )&&state.external)return lmnat(([x])=>fire_event(self,i,lmpair(getpair(x))))
   if(ikey(i,'drag'   )&&state.external)return lmnat(([x])=>fire_event(self,i,lmpair(getpair(x))))
   if(ikey(i,'release')&&state.external)return lmnat(([x])=>fire_event(self,i,lmpair(getpair(x))))
  }return interface_widget(self,i,x)
 },'canvas');ri.card=card
 ri.card=card
 {const v=dget(x,lms('image'));if(v)ri.image=image_read(ls(v)),iwrite(ri,lms('size'),lmpair(ri.image.size))}
 {const v=dget(x,lms('clip' ));if(v)canvas_clip(ri,ll(v))}
 {const v=dget(x,lms('size' ));if(v)ri.size=getpair(v)}
 {const v=dget(x,lms('scale'));if(v)ri.scale=max(0.1,ln(v))}
 init_field(ri,'border'   ,x)
 init_field(ri,'draggable',x)
 init_field(ri,'brush'    ,x)
 init_field(ri,'pattern'  ,x)
 init_field(ri,'font'     ,x)
 return ri
}
canvas_write=x=>{
 const r=lmd([lms('type')],[lms('canvas')])
 if(x.border!=undefined)dset(r,lms('border'),lmn(x.border))
 if(x.image    )dset(r,lms('image'    ),lms(image_write(x.image)))
 if(x.draggable)dset(r,lms('draggable'),lmn(x.draggable))
 if(x.brush    )dset(r,lms('brush'    ),lmn(x.brush))
 if(x.pattern!=undefined&&x.pattern!=1)dset(r,lms('pattern'),lmn(x.pattern))
 if(x.scale)dset(r,lms('scale'),lmn(x.scale))
 if(x.clip&&!requ(x.clip,rpair(rect(),getpair(ifield(x,'lsize')))))dset(r,lms('clip'),lml([x.clip.x,x.clip.y,x.clip.w,x.clip.h].map(lmn)))
 return r
}
widget_shows={solid:1,invert:1,transparent:1,none:1}
interface_widget=(self,i,x)=>{
 widget_renamer=(card,a,b)=>{const w=card.widgets,i=dkix(w,a);w.k[i]=b,w.v[i].name=ls(b),card_children(card,child=>widget_renamer(child,a,b))}
 widget_reorder=(card,a,b)=>{reorder(card.widgets,a,b),card_children(card,child=>widget_reorder(child,a,b))}
 if(x){
  if(ikey(i,'name'  ))return widget_renamer(self.card,lms(self.name),ukey(self.card.widgets,lms(ls(x)),ls(x),lms(self.name))),x
  if(ikey(i,'index' ))return widget_reorder(self.card,dvix(self.card.widgets,self),ln(x)),x
  if(ikey(i,'font'  ))return self.font=normalize_font(self.card.deck.fonts,x),x
  if(ikey(i,'script'))return self.script=ls(x),x
  if(ikey(i,'locked'))return self.locked=lb(x),x
  if(ikey(i,'size'  ))return self.size=rint(rclamp(rect(),getpair(x),rect(4096,4096))),x
  if(ikey(i,'pos'   ))return self.pos=rint(getpair(x)),x
  if(ikey(i,'show'  ))return self.show=normalize_enum(widget_shows,ls(x)),x
 }else{
  if(ikey(i,'name'  ))return lms(self.name)
  if(ikey(i,'index' ))return lmn(dvix(self.card.widgets,self))
  if(ikey(i,'script'))return lms(widget_inherit(self,ls(i),''))
  if(ikey(i,'locked'))return lmn(widget_inherit(self,ls(i),0))
  if(ikey(i,'pos'   ))return lmpair(widget_inherit(self,ls(i),rect()))
  if(ikey(i,'show'  ))return lms(widget_inherit(self,ls(i),'solid'))
  if(ikey(i,'font'  ))return dget(self.card.deck.fonts,lms(widget_inherit(self,ls(i))||(button_is(self)?'menu':'body')))
 }return x?x:NONE
}
widget_read=(x,card)=>{
 const type=ls(dget(x,lms('type'))||lms('button'))
 const ri=({button:button_read,field:field_read,slider:slider_read,grid:grid_read,canvas:canvas_read}[type]||button_read)(ld(x),card)
 ri.name=ls(ukey(card.widgets,dget(x,lms('name')),type))
 init_field(ri,'size'  ,x)
 init_field(ri,'script',x)
 init_field(ri,'font'  ,x)
 init_field(ri,'locked',x)
 init_field(ri,'pos'   ,x)
 init_field(ri,'show'  ,x)
 return ri
}
widget_write=x=>{
 const r=lmd()
 dset(r,lms('name'),lms(x.name))
 dset(r,lms('type'),lms(x.n))
 if(!has_parent(x))dset(r,lms('size'),ifield(x,'size'))
 if(!has_parent(x))dset(r,lms('pos' ),ifield(x,'pos' ))
 if(x.size  )dset(r,lms('size'  ),lmpair(x.size))
 if(x.pos   )dset(r,lms('pos'   ),lmpair(x.pos))
 if(x.locked)dset(r,lms('locked'),lmn(x.locked))
 if(x.script)dset(r,lms('script'),lms(x.script))
 if(x.font&&x.font!=(button_is(x)?"menu":"body"))dset(r,lms('font'),lms(x.font))
 if(x.show&&x.show!='solid')dset(r,lms('show'),lms(x.show))
 return dyad[','](r,button_is(x)?button_write(x): field_is (x)?field_write (x):slider_is(x)?slider_write(x):
                    grid_is  (x)?grid_write  (x): canvas_is(x)?canvas_write(x):lmd())
}

card_children=(card,f)=>card.deck.cards.v.map(c=>{if(card.name==c.parent)f(c)})
widget_strip=x=>dyad.take(lml([lms('name'),lms('type')]),x)
widget_addr=(card,x)=>{
 const r=widget_read(x,card);dset(card.widgets,ifield(r,'name'),r)
 card_children(card,child=>widget_addr(child,widget_strip(x)));return r
}
card_add=(card,type,name)=>{
 if(has_parent(card))return NONE
 if(lis(type)){
  if(!ls(type)in{button:1,field:1,slider:1,canvas:1,grid:1})return NONE
  const a=lmd([lms('type')],[type]);if(name)dset(a,lms('name'),lms(ls(name)))
  return widget_addr(card,a)
 }
 if(widget_is(type)){
  const a=widget_write(type);if(name)dset(a,lms('name'),name);
  return widget_addr(card,a)}
 return NONE
}
card_remove=(card,x)=>{
 const widget_remover=(card,name)=>{
  dget(card.widgets,name).dead=true,card.widgets=dyad.drop(name,card.widgets)
  card_children(card,child=>widget_remover(child,name))
 }
 if(!widget_is(x)||has_parent(card)||!dkey(card.widgets,x))return 0
 widget_remover(card,ifield(x,'name'));return 1
}
card_copy_raw=(card,z)=>z.filter(w=>widget_is(w)&&w.card==card).map(widget_write)
card_paste_raw=(card,payload)=>payload.map(p=>widget_addr(card,ld(p)))
card_copy=(card,z)=>has_parent(card)?NONE:lms(`%%WGT0${fjson(lml(card_copy_raw(card,lil(z)?ll(z):[z])))}`)
card_paste=(card,z)=>!lis(z)||!z.v.startsWith('%%WGT0')?NONE:lml(card_paste_raw(card,ll(pjson(ls(z),6,count(z)-6).value)))
card_read=(x,deck,cdata)=>{
 x=ld(x);const nav_dirs={right:1,left:1,up:1,down:1},ri=lmi((self,i,x)=>{
  if(self.dead)return NONE
  if(x){
   if(ikey(i,'name')){
    if(ls(x).length==0)return x;const n=ukey(deck.cards,lms(ls(x)),ls(x),lms(self.name))
    deck.cards.v.map(c=>{if(ifield(c,'parent')==self)c.parent=ls(n)})
    deck.cards.k[dvix(deck.cards,self)]=n,self.name=ls(n);return x
   }
   if(ikey(i,'script'))return self.script=ls(x),x
   if(ikey(i,'image' ))return self.image=image_is(x)?x:image_make(rect()),x
   if(ikey(i,'index'))return reorder(self.deck.cards,dvix(self.deck.cards,self),ln(x)),self.deck.history=[ln(ifield(self,'index'))],x
  }else{
   if(ikey(i,'name'   ))return lms(self.name)
   if(ikey(i,'size'   ))return lmpair(deck.size)
   if(ikey(i,'index'  ))return lmn(dvix(deck.cards,self))
   if(ikey(i,'script' ))return lms(card_inherit(self,i)||'')
   if(ikey(i,'widgets'))return self.widgets
   if(ikey(i,'parent' ))return dget(deck.cards,lms(self.parent))||NONE
   if(ikey(i,'image'  ))return card_inherit(self,i)||image_make(rect())
   if(ikey(i,'add'    ))return lmnat(([t,n])=>card_add(self,t,n))
   if(ikey(i,'remove' ))return lmnat(([x])=>lmn(card_remove(self,x)))
   if(ikey(i,'navigate')&&state.external)return lmnat(([x])=>fire_event(self,i,lms(normalize_enum(nav_dirs,ls(x)))))
   if(ikey(i,'copy'    )&&state.external)return lmnat(([z])=>card_copy(self,z))
   if(ikey(i,'paste'   )&&state.external)return lmnat(([z])=>card_paste(self,z))
  }return x?x:NONE
 },'card')
 const n=dget(x,lms('name'))
 ri.deck=deck
 ri.widgets=lmd()
 ri.name=ls(ukey(deck.cards,n&&lis(n)&&count(n)==0?null:n,'card'))
 ri.script=ls(dget(x,lms('script'))||lms(''))
 ri.parent=ls(dget(x,lms('parent'))||lms(''))
 {const v=dget(x,lms('image'));if(v)ri.image=image_read(ls(v))}
 parent_widgets_fuse=(pw,locals)=>{
  return pw.map(w=>{
   let a=widget_strip(w),n=dget(a,lms('name'))
   locals.map(l=>{const ln=dget(l,lms('name'));if(ln&&match(n,ln))a=dyad[','](l,a)})
   return a
  })
 }
 parent_widgets_raw=(name,cdata,locals)=>{
  const keys=Object.keys(cdata)
  for(let z=0;z<keys.length;z++){
   const k=keys[z],n=dget(cdata[k],lms('name'))
   if(!n||!match(name,n))continue
   const p=dget(cdata[k],lms('parent'))
   if(p)return parent_widgets_raw(p,cdata,locals)
   return parent_widgets_fuse(ll(dget(cdata[k],lms('widgets'))),locals)
  }return []
 }
 const parent_widgets_cooked=(parent,locals)=>{
  const c=ll(ifield(parent,'widgets')).map(x=>widget_strip(widget_write(x)))
  return parent_widgets_fuse(c,locals)
 }
 let w=ll(dget(x,lms('widgets'))||lml([]))
 w=!has_parent(ri)?w: cdata?parent_widgets_raw(lms(ri.parent),cdata,w): parent_widgets_cooked(ifield(ri,'parent'),w)
 w.filter(w=>dget(w,lms('name'))).map(w=>{const i=widget_read(w,ri);if(lii(i))dset(ri.widgets,ifield(i,'name'),i)})
 return ri
}
card_write=card=>{
 const r=lmd(),wids=lmd()
 dset(r,lms('name'),lms(card.name)),dset(r,lms('widgets'),wids)
 if(card.parent.length)dset(r,lms('parent'),lms(card.parent))
 if(card.script.length)dset(r,lms('script'),lms(card.script))
 if(card.image)dset(r,lms('image'),lms(image_write(card.image)))
 card.widgets.k.map((k,i)=>{
  let wid=widget_write(card.widgets.v[i]),n=dget(wid,lms('name'))
  if(has_parent(card))wid=dyad.drop(lms('type'),wid);wid=dyad.drop(lms('name'),wid)
  if(count(wid))dset(wids,n,wid)
 });return r
}
rename_sound=(deck,sound,name)=>{
 const sounds=deck.sounds,oldname=dkey(sounds,sound)
 sounds.k[dkix(sounds,oldname)]=ukey(sounds,name,ls(name),oldname)
}
deck_add=(deck,type,y,z)=>{
 const unpack_name=x=>x?lms(ls(x)):NONE
 if(font_is(type))return uset(deck.fonts,unpack_name(y),'font',font_read(font_write(type)))
 if(ikey(type,'font'))return uset(deck.fonts,unpack_name(z),'font',font_read(getpair(y)))
 if(sound_is(type))return uset(deck.sounds,unpack_name(y),'sound',sound_read(sound_write(type)))
 if(ikey(type,'sound'))return uset(deck.sounds,unpack_name(z),'sound',sound_read(y?ln(y):0))
 if(module_is(type)){const a=module_write(type);if(y)dset(a,lms('name'),lms(ls(y)));const r=module_read(a,deck);return dset(deck.modules,ifield(r,'name'),r),r}
 if(ikey(type,'module')){const a=lmd();if(y)dset(a,lms('name'),lms(ls(y)));const r=module_read(a,deck);return dset(deck.modules,ifield(r,'name'),r),r}
 if(card_is(type))return deck_paste(deck,deck_copy(deck,type),y?lms(ls(y)):null)
 if(ikey(type,'card')){
  const a=lmd();if(y)dset(a,lms('name'),lms(ls(y)))
  if(z&&card_is(z)){if(!dkey(deck.cards,z))return NONE;dset(a,lms('parent'),ifield(z,'name'))}
  const r=card_read(a,deck);dset(deck.cards,ifield(r,'name'),r);return r
 }return NONE
}
deck_remove=(deck,t)=>{
 if(module_is(t)){const k=dkey(deck.modules,t);if(k)return deck.modules=dyad.drop(k,deck.modules),1}
 if(sound_is(t)){const k=dkey(deck.sounds,t);if(k)return deck.sounds=dyad.drop(k,deck.sounds),1}
 if(font_is(t)){
  const k=dkey(deck.fonts,t);if(!k||ls(k)in{body:1,menu:1,mono:1})return 0
  deck.cards.v.map(c=>c.widgets.v.map(w=>{if(w.font==ls(k))w.font='body'}))
  return deck.fonts=dyad.drop(k,deck.fonts),1
 }
 if(card_is(t)){
  const widget_removeall=card=>{
   card.widgets.v.map(x=>x.dead=true),card.widgets=lmd()
   card_children(card,child=>widget_removeall(child))
  }
  if(count(deck.cards)<=1)return 0
  deck.cards.v.map(card=>{if(ifield(card,'parent')==t){card.parent=0,widget_removeall(card)}})
  deck.cards=dyad.drop(dkey(deck.cards,t)||NONE,deck.cards),t.dead=true
  if(deck.card>=count(deck.cards))deck.card=count(deck.cards-1)
  deck.history=[ln(ifield(ifield(deck,'card'),'index'))]
  return 1
 }return 0
}
deck_copy=(deck,z)=>!card_is(z)?NONE: lms(`%%CRD0${fjson(card_write(z))}`)
deck_paste=(deck,z,name)=>{
 if(!lis(z)||!ls(z).startsWith('%%CRD0'))return NONE
 const payload=ld(pjson(ls(z),6,count(z)-6).value)
 const w=dget(payload,lms('widgets'));
 if(w&&lid(w)){w.v.map((v,i)=>{dset(v,lms('name'),w.k[i])})}
 const r=card_read(payload,deck);dset(deck.cards,name||ifield(r,'name'),r)
 return r
}
deck_read=x=>{
 const deck={},scripts={},cards={},modules={}, fonts=lmd(),sounds=lmd(); let i=0,m=0,md=0
 Object.keys(FONTS).map(k=>dset(fonts,lms(k),font_read(FONTS[k])))
 const match=k=>x.startsWith(k,i)?(i+=k.length,1):0
 const end=_=>i>=x.length||x.startsWith('<\/script>',i)
 const str=e=>{let r='';while(!end()&&!match(e))r+=match('{l}')?'{': match('{r}')?'}': match('{c}')?':': match('{s}')?'/': x[i++];return clchars(r)}
 const last=dict=>{const k=Object.keys(dict);return dict[k[k.length-1]]||lmd()}
 match('<body><script language="decker">');while(!end()){
  if(x[i]=='\n')i++
  else if(x[i]=='#')while(!end()&&x[i]!='\n')i++
  else if(match('{deck}\n'   ))m=1
  else if(match('{fonts}\n'  ))m=2
  else if(match('{sounds}\n' ))m=3
  else if(match('{widgets}\n'))m=4
  else if(match('{card:')){const k=str('}');cards[k]=lmd(['name','widgets'].map(lms),[lms(k),lml([])]),m=5}
  else if(match('{script:')){const k=str('}\n');scripts[k]=str('\n{end}')}
  else if(match('{module:')){const k=str('}');modules[k]=lmd(['name','script','data'].map(lms),[lms(k),lms(''),lmd()]),m=6,md=0}
  else if(m==6&&match('{data}\n')){md=1}
  else if(m==6&&match('{script}\n')){dset(last(modules),lms('script'),lms(str('\n{end}'))),m=1}
  else{
   const k=str(':'),j=pjson(x,i,x.length-i),v=j.value;i=j.index
   if(m==1)deck[k]=v
   if(m==2)dset(fonts,lms(k),font_read(ls(v)))
   if(m==3)dset(sounds,lms(k),sound_read(ls(v)))
   if(m==4){if(Object.keys(cards).length)dget(last(cards),lms('widgets')).v.push(dset(ld(v),lms('name'),lms(k)))}
   if(m==5)dset(last(cards),lms(k),v)
   if(m==6)dset(md?dget(last(modules),lms('data')):last(modules),lms(k),v)
  }
 }
 const dscript=x=>{const k=lms('script'),s=dget(x,k);if(s)dset(x,k,lms(scripts[ls(s)]))}
 Object.keys(cards).map(k=>{const card=cards[k];dscript(card),dget(card,lms('widgets')).v.map(dscript)})
 const ri=lmi((self,i,x)=>{
  if(x){
   if(ikey(i,'locked'))return self.locked=lb(x),x
   if(ikey(i,'name'  ))return self.name=ls(x),x
   if(ikey(i,'author'))return self.author=ls(x),x
   if(ikey(i,'script'))return self.script=ls(x),x
   if(ikey(i,'card'  ))return n_go([x],self),x
  }else{
   if(ikey(i,'version' ))return lmn(self.version)
   if(ikey(i,'locked'  ))return lmn(self.locked)
   if(ikey(i,'name'    ))return lms(self.name)
   if(ikey(i,'author'  ))return lms(self.author)
   if(ikey(i,'script'  ))return lms(self.script)
   if(ikey(i,'patterns'))return self.patterns
   if(ikey(i,'sounds'  ))return dyad.drop(NONE,self.sounds)
   if(ikey(i,'fonts'   ))return dyad.drop(NONE,self.fonts)
   if(ikey(i,'cards'   ))return self.cards
   if(ikey(i,'modules' ))return self.modules
   if(ikey(i,'card'    ))return self.cards.v[min(count(self.cards)-1,self.card)]
   if(ikey(i,'add'     ))return lmnat(([x,y,z])=>deck_add(self,x,y,z))
   if(ikey(i,'remove'  ))return lmnat(([x])=>lmn(deck_remove(self,x)))
   if(ikey(i,'copy' )&&state.external)return lmnat(([x])=>deck_copy(self,x))
   if(ikey(i,'paste')&&state.external)return lmnat(([x])=>deck_paste(self,x))
  }return x?x:NONE
 },'deck')
 ri.fonts   =fonts
 ri.sounds  =sounds
 ri.cards   =lmd()
 ri.modules =lmd()
 ri.transit =lmd()
 ri.patterns=patterns_read(deck)
 ri.version ='version' in deck?ln(deck.version):1
 ri.locked  ='locked'  in deck?lb(deck.locked ):0
 ri.name    ='name'    in deck?ls(deck.name   ):''
 ri.author  ='author'  in deck?ls(deck.author ):''
 ri.script  ='script'  in deck?scripts[ln(deck.script)]:''
 ri.card    ='card'    in deck?clamp(0,ln(deck.card),Object.keys(cards).length-1):0
 ri.size    ='size'    in deck?rclamp(rect(320,240),getpair(deck.size),rect(4096,4096)):rect(512,342)
 if(Object.keys(cards).length==0)cards.home=lmd(['name'].map(lms),[lms('home')])
 const root=lmenv();constants(root),primitives(root,ri)
 pushstate(root),issue(root,parse(DEFAULT_TRANSITIONS));while(running())runop();popstate()
 Object.keys(cards  ).map(k=>{const v=card_read(cards[k],ri,cards);dset(ri.cards,ifield(v,'name'),v)})
 Object.keys(modules).map(k=>{const v=module_read(modules[k],ri);dset(ri.modules,ifield(v,'name'),v)})
 ri.history=[ln(ifield(ifield(ri,'card'),'index'))]
 return ri
}
deck_write=(x,html)=>{
 if(!deck_is(x))return '';let deck=x,scripts=lmd(),si=0,sci=0,r=(html?'<body><script language=\"decker\">\n':'')+'{deck}\nversion:1\n'
 const esc_write=(id,x)=>{
  let c='\0',lc=c,r='';for(let z=0;z<x.length;z++){
   lc=c,c=x[z],r+=c=='{'?'{l}': c=='}'?'{r}': c==':'&&id?'{c}': c=='/'&&lc=='<'?'{s}': c
  }return r
 }
 const script_ref=(base,x)=>{
  if(ls(x)=='undefined')throw new Error('welp')
  for(let z=0;z<scripts.v.length;z++)if(match(scripts.v[z],x))return scripts.k[z]
  const k=lms(base?`${base}.${sci}`:`${sci}`);sci++;dset(scripts,k,x);return k
 }
 const write_scripts=_=>{while(si<scripts.v.length)r+=`\n{script:${esc_write(1,ls(scripts.k[si]))}}\n${esc_write(0,ls(scripts.v[si++]))}\n{end}\n`}
 const write_line=(s,k,p,f)=>{const v=s[k];if(p(v))r+=`${k}:${fjson(f(v))}\n`}
 const write_key =(s,k,p,f)=>{const v=dget(s,lms(k));if(p(v))r+=`${k}:${fjson(f(v))}\n`}
 const write_dict=(k,x,f)=>r+=`${count(x)?k:''}${x.k.map((k,i)=>`${esc_write(1,ls(k))}:${fjson(f(x.v[i]))}\n`).join('')}`
 const pp=patterns_write(x.patterns),pa=anims_write(x.patterns),da=dyad.parse(lms('%j'),lms(DEFAULT_ANIMS))
 write_line(x,'card'      ,x=>1                                  ,lmn                       )
 write_line(x,'size'      ,x=>1                                  ,lmpair                    )
 write_line(x,'locked'    ,x=>x                                  ,lmn                       )
 write_line(x,'script'    ,x=>x.length                           ,x=>script_ref(null,lms(x)))
 write_line(x,'name'      ,x=>x.length                           ,lms                       )
 write_line(x,'author'    ,x=>x.length                           ,lms                       )
 write_line(x,'patterns'  ,x=>pp!=DEFAULT_PATTERNS               ,x=>lms(pp)                )
 write_line(x,'animations',x=>!match(pa,da)                      ,x=>pa                     )
 write_scripts()
 write_dict('\n{fonts}\n',dyad.drop(lml(['body','menu','mono'].map(lms)),deck.fonts),x=>lms(font_write(x)))
 write_dict('\n{sounds}\n',deck.sounds,x=>lms(sound_write(x)))
 deck.cards.v.map(c=>{
  const data=card_write(c),wids=dget(data,lms('widgets')),base=ls(dget(data,lms('name')));sci=0
  r+=`\n{card:${esc_write(1,base)}}\n`
  write_key(data,'parent',x=>x ,x=>x                 )
  write_key(data,'image' ,x=>x ,x=>x                 )
  write_key(data,'script',count,x=>script_ref(base,x))
  wids.v.map(wid=>{const k=lms('script'),v=dget(wid,k);if(v)dset(wid,k,script_ref(base,v))})
  write_dict('{widgets}\n',wids,x=>x)
  write_scripts()
 })
 deck.modules.v.map(m=>{
  const data=module_write(m)
  r+=`\n{module:${esc_write(1,ls(dget(data,lms('name'))))}}\n`
  write_key(data,'description',x=>x,x=>x)
  write_dict('{data}\n',dget(data,lms('data')),x=>x)
  r+=`{script}\n${esc_write(0,ls(ifield(m,'script')))}\n{end}\n`
 })
 return r+'\n'+(html?'<\/script>\nRuntime stub is NYI.':'')
}

n_go=([x,t],deck)=>{
 let r=null, i=deck.card;t=t?ls(t):''
 if(lin(x))r=clamp(0,ln(x),count(deck.cards)-1)
 else if(card_is(x)){const i=dvix(deck.cards,x);if(i>=0)r=i}
 else{
  x=ls(x);if(deck.history.length>1&&x=='Back'){
   deck.history.pop();const ix=last(deck.history);
   if(ix>=0&&ix<count(deck.cards)){go_notify(deck,ix,t,x),deck.card=ix;return lmn(deck.card)}
  }
  else if(x=='First')r=0
  else if(x=='Last' )r=count(deck.cards)-1
  else if(x=='Prev' )r=mod(i-1,count(deck.cards))
  else if(x=='Next' )r=mod(i+1,count(deck.cards))
  else{const ix=dkix(deck.cards,lms(x));if(ix>=0)r=ix}
 }if(r!=null){go_notify(deck,r,t,x),deck.card=r;if(i!=r)deck.history.push(r)}else{go_notify(deck,-1,t,x)}return lmn(deck.card)
}
n_sleep=([z])=>{if(lis(z)&&ls(z)=='play'){sleep_play=1}else{sleep_frames=max(1,ln(z))};return z}
n_transition=(f,deck)=>{const t=deck.transit;if(lion(f))dset(t,lms(f.n),f);return t}

constants=env=>{
 env.local('sys'    ,interface_system)
 env.local('rtext'  ,interface_rtext)
 env.local('pointer',pointer)
 env.local('pi'   ,lmn(3.141592653589793))
 env.local('e'    ,lmn(2.718281828459045))
 env.local('colors',lmd(
  'white|yellow|orange|red|magenta|purple|blue|cyan|green|darkgreen|brown|tan|lightgray|mediumgray|darkgray|black'.split('|').map(lms),
  range(16).map(x=>lmn(x+32))
 ))
}
primitives=(env,deck)=>{
 env.local('show'      ,lmnat(n_show    ))
 env.local('print'     ,lmnat(n_print   ))
 env.local('play'      ,lmnat(n_play    ))
 env.local('go'        ,lmnat(([x,t])=>n_go([x,t],deck)))
 env.local('transition',lmnat(([f])=>n_transition(f,deck)))
 env.local('sleep'     ,lmnat(n_sleep   ))
 env.local('eval'      ,lmnat(n_eval    ))
 env.local('random'    ,lmnat(n_random  ))
 env.local('array'     ,lmnat(n_array   ))
 env.local('image'     ,lmnat(n_image   ))
 env.local('sound'     ,lmnat(n_sound   ))
 env.local('readcsv'   ,lmnat(n_readcsv ))
 env.local('writecsv'  ,lmnat(n_writecsv))
 env.local('readxml'   ,lmnat(n_readxml ))
 env.local('writexml'  ,lmnat(n_writexml))
 env.local('alert'     ,lmnat(n_alert   ))
 env.local('read'      ,lmnat(n_open    ))
 env.local('write'     ,lmnat(n_save    ))
}
fire_async=(target,name,arg,hunk)=>{
 const scopes=lmd([NONE],[parse(DEFAULT_HANDLERS)]), root=lmenv(); let deck=null
 ancestors=target=>{
  if(deck_is(target))deck=target
  if(widget_is(target))ancestors(target.card)
  if(card_is(target)){const p=ifield(target,'parent');ancestors(card_is(p)?p:target.deck)}
  try{dset(scopes,target,parse(ls(ifield(target,'script'))))}
  catch(e){dset(scopes,target,lmblk());}
 }
 ancestors(target)
 primitives(root,deck)
 root.local('me',target)
 root.local('deck',deck)
 root.local('patterns',ifield(deck,'patterns'))
 constants(root)
 let core=null; arg=lml([arg])
 for(let z=scopes.v.length-1;z>=0;z--){
  let t=scopes.k[z], b=lmblk(), sname='!widget_scope'
  const bind=(n,v)=>{blk_lit(b,v),blk_loc(b,n),blk_op(b,op.DROP)}
  const func=(n,v)=>{blk_lit(b,lmon(n,[],blk_end(v))),blk_op(b,op.BIND),name=n,arg=lml([])}
  if(lin(t))sname='!default_handlers'
  if(deck_is(t)){
   t.modules.v.map((v,i)=>bind(t.modules.k[i],ifield(v,'value')))
   t.cards  .v.map((v,i)=>bind(t.cards  .k[i],v                ))
   sname='!deck_scope'
  }
  if(card_is(t)){
   bind(lms('card'),t)
   t.widgets.v.map((v,i)=>bind(t.widgets.k[i],v))
   sname='!card_scope'
  }
  blk_cat(b,scopes.v[z])
  if(!core&&hunk){func('!hunk',hunk)}
  else if(core){func(sname,core)}
  blk_get(b,lms(name)),blk_lit(b,arg),blk_op(b,op.CALL);if(!hunk)blk_op(b,op.DROP);core=b
 }
 pushstate(root),issue(root,core),pending_popstate=1
}
fire_event_async=(target,name,x)=>fire_async(target,name,x,null)
fire_hunk_async=(target,hunk)=>fire_async(target,null,null,hunk)
fire_event=(target,name,x)=>{
 fire_event_async(target,ls(name),x)
 while(running())runop()
 return arg(),popstate(),pending_popstate=0,NONE
}
// Decker

let pump=null, zoom=1, deck=null, fb=null, context=null, dirty=0
let FONT_BODY=null,FONT_MENU=null,FONT_MONO=null

const ELLIPSIS=String.fromCharCode(95+32)
const DOUBLE_CLICK_DELAY=20
const FIELD_CURSOR_DUTY =20
const FIELD_CHANGE_DELAY=60
const LISTEN_LINES      =30
const LISTEN_SIZE       =_=>rect(frame.size.x-20,100)
const MASTER_VOLUME     =0.3
const FAT               =8
const BG_MASK           =100

q=x=>document.querySelector(x)
gcd=(x,y)=>{while(x!=y){if(x>y){x-=y}else{y-=x}}return x}
lcm=(x,y)=>{const r=gcd(x,y);return 0|((x*y)/(r?r:1))}
copy_object=x=>Object.keys(x).reduce((r,k)=>((r[k]=x[k]),r),{})
plain_or_rich=x=>lit(x)?rtext_cast(x):lms(x?ls(x):'')
fieldstr=x=>({table:rtext_cast(x),scroll:0})
gridtab=(x,r)=>({table:x,scroll:0,row:r==undefined?-1:r})
open_url=x=>{window.open(x,'_blank')}
open_file=(filter,f)=>{const s=q('#source');s.value='',s.accept=filter,s.onchange=_=>{if(s.files.length)f(s.files[0])},s.click()}
open_text=(filter,f)=>open_file(filter,file=>{const r=new FileReader();r.onload=_=>{f(clchars(r.result))},r.readAsText(file)})
load_array=(file,after)=>{const r=new FileReader();r.onload=_=>{const b=new Uint8Array(r.result);after(array_make(b.length,'u8',0,b))},r.readAsArrayBuffer(file)}
load_image=(file,hint,after)=>{
 const read_image=grayscale=>{
  const i=q('#loader'),w=i.width,h=i.height,r=image_make(rect(w,h)); if(w==0||h==0)return r
  const t=document.createElement('canvas');t.width=w,t.height=h
  const tg=t.getContext('2d');tg.drawImage(i,0,0)
  const d=tg.getImageData(0,0,w,h).data
  let src=0,dst=0;while(dst<r.pix.length){
   const cr=d[src++],cg=d[src++],cb=d[src++],ca=d[src++]
   r.pix[dst++]=(ca!=0xFF)?(grayscale?0xFF:0x00): readcolor(cr,cg,cb,grayscale)
  }return r
 }
 const import_image=_=>{
  if(after){after(read_image(hint=='gray'));return}
  let i=read_image(0),m=null; if(i.size.x==0||i.size.y==0)return
  let color=0,c=new Uint8Array(256);i.pix.forEach(p=>c[p]++)
  let tw=c[0],ow=c[32];c[32]=0,c[47]=0;for(let z=2;z<256;z++)if(c[z]){color=1;break}
  if(color&&tw)i.pix.forEach((p,z)=>i.pix[z]=p!=0),m=i
  if(color){i=read_image(1)}else if(ow&&!tw){i.pix.forEach((p,z)=>i.pix[z]=p!=32)}
  setmode('draw'),bg_paste(i);dr.limbo_dither=color,dr.dither_threshold=0.5,dr.fatbits=0,dr.omask=m
 }
 const r=new FileReader();r.onload=_=>{q('#loader').src=r.result;setTimeout(import_image,100)};r.readAsDataURL(file)
}
save_text=(n,x)=>{
 const u=URL.createObjectURL(new Blob([x])), t=q('#target')
 t.download=n,t.href=u,t.click(),setTimeout(_=>URL.revokeObjectURL(u),200)
}
save_bin=(n,x)=>{
 const u=URL.createObjectURL(new Blob([Uint8Array.from(x)])), t=q('#target')
 t.download=n,t.href=u,t.click(),setTimeout(_=>URL.revokeObjectURL(u),200)
}
writegif=frames=>{
 const size=frames.reduce((s,f)=>rmax(s,f.size),rect(1,1)), pal=deck.patterns.pal.pix; let frame_index=0, payload=[]
 const anim_ants       =(x,y)=>(0|((x+y+frame_index)/3))%2?15:0
 const draw_pattern    =(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1
 const draw_color_trans=(pix,x,y)=>pix==ANTS?anim_ants(x,y): pix==0?16: pix>47?0: pix>31?pix-32: draw_pattern(pix,x,y)?15:0
 const b=x=>payload.push(x&0xFF), s=x=>{b(x);b(x>>8)}, t=x=>x.split('').forEach(x=>b(x.charCodeAt(0)))
 t('GIF89a'),s(size.x),s(size.y) // header, dimensions
 b(0xF4)                         // global colortable, 8-bits per channel, 32 colors
 b(0),b(0)                       // background color is 0, 1:1 pixel aspect ratio
 for(let z=0;z<16;z++)b(COLORS[z]>>16),b(COLORS[z]>>8),b(COLORS[z]) // global colortable
 for(let z=0;z<16;z++)b(0xFF         ),b(0xFF        ),b(0xFF     ) // padding entries
 s(0xFF21),b(11),t('NETSCAPE2.0'),b(3),b(1),s(0),b(0)               // NAB; loop gif forever
 for(let z=0;z<frames.length;z++){
  const frame=frames[z]
  s(0xF921),b(4)                            // graphic control extension
  b(9),s(20),b(16)                          // dispose to bg + has transparency, 20/100th of a second delay, color 16 is transparent
  b(0)                                      // end GCE
  b(0x2C)                                   // image descriptor
  s(0),s(0),s(frame.size.x),s(frame.size.y) // dimensions
  b(0),b(7)                                 // no local colortable,  minimum LZW code size
  let isize=frame.size.x*frame.size.y, off=0
  while(off<isize){
   const bsize=min(64,isize-off)
   b(1+bsize),b(0x80) // block size, LZW CLEAR
   for(let z=0;z<bsize;z++){
    const x=0|((off+z)%frame.size.x), y=0|((off+z)/frame.size.x)
    b(draw_color_trans(frame.pix[off+z],x,y))
   }off+=bsize
  }b(0),frame_index++ // end of frame
 };b(0x3B);return payload
}
writewav=sound=>{
 const payload=[], b=x=>payload.push(x&0xFF), s=x=>(b(x),b(x>>8))
 const d=x=>(b(x),b(x>>8),b(x>>16),b(x>>24)), t=x=>x.split('').forEach(x=>b(x.charCodeAt(0)))
 t('RIFF'),d(4+24+(8+sound.data.length)+(sound.data.length%2))
 t('WAVE'),t('fmt ')
 d(16)             // chunk size
 s(1),s(1),d(8000) // pcm, 1 channel, 8khz
 d(8000)           // 8000*(1 byte per sample)*(1 channel)
 s(1)              //      (1 byte per sample)*(1 channel)
 s(8)              // 8 bits per sample
 t('data'),d(sound.data.length)
 for(let z=0;z<sound.data.length;z++)b(128+sound.data[z])
 if(sound.data.length%2)b(0);return payload
}
writearray=array=>{
 const payload=[]
 for(let z=0;z<array.size;z++){const ix=array.base+z;payload.push(ix>=0&&ix<array.data.length?array.data[ix]:0)}
 return payload
}

keep_ratio=(r,s)=>{if(!ev.shift||s.x==0||s.y==0)return r;const scale=max(r.w/(s.x*1.0),r.h/(s.y*1.0));return rect(r.x,r.y,scale*s.x,scale*s.y)}
draw_frame=(image,clip)=>({brush:0,pattern:0,font:FONT_BODY,size:image.size,clip:clip||rect(0,0,image.size.x,image.size.y),image})
draw_pix=(x,y,pattern)=>{const h=rect(x,y);if(inclip(h))pix(h,pattern)}
draw_invert=(pal,r)=>{
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1; r=rclip(r,frame.clip)
 for(let a=r.y;a<r.y+r.h;a++)for(let b=r.x;b<r.x+r.w;b++){const h=rect(b,a), p=draw_pattern(gpix(h),b,a);pix(h,p==0||p==32?1:32)}
}
draw_shadow=(r,fcol,bcol,solid)=>{
 if(solid)draw_rect(rclip(r,frame.clip),bcol);draw_box(r,0,fcol),draw_hline(r.x+3,r.x+r.w,r.y+r.h,fcol),draw_vline(r.x+r.w,r.y+2,r.y+r.h+1,fcol)
}
draw_boxr=(r,fcol,bcol,background)=>{
 r=rint(r)
 draw_hline(r.x+2,r.x+r.w-2,r.y,fcol),draw_hline(r.x+2,r.x+r.w-2,r.y+r.h-1,fcol),draw_vline(r.x,r.y+2,r.y+r.h-2,fcol),draw_vline(r.x+r.w-1,r.y+2,r.y+r.h-2,fcol)
 draw_pix(r.x+1,r.y+1,fcol),draw_pix(r.x+r.w-2,r.y+1,fcol),draw_pix(r.x+1,r.y+r.h-2,fcol),draw_pix(r.x+r.w-2,r.y+r.h-2,fcol)
 if(background)draw_hline(r.x+2,r.x+r.w-2,r.y+1,bcol),draw_hline(r.x+2,r.x+r.w-2,r.y+r.h-2,bcol),draw_rect(rect(r.x+1,r.y+2,r.w-2,r.h-4),bcol)
}
draw_boxinv=(pal,r)=>{
 draw_invert(pal,rect(r.x,r.y,1,r.h))    ,draw_invert(pal,rect(r.x+r.w-1,r.y,1,r.h))
 draw_invert(pal,rect(r.x+1,r.y,r.w-2,1)),draw_invert(pal,rect(r.x+1,r.y+r.h-1,r.w-2,1))
}
draw_modalbox=s=>{
 const menu=16, r=rcenter(rect(0,menu,frame.size.x,frame.size.y-menu),s), o=inset(r,-5)
 draw_rect(inset(o,-5),32),draw_box(inset(o,-5),0,1),draw_box(inset(o,-2),0,1),draw_box(inset(o,-1),0,1);return r
}
draw_modal_rtext=extra=>{
 const size=rect(200,100)
 const l=lit(ms.message)?layout_richtext(deck,ms.message,FONT_BODY,ALIGN.center,size.x):layout_plaintext(ls(ms.message),FONT_BODY,ALIGN.center,size)
 const b=draw_modalbox(radd(l.size,extra)), tbox=rect(b.x,b.y,b.w,l.size.y)
 if(lit(ms.message)){draw_text_rich(tbox,l,1,1)}else{draw_text_wrap(tbox,l,1)}
 return b
}
draw_text_outlined=(pos,text,f)=>{
 ([[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]]).map(([x,y])=>draw_text(rect(pos.x+x,pos.y+y),text,f,32))
 draw_text(pos,text,f,1)
}
draw_textc=(r,text,font,pattern)=>{
 const size=font_textsize(font,text);
 if(pattern==-1){draw_text_outlined(rcenter(r,size),text,font)}
 else if(size.x<r.w){draw_text(rcenter(r,size),text,font,pattern)}else{draw_text_fit(r,text,font,pattern)}
}
draw_text_fit=(r,text,font,pattern)=>{
 const glyphs=[], glyph_push=(pos,c)=>glyphs.push({pos,c})
 let x=0,y=0,fh=font_h(font),ew=font_gw(font,ELLIPSIS)
 for(let z=0;z<text.length&&(y+fh)<=r.h;z++){
  const c=text[z]
  if(c=='\n'){x=0,y+=fh}
  else if(x+font_gw(font,c)>=(r.w-ew)){glyph_push(rect(x,y),ELLIPSIS);while(z<text.length&&text[z]!='\n')z++;x=0;if(z<text.length){y+=fh}else{z--}}
  else{glyph_push(rect(x,y),c),x+=font_gw(font,c)+font_sw(font)}
 }
 let yo=ceil((r.h-(y+fh))/2.0);glyphs.map(g=>{g.pos.x+=r.x,g.pos.y+=yo+r.y,draw_char(g.pos,font,g.c,pattern)})
}
draw_scaled=(r,image,opaque)=>{
 if(r.w==0||r.h==0)return;const s=image.size
 if(r.w==s.x&&r.h==s.y){image_paste(r,frame.clip,image,frame.image,opaque);return}
 image_paste_scaled(r,frame.clip,image,frame.image,opaque)
}
draw_invert_scaled=(pal,r,image)=>{
 if(r.w==0||r.h==0)return;const s=image.size, fb=frame.image.pix, fs=frame.image.size.x
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal[(x%8)+(8*(y%8))+(8*8*pix)]&1
 const gpix=p=>fb[p.x+p.y*fs], pix=(p,v)=>fb[p.x+p.y*fs]=v
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  const sx=0|(s.x==r.w?b:((b*1.0)/r.w)*s.x), sy=0|(s.y==r.h?a:((a*1.0)/r.h)*s.y), dx=r.x+b, dy=r.y+a
  const c=draw_pattern(image.pix[sx+sy*s.x],dx,dy), h=rect(dx,dy)
  if(inclip(h))pix(h,c^draw_pattern(gpix(h),dx,dy))
 }
}
draw_fat=(src,pal,frame_count,mask,scale,offset)=>{
 const anim=deck.patterns.anim
 const anim_pattern=(pix,x,y)=>pix<28||pix>31?pix: anim[pix-28][(0|(frame_count/4))%max(1,anim[pix-28].length)]
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal[(x%8)+(8*(y%8))+(8*8*pix)]&1
 const s=src.size;for(let y=0;y<ceil(frame.size.y/scale);y++)for(let x=0;x<ceil(frame.size.x/scale);x++){
  if(offset.x+x>=s.x||offset.y+y>=s.y||offset.x+x<0||offset.y+y<0)continue
  const v=src.pix[(offset.x+x)+(offset.y+y)*frame.size.x];if(v==mask)continue
  const c=anim_pattern(v,offset.x+x,offset.y+y),p=draw_pattern(c,offset.x+x,offset.y+y)
  draw_rect(rect(x*scale,y*scale,scale,scale),c>=32?c: c==0?0: p?1:32)
 }
}
draw_fat_scaled=(r,image,opaque,pal,frame_count,scale,offset)=>{
 const anim=deck.patterns.anim
 const anim_pattern=(pix,x,y)=>pix<28||pix>31?pix: anim[pix-28][(0|(frame_count/4))%max(1,anim[pix-28].length)]
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal[(x%8)+(8*(y%8))+(8*8*pix)]&1
 if(r.w==0||r.h==0)return;const s=image.size
 for(let y=0;y<r.h;y++)for(let x=0;x<r.w;x++){
  const sx=0|(s.x==r.w?x:((x*1.0)/r.w)*s.x), sy=0|(s.y==r.h?y:((y*1.0)/r.h)*s.y), v=image.pix[sx+sy*s.x]
  const c=anim_pattern(v,r.x+x,r.y+y),p=draw_pattern(c,r.x+x,r.y+y)
  if(opaque||v!=0)draw_rect(rect((r.x-offset.x+x)*scale,(r.y-offset.y+y)*scale,scale,scale),c>=32?c: p?1:0)
 }
}
draw_dithered=(r,image,opaque,mask,threshold)=>{
 if(r.w==0||r.h==0)return;const s=image.size, stride=2*r.w, m=[0,1,r.w-2,r.w-1,r.w,stride-1], dither_err=new Float32Array(stride)
 for(let ei=0,a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  const sx=0|(((b*1.0)/r.w)*s.x), sy=0|(((a*1.0)/r.h)*s.y), src=0xFF&image.pix[sx+sy*s.x], ms=mask?mask.pix[sx+sy*s.x]:1
  const p=(src/256.0)+dither_err[ei], col=p>threshold?1:0, err=(p-col)/8.0
  dither_err[ei]=0, ei=(ei+1)%stride; for(let z=0;z<6;z++)dither_err[(ei+m[z])%stride]+=err
  const c=!col;if(ms&&(opaque||c!=0)){const h=rect(r.x+b,r.y+a);if(inclip(h))pix(h,c)}
 }
}
draw_card=(card,active)=>{
 const im=ms.in_modal;ms.in_modal=active
 const rsize=getpair(ifield(card,'size')),r=image_make(rsize),t=frame,te=copy_object(ev);frame=draw_frame(r),ev=event_state()
 const back=ifield(card,'image'), bsize=back.size, wids=ifield(card,'widgets')
 if(bsize.x!=0&&bsize.y!=0)image_paste(rpair(rect(),bsize),frame.clip,back,frame.image,1)
 if(uimode!='draw'||dr.show_widgets)wids.v.map(w=>{
  if(button_is(w))widget_button(w,unpack_button(w),lb(ifield(w,'value')))
  if(slider_is(w))widget_slider(w,unpack_slider(w))
  if(canvas_is(w))widget_canvas(w,unpack_canvas(w),canvas_image(w,0))
  if(grid_is  (w))widget_grid  (w,unpack_grid  (w),unpack_grid_value(w))
  if(field_is (w))widget_field (w,unpack_field (w),unpack_field_value(w))
 });return ev=te,frame=t,ms.in_modal=im,r
}
draw_thumbnail=(card,r)=>{
 const back=ifield(card,'image');r=inset(r,1),draw_rect(r,0);if(back.size.x>0||back.size.y>0)draw_scaled(r,back,1)
 const wids=ifield(card,'widgets'),s=getpair(ifield(card,'size')), xr=r.w*(1.0/s.x), yr=r.h*(1.0/s.y)
 wids.v.map(wid=>{const w=unpack_widget(wid);draw_box(rclip(rect(r.x+w.size.x*xr,r.y+w.size.y*yr,w.size.w*xr,w.size.h*yr),r),0,w.show=='invert'?0:1)})
}
draw_lil=(size,align,bare,x)=>{
 const GAP=50, w=size.x-GAP; let xo=align==ALIGN.right?GAP: align==ALIGN.left?0: 0|(GAP/2)
 const r=image_make(size), t=frame; frame=draw_frame(r)
 if(lit(x)){
  const tk=Object.keys(x.v), tc=tk.length, tr=tk.length?x.v[tk[0]].length:0
  const hh=tc?3+font_h(FONT_BODY):3, ch=font_h(FONT_MONO), fh=font_h(FONT_BODY), rows=0|min((size.y-(hh+fh))/ch,tr)
  const f=dyad.take(NONE,x), cw=range(256).map(x=>0)
  for(let c=0;c<tc&&c<256;c++){
   const dr=rows<tr?rows-1:rows;cw[c]=0;for(let r=0;r<dr;r++){
    const s=show(x.v[tk[c]][r]);f.v[tk[c]].push(lms(s))
    cw[c]=max(cw[c],font_textsize(FONT_MONO,s).x+10)
   }if(rows<tr)f.v[tk[c]].push(lms(' \x7f'))
   cw[c]=min(100,max(cw[c],font_textsize(FONT_BODY,tk[c]).x+10))
  }
  let cols=0,tw=0,ve=0;for(let c=0;c<tc&&c<256;c++){if(tw+cw[c]>=w){ve=1;break};cols++;if(c+1<=tc&&c+1<=256)tw+=cw[c]}
  xo=align==ALIGN.right?size.x-tw: align==ALIGN.left?0: 0|((size.x-tw)/2)
  let cx=xo;for(let c=0;c<cols;c++){
   if(c)draw_vline(cx,0,hh+ch*rows+(rows==0),1);draw_text_fit(rect(cx+2,0,cw[c]-4,hh),tk[c],FONT_BODY,1)
   for(let r=0;r<rows;r++)draw_text_fit(rect(cx+2,hh+ch*r,cw[c]-4,ch),ls(f.v[tk[c]][r]),FONT_MONO,1)
   if(c+1<=cols)cx+=cw[c]
  }draw_hline(xo,cx,hh-1,1);const bh=hh+ch*rows+1+(rows==0);if(cx==xo)cx+=min(25,w)
  const desc=`(${tc} column${tc==1?'':'s'}, ${tr} row${tr==1?'':'s'}.)`
  const ds=font_textsize(FONT_BODY,desc);draw_text_fit(rect(xo+tw-ds.x,bh,w,fh),desc,FONT_BODY,1)
  draw_box(rect(xo,0,cx-xo,bh),0,1);if(ve)draw_vline(cx-1,0,bh,13)
 }
 else if(image_is(x)){
  const s=x.size, desc=`(${s.x} x ${s.y})`, ds=font_textsize(FONT_BODY,desc), mh=max(1,min(s.y,size.y-font_h(FONT_BODY))), mw=max(1,min(s.x,w))
  const scale=s.x==0&&s.y==0?1:min(mw/(s.x*1.0),mh/(s.y*1.0)), iw=0|(scale*s.x), ih=0|(scale*s.y)
  const b=rect(xo+(w-max(iw,ds.x)),0,iw,ih); draw_scaled(b,x,1),draw_box(b,0,1),draw_text(rect(b.x,b.y+b.h,ds.x,ds.y),desc,FONT_BODY,1)
 }
 else{const l=layout_plaintext(bare?ls(x): show(x,0),FONT_MONO,align,rect(w,size.y));draw_text_wrap(rect(xo,0,w,size.y),l,1)}
 for(let y=size.y-1;y>0;y--){let f=0;for(let x=0;x<size.x;x++)if(r.pix[x+(y*size.x)]){f=1;break}if(f){break}else{r.size.y--}}
 return frame=t,r
}
unpack_widget=x=>({
 size  :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 show  :ls(ifield(x,'show'  )),
 locked:lb(ifield(x,'locked')),
})
unpack_button=x=>({
 size  :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 text  :ls(ifield(x,'text'  )),
 font  :   ifield(x,'font'  ) ,
 style :ls(ifield(x,'style' )),
 show  :ls(ifield(x,'show'  )),
 locked:lb(ifield(x,'locked')),
})
unpack_slider=x=>({
 size  :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 font  :   ifield(x,'font'  ) ,
 format:ls(ifield(x,'format')),
 show  :ls(ifield(x,'show'  )),
 style :ls(ifield(x,'style' )),
 locked:lb(ifield(x,'locked')),
 step  :ln(ifield(x,'step'  )),
 value :ln(ifield(x,'value' )),
 min   :getpair(ifield(x,'interval')).x,
 max   :getpair(ifield(x,'interval')).y,
})
unpack_canvas=x=>({
 size     :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 scale    :ln(ifield(x,'scale'    )),
 border   :lb(ifield(x,'border'   )),
 draggable:lb(ifield(x,'draggable')),
 show     :ls(ifield(x,'show'     )),
 locked   :lb(ifield(x,'locked'   )),
})
unpack_field=x=>({
 size     :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 font     :   ifield(x,'font'     ) ,
 show     :ls(ifield(x,'show'     )),
 scrollbar:lb(ifield(x,'scrollbar')),
 border   :lb(ifield(x,'border'   )),
 style    :ls(ifield(x,'style'    )),
 locked   :lb(ifield(x,'locked'   )),
 align    :ALIGN[ls(ifield(x,'align'))],
})
unpack_field_value=x=>({
 table :   ifield(x,'value' ) ,
 scroll:ln(ifield(x,'scroll')),
})
unpack_grid=x=>({
 size     :rpair(getpair(ifield(x,'pos')),getpair(ifield(x,'size'))),
 widths   :ll(ifield(x,'widths')).map(ln),
 font     :   ifield(x,'font'     ) ,
 format   :ls(ifield(x,'format'   )),
 headers  :lb(ifield(x,'headers'  )),
 scrollbar:lb(ifield(x,'scrollbar')),
 lines    :lb(ifield(x,'lines'    )),
 show     :ls(ifield(x,'show'     )),
 locked   :lb(ifield(x,'locked'   )),
})
unpack_grid_value=x=>({
 table :   ifield(x,'value' ) ,
 scroll:ln(ifield(x,'scroll')),
 row   :ln(ifield(x,'row'   )),
})

let cursor={default:'default',point:'pointer',ibeam:'text',drag:'grabbing'}
image_tiles=(count,size,image)=>range(count).map(x=>image_copy(image,rect(0,x*size,size,size)))
TOOLS=image_tiles(12,16,image_read(
 '%%IMG0ABAAwAMABIAEgASABIAEgGTwlKxMqiQKJAIQAggCCAQEBAQEAAAAAAAAAAA//EACgAGAAYABgAFAAz/+H/wAAAAAAA'+
 'AAAAAA8+eAAYABAAAAAIABgAGAAQAAAACAAYAB588AAADwAIgBCAGQAnACIAQgBEAIQAiAEIARAB4AHAAYABAAAAAAAAP4HA'+
 'YgAUABgAGAAoAcuOBnAFQAOAAIAAgAEAAAAAAAAADAADAADAADAADAADAADAADAAAAAAAAAAAAAAcACIAJgArADLAInBCOIU'+
 'dAh4APgBdAJyBHEIcJBgYEAAAAADgcVjar69VVqqvVVaqrVVZqrDVYGrAHwAAAAAAAAAAA//+AAYABgAGAAYABgAGAAYABgA'+
 'H//wAAAAAAAAAAAAD//6qr1VWqq9VVqqvVVaqr1VWqq///AAAAAAAAAAAAAAAAB+AYGCAEQAKAAYABgAFAAiAEGBgH4AAAAA'+
 'AAAAAAAAAH4B1YKqxVVqqr1VWqq1VWKqwdWAfgAAAAAA=='
))
ARROWS=image_tiles(8,12,image_read(
 '%%IMG0AAwAYAAABgAJARCCIERAL/DxEIEQgRCBH4AAAAABH4EQgRCBEI/w9EAiIEEQgAkABgAAAAAABgAPAR+DP8d/7//xH4'+
 'EfgR+BH4AAAAABH4EfgR+BH4//93/jP8EfgA8ABgAAAAIABgAKARPiICRAJEAiICET4AoABgACAAQABgAFB3yEQERAJEAkQE'+
 'd8gAUABgAEAAIABgAOAR/jP+d/53/jP+Ef4A4ABgACAAQABgAHB3+Hf8d/53/nf8d/gAcABgAEAA=='
))
CHECK=image_read('%%IMG0AAkABwCAAcGDY8Y2bBw4CBAA')
CHECKS=[
 '%%IMG0AA8ADQAAf/BAEEAQQBBAEEAQQBBAEEAQQBB/8AAA',
 '%%IMG0AA8ADQAAf/BgMFBQSJBFEEIQRRBIkFBQYDB/8AAA',
 '%%IMG0AA8ADQAAVVAAAEAQAABAEAAAQBAAAEAQAABVUAAA',
 '%%IMG0AA8ADQAAVVAgIFBQCIBFEAIARRAIgFBQICBVUAAA',
].map(image_read)
LOCK     =image_read('%%IMG0AAgACDhERP7+/v4A')
ZOOM     =image_read('%%IMG0AAwADB4AIQBMgIxAv0C/QIxATIAhwB7gAHAAMA==')
CORNERS=['%%IMG0AAUABf/mxISA','%%IMG0AAUABfk4GAgI','%%IMG0AAUABYSGx+f4','%%IMG0AAUABQgIGT/4'].map(image_read)
GESTURES={
 left: image_read('%%IMG2ABAAEAAaIAQACiADAQIgAQAIIAMBBCABAAYgAwEGIAEABCADAQggBgEMIAIBDiAEAQwgAQACIAMBCCADAAQgAwEGIAEACCADAQQgAQAKIAMBAiABAAwgBAAi'),
 right:image_read('%%IMG2ABAAEAASIAQADCABAQIgAwAKIAEBBCADAAggAQEGIAMABCADAQggAwACIAEBDCAEAQ4gAgEMIAYBCCADAAQgAQEGIAMABiABAQQgAwAIIAEBAiADAAogBAAq'),
 up:   image_read('%%IMG2ABAAEAAHIAMADSABAQEgAQAMIAIBASACAAsgAQEDIAEACiACAQMgAgAJIAEBBSABAAggAgEFIAIAByABAQcgAQAGIAIBByACAAUgAQEJIAEABCACAQkgAgADIAEBCyABAAMgAQELIAEAAyAFAQMgBQAHIAEBAyABAAsgBQAF'),
 down: image_read('%%IMG2ABAAEAAFIAUACyABAQMgAQAHIAUBAyAFAAMgAQELIAEAAyABAQsgAQADIAIBCSACAAQgAQEJIAEABSACAQcgAgAGIAEBByABAAcgAgEFIAIACCABAQUgAQAJIAIBAyACAAogAQEDIAEACyACAQEgAgAMIAEBASABAA0gAwAH'),
}
RADIOS=[
 '%%IMG0ABAADgAAB4AYYCAQIBBACEAIQAhACCAQIBAYYAeAAAA=',
 '%%IMG0ABAADgAAB4Af4DhwMDBgGGAYYBhgGDAwOHAf4AeAAAA=',
 '%%IMG0ABAADgAAAAAAAAAAB4APwA/AD8APwAeAAAAAAAAAAAA=',
 '%%IMG0ABAADgAAB4Af4D/wP/B/+H/4f/h/+D/wP/Af4AeAAAA=',
].map(image_read)
ICONS=[
 '%%IMG0AAwADAAAAAM8BEPkQBRAFEAUQBRAFEAXf/AAAA==',
 '%%IMG0AAwADAABHwERgRFBEeEQIRAhECEQIRAhH+AAAA==',
 '%%IMG0AAwADAAAAgAGQA4jPqM+oz6gDiAGQAIAAAAAAA==',
 '%%IMG0AAwADAABH4EfwAHBH8M/wznDOcM5wz/hH+AAAA==',
 '%%IMG0AAwADAAAAgAFAAiBEEIjpERSLiEScAnwBWACAA==',
 '%%IMG0AAwADAAAAAEf4RShF6EUoRShF6EQoR/gAAAAAA==',
 '%%IMG0AAwADAABFUIqoRVCKqEVQiqhFUIqoRVCKqAAAA==',
 '%%IMG0AAwADAAAAAAAIABgAMRBhmMDNgEcAAgAAAAAAA==',
].map(image_read)
ICON={dir:0,doc:1,sound:2,font:3,app:4,lil:5,pat:6,chek:7,none:8}
PANGRAM='How razorback jumping-frogs can level six piqued gymnasts.'

// State

let uimode='interact', uicursor=0, enable_gestures=0, profiler=0
setmode=mode=>{
 n_play([NONE,lms('loop')])
 grid_exit(),field_exit(),bg_end_selection(),bg_end_lasso(),ob.sel=[],wid.active=-1,sc.others=[],dr.poly=[]
 msg.next_view   =(uimode!=mode)&&mode=='interact'
 msg.pending_loop=(uimode!=mode)&&mode=='interact'
 uimode=mode;if(mode!='interact')msg.pending_halt=1;if(mode!='draw')dr.fatbits=0
}

event_state=_=>({ // event state
 mu:0,md:0, clicktime:0,click:0,rdown:0,rup:0, dclick:0, clicklast:0,
 drag:0, tab:0, shift:0, alt:0, action:0, dir:0, exit:0, eval:0, scroll:0, hidemenu:0,
 pos:rect(), dpos:rect(), rawpos:rect(), rawdpos:rect(), shortcuts:{},
 callback:null, callback_rect:null, callback_drag:0
})
let ev=event_state()
over=r=>rin(r,ev.pos)
dover=r=>rin(r,ev.dpos)

wid_state=_=>({ // widget state
 active:0,count:0, scrolls:0,thumbid:0,thumbo:0, col_drag:0,col_num:0,col_orig:0,
 ingrid:0,g:null,gt:null,gv:null,pending_grid_edit:0,
 infield:0,f:null,ft:null,fv:null,field_dirty:0,change_timer:0,
 cursor:rect(),cursor_timer:0,
 hist:[],hist_cursor:0,
})
let wid=wid_state()
let ms={ // modal state
 type:null,subtype:null,in_modal:0,edit_json:0,old_wid:null,
 filter:0, grid:null,grid2:null, text:null,name:null,form0:null,form1:null,form2:null,
 desc:'',path:'',filter:'', message:null,verb:null, cell:rect(),
 from_listener:0,from_action:0, act_go:0,act_card:0,act_gomode:0,act_trans:0,act_transo:0,act_sound:0,
 time_curr:0,time_end:0,time_start:0, carda:null,cardb:null,trans:null,canvas:null,
}
let msg={ // interpreter event messages
 pending_drag:0,pending_halt:0,pending_view:0,pending_loop:0,next_view:0,overshoot:0,
 target_click:null,target_drag:null,target_release:null,target_order:null,target_run:null,target_link:null,target_change:null,target_navigate:null,
 arg_click:rect(),arg_drag:rect(),lastdrag:rect(),arg_release:rect(),arg_order:null,arg_run:null,arg_link:null,arg_change:null,arg_navigate:null,
}
let li={hist:[],vars:{},scroll:0} // listener state
let ob={sel:[],show_bounds:1,show_names:0,show_cursor:0,move:0,move_first:0,resize:0,resize_first:0,handle:-1,prev:rect(),orig:rect()} // object editor state
let sc={target:null,others:[],next:null, f:null,prev_mode:null,xray:0,status:''} // script editor state
script_save=x=>{const k=lms('script');mark_dirty();if(sc.target)iwrite(sc.target,k,x);if(sc.others)sc.others.map(o=>iwrite(o,k,x))}

draw_state=_=>({ // drawing tools state
 tool:'pencil',brush:3,pattern:1,fill:0,erasing:0, dither_threshold:0,
 show_widgets:1,show_anim:1,trans:0,trans_mask:0,fatbits:0,offset:rect(),
 show_grid:0,snap:0,grid_size:rect(32,32), sel_here:rect(),sel_start:rect(),limbo:null,limbo_dither:0,
 scratch:null,mask:null,omask:null, pickfill:0, poly:[]
})
let dr=draw_state()
settool=tool=>{setmode('draw'),dr.tool=tool}
bg_pat=_=>(dr.trans_mask&&dr.pattern==0)?32:dr.pattern
bg_fill=_=>(dr.trans_mask&&dr.fill==0)?32:dr.fill
bg_has_sel=_=>dr.tool=='select'&&(dr.sel_here.w>0||dr.sel_here.h>0)
bg_has_lasso=_=>dr.tool=='lasso'&&dr.mask!=null
sint=(x,a)=>a*(0|((0|(x+a/2))/a))
snap=p=>!dr.snap?p:rect(sint(p.x,dr.grid_size.x),sint(p.y,dr.grid_size.y),p.w,p.h) // position only
snapr=r=>rpair(snap(r),snap(rect(r.w,r.h))) // position + dimensions
snap_delta=p=>{const a=snap(p);return rect(a.x-p.x,a.y-p.y)}
fat_offset=p=>{const d=rect(frame.size.x/FAT,frame.size.y/FAT);dr.offset=rint(rect(p.x-d.x/2,p.y-d.y/2))}
fat_clip=_=>dr.fatbits?rint(rect(dr.offset.x,dr.offset.y,frame.size.x/FAT,frame.size.y/FAT)): frame.clip
fat_to_card=a=>rint(rect((a.x/FAT)+dr.offset.x,(a.y/FAT)+dr.offset.y))
card_to_fat=a=>dr.fatbits?rect((a.x-dr.offset.x)*FAT,(a.y-dr.offset.y)*FAT,a.w*FAT,a.h*FAT):a
mark_dirty=_=>{dirty=1}

let au={target:null,mode:'stopped', head:0,sel:rect(), hist:[],hist_cursor:0, clip:null,tick:null, record_stream:null,norecord:0} // audio editor state
byte_to_sample=b=>(b<<24>>24)/128
sample_to_byte=s=>0xFF&clamp(-127,s*128,127)

// Menus

const menu={heads:[],items:[],x:0,active:-1,stick:-1,lw:-1,sz:rect()}
no_menu=_=>menu.active==-1&&menu.stick==-1
in_layer=_=>no_menu()&&(ms.type?ms.in_modal:1)&&((!running()&&!msg.overshoot)||ms.type!=null)
in_widgets=_=>ms.type!=null?ms.in_modal:1
menus_off=_=>lb(ifield(deck,'locked'))||(uimode=='draw'&&ev.hidemenu&&ms.type==null)
menu_head=(name,enabled,t,b)=>({name,enabled,t,b}) // string,bool,rect,rect
menu_entry=(name,enabled,check,shortcut,t,b)=>({name,enabled,check,shortcut,t,b}) // string,bool,bool,char,rect,rect
menus_clear=_=>(menu.active=-1,menu.stick=-1)
menu_setup=_=>(menu.x=10,menu.heads=[],menu.sz=rect(),menu.active=-1)
menu_bar=(name,enabled)=>{
 if(menus_off())enabled=0
 const t=rpair(rect(menu.x,2),font_textsize(FONT_MENU,name)), b=rect(t.x-5,0,t.w+10,t.h+3), i=menu.heads.length
 menu.heads.push(menu_head(name,enabled,t,b)), menu.x=b.x+b.w+5
 if(ev.click&&enabled&&over(b)){ev.mu=0;if(menu.stick==-1)menu.stick=i}
 if(menu.stick!=-1&&enabled&&over(b))menu.stick=i,menu.lw=0
 if(menu.stick==-1){
  if(ev.drag&&enabled&&over(b)&&ev.dpos.y<b.h)ev.dpos=ev.pos,menu.lw=0
  if((ev.drag||ev.mu)&&enabled&&rin(b,ev.dpos))menu.active=i
 }if(i==menu.active||i==menu.stick)menu.sz=rect(b.x,b.h,max(b.w,menu.lw),0),menu.items=[]
 if(ev.md&&over(b)&&enabled)ev.md=0
}
shortcut_w=c=>!c?0: 10+font_textsize(FONT_MENU,'^'+c).x
menu_check=(name,enabled,check,shortcut,func)=>{
 const sc=enabled&&shortcut&&ev.shortcuts[shortcut]; if(sc)delete ev.shortcuts[shortcut];
 if(menu.heads.length-1!=menu.active&&menu.heads.length-1!=menu.stick)return sc
 const t=name?rpair(rect(menu.sz.x+5+8,menu.sz.y+menu.sz.h+2),font_textsize(FONT_MENU,name)): rect(menu.sz.x,menu.sz.y+menu.sz.h+2,1,1)
 if(shortcut)t.w+=shortcut_w(shortcut)
 const b=rect(menu.sz.x,menu.sz.y+menu.sz.h,max(menu.sz.w,t.w+10+8),t.h+4)
 menu.items.push(menu_entry(name,enabled,check,shortcut,t,b)), menu.sz=runion(menu.sz,b)
 if(enabled&&over(b)&&func)ev.callback=func,ev.callback_rect=rcopy(b),ev.callback_drag=1
 return sc||(enabled&&ev.mu&&over(b))
}
menu_item=(name,enabled,shortcut,func)=>menu_check(name,enabled,-1,shortcut,func)
menu_separator=_=>menu_check(0,0,0,0,0)
menu_finish=_=>{
 if(menus_off())return
 const b=rect(0,0,context.size.x,3+font_h(FONT_MENU)); draw_rect(b,32),draw_hline(0,b.w,b.h,1); const pal=deck.patterns.pal.pix
 menu.heads.map((x,i)=>{
  let a=x.enabled&&(over(x.b)||i==menu.stick||i==menu.active);if(ev.drag&&!dover(b))a=0
  draw_text(x.t,x.name,FONT_MENU,x.enabled?1:13);if(a)draw_invert(pal,x.b)
 })
 if(!menu.sz.w)return
 draw_shadow(menu.sz,1,32,1);menu.lw=0;let sw=0;menu.items.map(x=>{menu.lw=max(menu.lw,x.b.w),sw=max(sw,shortcut_w(x.shortcut))})
 menu.items.map(x=>{
  const o=over(x.b)&&x.name&&x.enabled
  if(x.name){draw_text(x.t,x.name,FONT_MENU,x.enabled?1:13)}else{draw_rect(rect(x.t.x+2,x.t.y,menu.sz.w-5,1),19)}
  if(x.check==1)draw_icon(rect(menu.sz.x+2,x.t.y+3),CHECK,x.enabled?1:13)
  if(x.shortcut)draw_text(rect(menu.sz.x+menu.sz.w-3-sw+10,x.t.y),'^'+x.shortcut,FONT_MENU,x.enabled?1:13)
  if(o)draw_invert(pal,inset(x.b,1))
 });if(ev.mu)menu.stick=-1
}

// Widgets

widget_setup=_=>{
 if(ev.mu||wid.active==-1)wid.col_drag=0;if(wid.active>=wid.count)wid.active=0
 if((uimode=='interact'||ms.type!=null)&&ev.tab&&wid.count&&!(wid.infield&&wid.f.style=='code')){
  if(wid.ingrid)grid_exit()
  if(wid.infield)field_exit()
  wid.active+=ev.shift?-1:1
  if(wid.active<0)wid.active+=wid.count;wid.cursor=rect()
 }
 if(uimode!='interact'&&uimode!='script'&&ms.type==null){
  if(wid.ingrid ||wid.gv!=null)grid_exit()
  if(wid.infield||wid.fv!=null)field_exit()
  wid.active=-1
 }wid.count=0,wid.scrolls=0,wid.ingrid=0,wid.infield=0
}

scrollbar=(r,n,line,page,scroll,visible,inverted)=>{
 const addscroll=x=>scroll=clamp(0,scroll+x,n)
 addscroll(0);const sz=ARROWS[0].size.x+2, fcol=!in_layer()?13:inverted?32:1, bcol=inverted?1:32
 const b=rect(r.x+r.w-sz-2,r.y,sz+2,r.h), rr=rect(r.x+1,r.y,visible?r.w-b.w-1:r.w-2,r.h-2), pal=deck.patterns.pal.pix
 let dragging_thumb=(wid.thumbid==wid.scrolls++)&&dover(b)
 if(visible){
  draw_box(b,0,fcol)
  arrow=(bb,base,dir)=>{
   const a=n>0&&over(bb)&&!dragging_thumb, o=a&&(ev.mu||ev.drag)&&dover(r)
   draw_box(bb,0,fcol),draw_icon(rect(bb.x+2,bb.y+2),ARROWS[base+(o?2:0)],fcol)
   if(o&&ev.md)addscroll(line*dir);if(a&&!ev.drag)uicursor=cursor.point
  }
  arrow(rect(b.x,b.y         ,sz+2,sz+2),0,-1)
  arrow(rect(b.x,b.y+b.h-sz-2,sz+2,sz+2),1, 1)
  if(n<=0||!in_layer())return {size:rr,scroll}
  const s=rect(b.x+1,b.y+sz+2,b.w-2,b.h-2*(sz+2));draw_rect(s,inverted?9:12)
  const thumb_height=0|max(16,s.h/(1+n)), thumb_y=0|((s.h-thumb_height)*(scroll/n)), thumb=rect(s.x,s.y+thumb_y,s.w,thumb_height)
  if(in_layer()&&ev.md&&over(thumb)){wid.thumbid=wid.scrolls-1,wid.thumbo=ev.dpos.y-thumb.y,dragging_thumb=1}
  if(in_layer()&&ev.drag&&dragging_thumb){
   const capped=max(s.y,min(s.y+s.h-thumb.h,ev.pos.y-wid.thumbo))-s.y
   thumb.y=capped+s.y,scroll=max(0,0|min((capped/(s.h-thumb_height))*n,n)),uicursor=cursor.drag
  }
  if(in_layer()&&ev.mu&&dragging_thumb)wid.thumbid=-1
  draw_rect(thumb,bcol),draw_box(thumb,0,fcol)
  inner=(bb,dir)=>{
   if(!dragging_thumb&&over(bb))uicursor=cursor.point
   if(!dragging_thumb&&ev.mu&&over(bb))draw_invert(pal,bb),addscroll(page*dir)
  }
  if(thumb_y>0          )inner(rect(s.x,s.y                ,s.w,     thumb_y         ),-1)
  if(thumb_y+thumb.h<s.h)inner(rect(s.x,s.y+thumb_y+thumb.h,s.w,s.h-(thumb_y+thumb.h)), 1)
 }
 if(in_layer()&&over(runion(r,b))&&ev.scroll)addscroll(ev.scroll*line)
 return {size:rr,scroll}
}

widget_button=(target,x,value,func)=>{
 const l=x.locked||!in_layer(), pal=deck.patterns.pal.pix, font=x.font||FONT_MENU;let b=x.size
 const fcol=l?13:x.show=='invert'?32:1, bcol=x.show=='invert'?1:32, scol=x.show=='invert'?32:1
 const sel=!l&&x.show!='none'&&x.style!='invisible'&&wid.active==wid.count
 const a=!l&&dover(b)&&over(b), cs=sel&&!func&&ev.action, cl=cs||((ev.md||ev.drag)&&a), cr=cs||(ev.mu&&a)
 if(func&&a){ev.callback=func,ev.callback_rect=rcopy(b)}
 if(!l&&over(b)&&!ev.drag)uicursor=cursor.point
 if(x.show=='none')return cr; let ar=inset(b,2)
 if(x.style=='round'){
  draw_boxr(b,fcol,bcol,x.show!='transparent')
  draw_textc(inset(b,3),x.text,font,fcol)
  if(sel)draw_box(ar,0,13);if(cl)draw_invert(pal,ar)
 }
 if(x.style=='rect'){
  if(cl){b=rect(b.x+1,b.y+1,b.w-1,b.h-1),ar=rect(ar.x+1,ar.y+1,ar.w-1,ar.h-1);if(x.show!='transparent')draw_rect(b,bcol);draw_box(b,0,fcol)}
  else  {b=rect(b.x  ,b.y  ,b.w-1,b.h-1),ar=rect(ar.x  ,ar.y  ,ar.w-1,ar.h-1);draw_shadow(b,fcol,bcol,x.show!='transparent')}
  draw_textc(inset(b,3),x.text,font,fcol);if(sel)draw_box(ar,0,13)
 }
 if(x.style=='check'||x.style=='radio'){
  if(x.show!='transparent')draw_rect(b,bcol)
  const ts=font_textsize(font,x.text), cdim=(x.style=='check'?CHECKS[0]:RADIOS[0]).size, bh=max(ts.y,cdim.y)
  const br=rect(b.x,b.y+(0|((b.h-bh)/2)),b.w,bh), to=rclip(b,rect(br.x+cdim.x,0|(br.y+(br.h-ts.y)/2),b.w-cdim.x,ts.y))
  draw_rect(rect(br.x+1,br.y+1,cdim.x-4,cdim.y-3),bcol)
  if(x.style=='check'){draw_icon(rect(br.x,br.y),CHECKS[(value^(cl||cr))+2*x.locked],scol)}
  else{const p=rect(br.x,br.y);draw_icon(p,RADIOS[3],bcol),draw_icon(p,RADIOS[cl||cr?1:0],fcol);if(value)draw_icon(p,RADIOS[2],fcol)}
  draw_text_fit(to,x.text,font,fcol);ar=to;if(sel)draw_box(rect(to.x-2,to.y-1,to.w+2,to.h+2),0,13);if(cl)draw_invert(pal,ar)
 }
 if(x.style=='invisible'){draw_textc(inset(b,3),x.text,font,fcol);if(cl)draw_invert(pal,ar)}
 if(target&&cr)msg.target_click=target
 if(!x.locked&&in_widgets())wid.count++
 return cr
}

widget_slider=(target,x)=>{
 const l=x.locked||!in_layer(), pal=deck.patterns.pal.pix, font=x.font||FONT_MENU
 let b=x.size, fcol=l?13:x.show=='invert'?32:1, bcol=x.show=='invert'?1:32, bpat=x.show=='invert'?9:12
 const sel=!l&&x.show!='none'&&wid.active==wid.count, ov=x.value
 if(x.show=='none')return
 const t=ls((x.style=='bar'||x.style=='compact')?dyad.format(lms(x.format),lmn(x.value)):lms(''))
 const oc=frame.clip; frame.clip=rclip(b,frame.clip)
 const hv_btn=(bb,dir,ba)=>{
  const a=!l&&over(bb), o=a&&(ev.mu||ev.drag)&&dover(bb)
  if(o&&ev.md)x.value+=(dir*x.step); if(a)uicursor=cursor.point
  draw_rect(bb,bcol),draw_box(bb,0,fcol),draw_icon(rint(rect(bb.x+(bb.w-12)/2,bb.y+(bb.h-12)/2)),ARROWS[ba+(o?2:0)],fcol)
 }
 const hv_tsz=(axis,tf,av,as)=>{
  let drag=(wid.thumbid==wid.scrolls++)&&dover(b),
  n=0|((x.max-x.min)/x.step), ts=0|max(16,axis/(1+n)), tp=0|(((x.value-x.min)/max(1,(x.max-x.min)))*(axis-ts)), thumb=rint(tf(tp,ts))
  if(!l){
   if(ev.md&&over(thumb))wid.thumbid=wid.scrolls-1,wid.thumbo=ev.dpos[av]-thumb[av],drag=1
   if(ev.drag&&drag){
    const capped=max(b[av],min(b[av]+b[as]-thumb[as],ev.pos[av]-wid.thumbo))-b[av]
    thumb[av]=capped+b[av],uicursor=cursor.drag,x.value=x.min+capped*((x.max-x.min)/(b[as]-ts))
   }if(ev.mu&&drag)wid.thumbid=-1
  }draw_rect(thumb,bcol),draw_box(thumb,0,fcol);return {drag,thumb}
 }
 const hv_gap=(drag,bb,dir)=>{if(!l&&!drag&&over(bb)){uicursor=cursor.point;if(ev.mu)draw_invert(pal,bb),x.value+=(dir*10*x.step);}}
 if(x.style=='horiz'||x.style=='vert'){if(x.show!='transparent')draw_rect(b,bpat);draw_box(b,0,sel?13:fcol)}
 if(x.style=='horiz'){
  hv_btn(rect(b.x,b.y,16,b.h),-1,4),hv_btn(rect(b.x+b.w-16,b.y,16,b.h),1,5),b=rect(b.x+16,b.y+1,b.w-32,b.h-2)
  const a=hv_tsz(b.w,(tp,ts)=>rect(b.x+tp,b.y,ts,b.h),'x','w'), drag=a.drag, thumb=a.thumb
  hv_gap(drag,rect(b.x,b.y,thumb.x-b.x,b.h),-1),hv_gap(drag,rect(thumb.x+thumb.w,b.y,b.w-(thumb.x+thumb.w-b.x),b.h),1)
 }
 if(x.style=='vert'){
  hv_btn(rect(b.x,b.y,b.w,16),-1,0);hv_btn(rect(b.x,b.y+b.h-16,b.w,16),1,1),b=rect(b.x+1,b.y+16,b.w-2,b.h-32)
  const a=hv_tsz(b.h,(tp,ts)=>rect(b.x,b.y+tp,b.w,ts),'y','h'), drag=a.drag, thumb=a.thumb
  hv_gap(drag,rect(b.x,b.y,b.w,thumb.y-b.y),-1),hv_gap(drag,rect(b.x,thumb.y+thumb.h,b.w,b.h-(thumb.y+thumb.h-b.y)),1)
 }
 if(x.style=='bar'){
  fcol=x.locked?(x.show=='invert'?32:1):fcol
  if(x.show!='transparent')draw_rect(b,bcol);draw_box(b,0,sel?13:fcol)
  b=inset(b,2),draw_textc(b,t,font,fcol);if(!l&&over(b))uicursor=cursor.point
  const f=(x.max==x.min)?0:((x.value-x.min)/(x.max-x.min))*b.w;draw_invert(pal,rect(b.x,b.y,0|f,b.h))
  if(!l&&dover(b)&&(ev.md||ev.drag)){x.value=x.min+(ev.pos.x-b.x)*((x.max-x.min)/b.w),uicursor=cursor.drag}
 }
 if(x.style=='compact'){
  if(x.show=='transparent')draw_rect(rect(b.x+1,b.y+1,13,b.h-2),bcol),draw_rect(rect(b.x+b.w-14,b.y+1,13,b.h-2),bcol)
  draw_boxr(b,fcol,bcol,x.show!='transparent'),draw_textc(rect(b.x+14,b.y,b.w-28,b.h),t,font,fcol)
  const comp_btn=(xo,dir,ba,li)=>{
   const bb=rect(b.x+xo,b.y,14,b.h), a=!l&&over(bb), o=a&&(ev.mu||ev.drag)&&dover(bb)
   if(o&&ev.md)x.value+=(dir*x.step); if(a)uicursor=cursor.point
   draw_icon(rect(bb.x+1,0|(b.y+(b.h-12)/2)),ARROWS[ba+(o?2:0)],fcol),draw_vline(bb.x+li,b.y+1,b.y+b.h-1,sel?13:fcol)
  };comp_btn(0,-1,4,13),comp_btn(b.w-14,1,5,0)
 }
 if(sel&&ev.dir=='up')x.value-=x.step;if(sel&&ev.dir=='down')x.value+=x.step
 if(in_layer()&&over(frame.clip)&&ev.scroll)x.value+=x.step*ev.scroll; x.value=slider_normalize(target,x.value)
 if(target&&Math.abs(ov-x.value)>(x.step/2)){msg.target_change=target,msg.arg_change=lmn(x.value);iwrite(target,lms('value'),msg.arg_change),mark_dirty()}
 if(!x.locked&&in_widgets())wid.count++
 frame.clip=oc
}

widget_canvas=(target,x,image)=>{
 if(x.show=='none')return; const b=x.size, pal=deck.patterns.pal.pix
 if(x.show=='solid'){if(image){draw_scaled(b,image,1)}else{draw_rect(b,0)}}
 if(image&&x.show=='transparent')draw_scaled(b,image,0)
 if(image&&x.show=='invert')draw_invert_scaled(pal,b,image)
 if(x.border){if(x.show=='invert'){draw_boxinv(pal,b)}else{draw_box(b,0,1)}}
 if(!target||!in_layer())return
 if(x.draggable){
  const sel=ob.sel.length&&ob.sel[0]==target
  if     (ev.md&&dover(b))msg.target_click  =target,msg.arg_click  =rect(b.x,b.y),ob.sel=[target],ob.prev=rint(rsub(ev.pos,b))
  else if(ev.mu     &&sel)msg.target_release=target,msg.arg_release=rsub(ev.dpos,ob.prev),ob.sel=[]
  else if(ev.drag   &&sel)msg.target_drag   =target,msg.arg_drag   =rsub(ev.dpos,ob.prev)
 }else if(dover(b)){
  const p=rint(rect((ev.pos.x-b.x)/x.scale,(ev.pos.y-b.y)/x.scale)), dp=p.x!=msg.lastdrag.x||p.y!=msg.lastdrag.y, im=over(b)
  if     (ev.md          )msg.target_click  =target,msg.arg_click  =p,msg.lastdrag=p
  else if(ev.mu          )msg.target_release=target,msg.arg_release=p
  else if(ev.drag&&dp&&im)msg.target_drag   =target,msg.arg_drag   =p,msg.lastdrag=p
 }
}

grid_exit=_=>{
 wid.ingrid=0,wid.gv=null,wid.gt=null
 if(wid.hist)wid.hist=[],wid.hist_cursor=0
}
widget_grid=(target,x,value)=>{
 if(x.show=='none')return 0; const hfnt=FONT_BODY, hsize=x.headers?font_h(hfnt)+5:0, showscroll=x.size.h<=(50+hsize)||x.size.w<16?0:x.scrollbar
 const fnt=x.font?x.font:FONT_MONO, os=value.scroll, or=value.row, files=x.headers==2, headers=files||x.size.h<=hsize?0:x.headers
 const tk=Object.keys(value.table.v), nr=count(value.table), nc=tk.length, rh=font_h(fnt)+(x.lines?5:3)
 const fcol=!in_layer()?13:x.show=='invert'?32:1, bcol=x.show=='invert'?1:32, b=x.size, pal=deck.patterns.pal.pix
 let sel=in_layer()&&x.show!='none'&&wid.active==wid.count
 if(in_layer()&&dover(b)&&(ev.md||ev.mu||ev.drag)){if(!sel&&wid.gv)grid_exit(); wid.active=wid.count,sel=1}
 if(sel&&in_layer()&&!over(b)&&ev.md)sel=0,wid.active=-1,grid_exit()
 if(sel){if(wid.fv)field_exit();wid.ingrid=1,wid.g=x,wid.gv=value,wid.gt=target}; if(x.show!='transparent')draw_rect(b,bcol)
 const bh=rect(b.x,b.y,b.w,headers?font_h(hfnt)+5:0), nrd=0|min(nr,((b.h-bh.h+1)/rh)), scrollmax=nr-nrd
 const sbar=scrollbar(rect(b.x,b.y+(headers?bh.h-1:0),b.w,b.h-(headers?bh.h-1:0)),scrollmax,1,nrd,value.scroll,showscroll,x.show=='invert')
 const bb=sbar.size; value.scroll=sbar.scroll, hwt=x.widths.reduce((x,y)=>x+y,0)
 draw_box(x.lines?b:rect(b.x,bb.y,b.w,b.h-(bb.y-b.y)),0,sel?13:fcol)
 const cw=n=>0|(n>=x.widths.length?((bb.w-hwt)/(nc-x.widths.length)):x.widths[n])
 if(x.lines)draw_rect(bh,fcol);if(nc<=0)draw_textc(inset(bb,1),'(no data)',hfnt,fcol)
 for(let z=0,cols=0,cx=0;z<nc&&cx+cw(cols)<=bb.w;z++){
  const hs=rect(bh.x+4+cx,bh.y+1,cw(cols)-5,bh.h-2)
  if(headers){
   draw_textc(hs,tk[z],hfnt,x.lines?bcol:fcol)
   const oa=target&&in_layer()&&over(hs)&&((ev.drag||ev.mu)?dover(hs):1)&&!wid.col_drag
   if(oa&&(ev.md||ev.drag))draw_invert(pal,hs); if(oa&&!ev.drag)uicursor=cursor.point
   if(oa&&ev.mu)msg.target_order=target,msg.arg_order=lms(tk[z])
  }
  if(cols&&x.lines)draw_invert(pal,rect(hs.x-3,b.y+1,1,b.h-2));cx+=cw(cols),cols++
  for(let y=0;y<nrd;y++){
   const cell=rect(hs.x-3,bb.y+rh*y+1,hs.w+5,rh-1), v=value.table.v[tk[z]][y+value.scroll]
   const cf=ls(dyad.format(lms(`%${x.format[z]=='L'?'s':(x.format[z]||'s')}`),v)), ip=rcenter(cell,ICONS[0].size)
   const oc=frame.clip; frame.clip=rclip(cell,frame.clip)
   if     (x.format[z]=='I'){const i=clamp(0,ln(v),8);if(i<8)draw_icon(ip,ICONS[i],fcol)}
   else if(x.format[z]=='B'){if(lb(v))draw_icon(ip,ICONS[ICON.chek],fcol)}
   else{draw_text_fit(rect(hs.x+1,bb.y+rh*y,hs.w-2,rh),cf,fnt,fcol)}
   frame.clip=oc
   if(sel&&ev.dclick&&!x.locked&&over(cell)){
    const f=x.format[z]||'s', tc=rect(z,y+value.scroll)
    if     (f=='I'||f=='L'){} // no editing allowed
    else if(f=='B'||f=='b'){grid_edit_cell(tc,lmn(!lb(v)))} // toggle
    else{wid.pending_grid_edit=1,ms.cell=tc,ms.text=fieldstr(lms(cf))}
   }
  }
 }
 if(!x.locked&&in_layer()&&target)for(let z=0,cx=bh.x;z<nc;cx+=cw(z),z++){
  const h=rect(cx+cw(z)-1,bh.y,5,bh.h);if(h.x+h.w>b.x+b.w)break
  if(over(h))draw_vline(h.x+2,h.y,h.y+h.h,13)
  if(ev.md&&dover(h))wid.col_drag=1,wid.col_num=z,wid.col_orig=cw(z)
  if(sel&&wid.col_drag&&wid.col_num==z&&ev.drag){
   const s=min(max(10,wid.col_orig+(ev.pos.x-ev.dpos.x)),bb.w-10),i=z;uicursor=cursor.drag
   iwrite(target,lms('widths'),lml(range(max(x.widths.length,i+1)).map(z=>lmn(i==z?s:cw(z))))),mark_dirty()
  }
 }
 const rowb=n=>rect(bb.x,bb.y+rh*n,bb.w,rh)
 const rowh=n=>inset(rect(bb.x+1,bb.y+rh*n+2,bb.w-2,rh-3),x.lines?0:-1)
 let clicked=0,rsel=0;for(let y=0;y<nrd;y++){
  if(y&&x.lines)draw_hline(bb.x,bb.x+bb.w,bb.y+rh*y,fcol); const ra=in_layer()&&over(bb)&&over(rowb(y))
  if(ra&&(ev.md||ev.drag))draw_invert(pal,rowh(y)),rsel=1; if(ra&&ev.mu)clicked=1,value.row=y+value.scroll; if(ra&&!ev.drag)uicursor=cursor.point
 }
 const rr=value.row-value.scroll;if(!rsel&&rr>=0&&rr<nrd)draw_invert(pal,rowh(rr))
 if(target&&os!=value.scroll)iwrite(target,lms('scroll'),lmn(value.scroll)),mark_dirty()
 if(target&&or!=value.row)iwrite(target,lms('row'),lmn(value.row)),mark_dirty()
 if(target&&clicked)msg.target_click=target,msg.arg_click=rect(0,value.row)
 if(in_widgets())wid.count++
 return files?((clicked&&ev.dclick)||(sel&&ev.action)): clicked
}
grid_format=_=>lms(wid.g.format.length?wid.g.format:'s'.repeat(Object.keys(wid.gv.table.v).length))
grid_apply=v=>{
 wid.gv.table=v,wid.gv.row=-1;if(!wid.gt)return
 iwrite(wid.gt,lms('value'),v),iwrite(wid.gt,lms('row'),lmn(-1)),mark_dirty()
 msg.target_change=wid.gt,msg.arg_change=v
}
grid_undo=_=>{const x=wid.hist[--(wid.hist_cursor)];grid_apply(x[0])}
grid_redo=_=>{const x=wid.hist[(wid.hist_cursor)++];grid_apply(x[1])}
grid_edit=v=>{wid.hist=wid.hist.slice(0,wid.hist_cursor),wid.hist.push([wid.gv.table,v]),grid_redo()}
grid_deleterow=_=>grid_edit(dyad.drop(lml([lmn(wid.gv.row)]),wid.gv.table))
grid_insertrow=_=>{
 const f=grid_format(), x=wid.gv.table.v, r={}, s=wid.gv.row+1
 Object.keys(x).map((k,col)=>{r[k]=range(x[k].length+1).map(i=>(i==s)?('sluro'.indexOf(f[col])>=0?lms(''):NONE): x[k][i-(i>=s?1:0)])})
 grid_edit(lmt(r))
}
grid_edit_cell=(cell,v)=>{
 const r={};Object.keys(wid.gv.table.v).map((k,i)=>{r[k]=wid.gv.table.v[k].slice(0);if(i==cell.x)r[k][cell.y]=v})
 grid_edit(lmt(r))
}
grid_keys=(code,shift)=>{
 const fnt=wid.g.font?wid.g.font:FONT_MONO, hfnt=FONT_BODY;let m=0, nr=count(wid.gv.table), r=wid.gv.row
 const rh=font_h(fnt)+5, bh=wid.g.headers?font_h(hfnt)+5:0, nrd=min(nr,0|((wid.g.size.h-bh+1)/rh))
 if(code=='ArrowUp'  ){m=1;if(r==-1){r=0}else{r-=1}}
 if(code=='ArrowDown'){m=1;if(r==-1){r=0}else{r+=1}}
 if(code=='PageUp'   ){m=1;if(r==-1)r=0;r-=nrd}
 if(code=='PageDown' ){m=1;if(r==-1)r=0;r+=nrd}
 if(code=='Home'     ){m=1,r=0}
 if(code=='End'      ){m=1,r=nr-1}
 if(code=='Backspace'||code=='Delete')grid_deleterow()
 if(!m)return
 wid.gv.row=r=max(0,min(r,nr-1));if(wid.gt){iwrite(wid.gt,lms('row'),lmn(r)),mark_dirty(),msg.target_click=wid.gt,msg.arg_click=rect(0,r)}
 const os=wid.gv.scroll;if(r-os<0)wid.gv.scroll=r;if(r-os>=nrd)wid.gv.scroll=r-(nrd-1)
 if(wid.gt&&os!=wid.gv.scroll)iwrite(wid.gt,lms('scroll'),lmn(wid.gv.scroll)),mark_dirty()
}

layout_index=(x,p)=>{
 for(let z=0;z<x.lines.length;z++){
  const l=x.lines[z].pos  ;if(p.y>l.y+l.h)continue
  const r=x.lines[z].range;if(p.y<l.y    )return r.x
  for(let i=r.x;i<=r.y;i++){const g=x.layout[i].pos;if(p.x<g.x+(g.w/2))return i;}
  return z==x.lines.length-1?x.layout.length: r.y
 }return x.layout.length
}
layout_last=(x,font)=>x.layout.length>0?last(x.layout): {pos:rect(0,0,1,font_h(font)),line:0,char:'\0',font,arg:NONE}
layout_cursor=(x,index,font,f)=>{
 const bw=f.size.w-5-(f.scrollbar?ARROWS[0].size.x+3:0), bx=f.align=='center'?0|(bw/2): f.align==ALIGN.right?bw: 0
 const r=x.layout.length>0?rcopy(x.layout[min(index,x.layout.length-1)].pos):rect(bx,0,1,font_h(font))
 if(index>=x.layout.length){if(layout_last(x,font).char=='\n'){r.x=0,r.y+=r.h}else{r.x+=r.w-1}}
 r.w=1;return r
}
field_change=_=>{
 if(!wid.field_dirty||!wid.ft)return
 iwrite(wid.ft,lms('value'),wid.fv.table),mark_dirty()
 msg.target_change=wid.ft, msg.arg_change=rtext_string(wid.fv.table)
}
field_exit=_=>{field_change();wid.infield=0,wid.fv=null,wid.ft=null,wid.cursor=rect(),wid.field_dirty=0,wid.change_timer=0}
widget_field=(target,x,value)=>{
 if(x.show=='none')return; if(x.size.h<=50||x.size.w<16)x.scrollbar=0
 const l=!in_layer(), fnt=x.font?x.font: x.style=='code'?FONT_MONO: FONT_BODY, b=x.size, pal=deck.patterns.pal.pix
 const fcol=(l&&!x.locked)?13:x.show=='invert'?32:1, bcol=x.show=='invert'?1:32, os=value.scroll
 if(x.show!='transparent')draw_rect(rclip(b,frame.clip),bcol); if(x.border)draw_box(b,0,fcol)
 let bi=inset(b,2);if(x.scrollbar)bi.w-=ARROWS[0].size.x+3
 if(!l&&!x.locked&&over(bi)&&(ev.drag?dover(bi):1))uicursor=cursor.ibeam
 const layout=layout_richtext(deck,value.table,fnt,x.align,bi.w), last=layout_last(layout,fnt), eol=last.char!='\n'?0: last.pos.h
 const sbar=scrollbar(b,max(0,(last.pos.y+last.pos.h+eol)-bi.h),10,bi.h,value.scroll,x.scrollbar,x.show=='invert');value.scroll=sbar.scroll
 let sel=!x.locked&&!l&&wid.active==wid.count
 // find active link (if any)
 let alink=null;if(x.locked&&!sel&&in_layer()&&x.locked&&(ev.md||ev.drag))for(let z=0;z<layout.layout.length;z++){
  const g=layout.layout[z], pos=rcopy(g.pos);if(pos.w<1)continue // skip squashed spaces/newlines
  pos.y-=value.scroll;if(pos.y+pos.h<0||pos.y>bi.h)continue; pos.x+=bi.x, pos.y+=bi.y // coarse clip
  if(lis(g.arg)&&count(g.arg)&&dover(pos)&&over(pos)){alink=g.arg;break}
 }
 if(!x.locked&&!l&&dover(bi)&&(ev.md||ev.mu||ev.drag)){
  const i=layout_index(layout,rect(ev.pos.x-bi.x,ev.pos.y-bi.y+value.scroll))
  if(ev.md){wid.cursor.x=wid.cursor.y=i}else{wid.cursor.y=i}
  if(ev.dclick){ // double-click to select a word or whitespace span:
   let a=0, w=layout.layout.length&&/\s/g.test(layout.layout[min(wid.cursor.y,layout.layout.length-1)].char)
   a=wid.cursor.y;while(a>=0&&a<layout.layout.length&&(w^!/\s/g.test(layout.layout[a].char)))a--;wid.cursor.x=a+1
   a=wid.cursor.y;while(      a<layout.layout.length&&(w^!/\s/g.test(layout.layout[a].char)))a++;wid.cursor.y=a
  }
  const c=layout_cursor(layout,wid.cursor.y,fnt,x);c.y-=value.scroll, ch=min(bi.h,c.h)
  if(c.y<0)value.scroll-=4;if(c.y+ch>bi.h)value.scroll+=clamp(1,(c.y+ch)-bi.h,4) // drag to scroll!
  if(!sel&&wid.fv)field_exit(); wid.active=wid.count,sel=1
 }
 if(sel&&in_layer()&&!over(b)&&ev.md)sel=0,wid.active=-1,field_exit()
 if(sel){if(wid.gv)grid_exit();wid.infield=1,wid.f=x,wid.fv=value,wid.ft=target}
 // render
 bi=rclip(frame.clip,bi); const oc=frame.clip;frame.clip=bi
 for(let z=0;z<layout.layout.length;z++){
  const g=layout.layout[z], pos=rcopy(g.pos);if(pos.w<1)continue // skip squashed spaces/newlines
  pos.y-=value.scroll;if(pos.y+pos.h<0||pos.y>bi.h)continue; pos.x+=bi.x, pos.y+=bi.y // coarse clip
  if(lis(g.arg)&&count(g.arg)){
   draw_hline(pos.x,pos.x+pos.w,pos.y+pos.h-1,alink==g.arg?fcol:19)
   const a=x.locked&&in_layer()&&over(pos)&&target;if(a&&!ev.drag)uicursor=cursor.point
   if(a&&ev.mu&&dover(pos))msg.target_link=target,msg.arg_link=g.arg
  }
  const csel=sel&&wid.cursor.x!=wid.cursor.y&&z>=min(wid.cursor.x,wid.cursor.y)&&z<max(wid.cursor.x,wid.cursor.y)
  if(csel)draw_rect(rclip(pos,frame.clip),fcol)
  if(image_is(g.arg)){image_paste(pos,frame.clip,g.arg,frame.image,x.show!='transparent');if(csel)draw_invert(pal,pos)}
  else{draw_char(pos,g.font,g.char,csel?bcol:fcol)}
 }
 if(sel&&wid.cursor_timer<FIELD_CURSOR_DUTY){
  const c=layout_cursor(layout,wid.cursor.y,fnt,x);c.y-=value.scroll;c.y+=bi.y,c.x+=bi.x
  draw_invert(pal,rclip(c,frame.clip))
 }
 if(target&&os!=value.scroll)iwrite(target,lms('scroll'),lmn(value.scroll)),mark_dirty()
 frame.clip=oc;if(!x.locked&&in_widgets())wid.count++
}
field_showcursor=_=>{
 const b=wid.f.size, bi=inset(b,2);if(wid.f.scrollbar)bi.w-=ARROWS[0].size.x+3
 const fnt=wid.f.font?wid.f.font: wid.f.style=='code'?FONT_MONO: FONT_BODY
 const layout=layout_richtext(deck,wid.fv.table,fnt,wid.f.align,bi.w), os=wid.fv.scroll
 const c=layout_cursor(layout,wid.cursor.y,fnt,wid.f), ch=min(bi.h,c.h);c.y-=wid.fv.scroll
 if(c.y<0){wid.fv.scroll+=c.y}if(c.y+ch>=bi.h){wid.fv.scroll+=((c.y+ch)-bi.h)}
 if(wid.ft&&os!=wid.fv.scroll)iwrite(wid.ft,lms('scroll'),lmn(wid.fv.scroll)),mark_dirty()
 return layout
}
field_apply=(v,c)=>{
 wid.fv.table=v,wid.cursor=rect(c.x,c.y);if(wid.cursor.x<0)wid.cursor.x=0;if(wid.cursor.y<0)wid.cursor.y=0
 field_showcursor(),wid.field_dirty=1,wid.change_timer=FIELD_CHANGE_DELAY
}
field_undo=_=>{const x=wid.hist[--(wid.hist_cursor)];field_apply(x[0],x[1])}
field_redo=_=>{const x=wid.hist[(wid.hist_cursor)++];field_apply(x[2],x[3])}
field_edit=(font,arg,text,pos)=>{
 const c=rect(), spliced=rtext_splice(wid.fv.table,font,arg,text,pos,c); wid.hist=wid.hist.slice(0,wid.hist_cursor)
 wid.hist.push([
  wid.fv.table,rect(wid.cursor.x,wid.cursor.y), // before
  spliced     ,c                              , // after
 ]),field_redo()
}
field_sel_lines=_=>{
 let a=min(wid.cursor.x,wid.cursor.y),b=max(wid.cursor.x,wid.cursor.y),l=field_showcursor()
 while(a                &&l.layout[a-1].char!='\n')a--
 while(b<l.layout.length&&l.layout[b  ].char!='\n')b++
 return {layout:l,sel:rect(a,b)}
}
field_comment=_=>{
 const s=field_sel_lines(), p=s.sel, layout=s.layout.layout; let ac=1,z=p.x;while(z<p.y){
  while(z<p.y&&layout[z].char==' ')z++
  if   (z<p.y&&layout[z].char!='#')ac=0
  while(z<p.y&&layout[z].char!='\n')z++;z++
 }
 let r='';z=p.x;while(z<p.y){
  while(z<p.y&&layout[z].char==' ')r+=' ',z++
  if(ac){if(layout[z].char=='#'){z++;if(z<p.y&&layout[z].char==' ')z++}}else{r+='# '}
  while(z<p.y&&layout[z].char!='\n')r+=layout[z++].char
  if(z<p.y&&layout[z].char=='\n')r+='\n',z++
 }field_edit(lms(''),lms(''),r,p),wid.cursor=rect(p.x,wid.cursor.y)
}
field_indent=add=>{
 const s=field_sel_lines(), p=s.sel, layout=s.layout.layout; let r='',z=p.x;while(z<p.y){
  if(add){r+=' '}else{if(layout[z].char==' ')z++}
  while(z<p.y&&layout[z].char==' ')r+=' ',z++
  while(z<p.y&&layout[z].char!='\n')r+=layout[z++].char
  if(z<p.y&&layout[z].char=='\n')r+='\n',z++
 }field_edit(lms(''),lms(''),r,p);wid.cursor=rect(p.x,wid.cursor.y)
}
field_stylespan=(font,arg)=>field_edit(font,arg,ls(rtext_string(wid.fv.table,wid.cursor)),wid.cursor)
field_input=text=>{
 if(text=='\n'){if(ms.type=='save')ev.action=1;if(ms.type=='save'||ev.shift)return}
 const rtext_font=(table,x)=>{const i=rtext_get(table,x);return i<0?lms(''):table.v.font[i]}
 field_edit(rtext_font(wid.fv.table,wid.cursor.y),lms(''),clchars(text),wid.cursor)
}
field_keys=(code,shift)=>{
 const b=wid.f.size, bi=inset(b,2);if(wid.f.scrollbar)bi.w-=ARROWS[0].size.x+3
 const fnt=wid.f.font?wid.f.font: wid.f.style=='code'?FONT_MONO: FONT_BODY, layout=layout_richtext(deck,wid.fv.table,fnt,wid.f.align,bi.w)
 let m=0, s=wid.cursor.x!=wid.cursor.y
 const l=wid.cursor.y>=layout.layout.length?layout.lines.length-1:layout.layout[wid.cursor.y].line, c=layout_cursor(layout,wid.cursor.y,fnt,wid.f)
 if(code=='ArrowLeft'   ){m=1;if(s&&!shift){wid.cursor.x=wid.cursor.y=min(wid.cursor.x,wid.cursor.y)}else{wid.cursor.y--}}
 if(code=='ArrowRight'  ){m=1;if(s&&!shift){wid.cursor.x=wid.cursor.y=max(wid.cursor.x,wid.cursor.y)}else{wid.cursor.y++}}
 if(code=='ArrowUp'     ){m=1;if(l>=0)wid.cursor.y=layout_index(layout,rect(c.x-1,layout.lines[l].pos.y                      -1   ))}
 if(code=='ArrowDown'   ){m=1;if(l>=0)wid.cursor.y=layout_index(layout,rect(c.x-1,layout.lines[l].pos.y+layout.lines[l].pos.h+1   ))}
 if(code=='PageUp'      ){m=1;if(l>=0)wid.cursor.y=layout_index(layout,rect(c.x-1,layout.lines[l].pos.y                      -bi.h))}
 if(code=='PageDown'    ){m=1;if(l>=0)wid.cursor.y=layout_index(layout,rect(c.x-1,layout.lines[l].pos.y+layout.lines[l].pos.h+bi.h))}
 if(code=='Home'        ){m=1,wid.cursor.y=0}
 if(code=='End'         ){m=1,wid.cursor.y=layout.layout.length;}
 if(code=='Backspace'   ){field_edit(lms(''),lms(''),'',s?wid.cursor:rect(wid.cursor.y-1,wid.cursor.y))}
 if(code=='Delete'      ){field_edit(lms(''),lms(''),'',s?wid.cursor:rect(wid.cursor.y,wid.cursor.y+1))}
 if(code=='Enter'       ){
  if(shift&&wid.ft){field_change(),msg.target_run=wid.ft,msg.arg_run=rtext_string(wid.fv.table,s?wid.cursor:rect(0,RTEXT_END))}
  else{
   let i=0;if(wid.f.style=='code'){
    const sl=field_sel_lines(),s=sl.sel,layout=sl.layout.layout
    while(s.x<layout.length&&layout[s.x].char==' ')i++,s.x++
   }field_input('\n'+' '.repeat(i))
  }
 }
 if(code=='Tab'&&wid.f.style=='code'){if(!shift&&!s){field_input(' ')}else{field_indent(!shift)}}
 const nl=layout_richtext(deck,wid.fv.table,fnt,wid.f.align,bi.w)
 wid.cursor.y=clamp(0,wid.cursor.y,nl.layout.length); if(!m)return
 wid.cursor_timer=0; if(!shift)wid.cursor.x=wid.cursor.y; field_showcursor()
}

ui_button  =(r,label,    enable,func )=>widget_button(null,{text:label,size:r,font:FONT_MENU,style:'round',show:             'solid',locked:!enable},0,func)
ui_toggle  =(r,label,inv,enable,func )=>widget_button(null,{text:label,size:r,font:FONT_MENU,style:'round',show:inv?'invert':'solid',locked:!enable},0,func)
ui_radio   =(r,label,    enable,value)=>widget_button(null,{text:label,size:r,font:FONT_BODY,style:'radio',show:             'solid',locked:!enable},value)
ui_checkbox=(r,label,    enable,value)=>widget_button(null,{text:label,size:r,font:FONT_BODY,style:'check',show:             'solid',locked:!enable},value)
ui_field   =(r,       value)=>widget_field(null,{size:r,font:FONT_BODY,show:'solid',scrollbar:0,border:1,style:'plain',align:ALIGN.left,locked:0},value)
ui_textedit=(r,border,value)=>widget_field(null,{size:r,font:FONT_BODY,show:'solid',scrollbar:1,border  ,style:'plain',align:ALIGN.left,locked:0},value)
ui_codeedit=(r,border,value)=>widget_field(null,{size:r,font:FONT_MONO,show:'transparent',scrollbar:1,border  ,style:'code' ,align:ALIGN.left,locked:running()},value)
ui_table   =(r,widths,format,value)=>widget_grid(null,{size:r,font:FONT_BODY,widths   ,format   ,headers:2,scrollbar:1,lines:0,show:'solid',locked:1},value)
ui_list    =(r,              value)=>widget_grid(null,{size:r,font:FONT_BODY,widths:[],format:'',headers:0,scrollbar:1,lines:0,show:'solid',locked:1},value)

// The Listener

listen_show=(align,bare,x)=>{
 frame=context;while(li.hist.length>=LISTEN_LINES)li.hist.shift()
 li.hist.push([draw_lil(rsub(LISTEN_SIZE(),rect(18,5)),align,bare,x),x])
 li.scroll=RTEXT_END
}
n_show=(a)=>{if(a.length<2){listen_show(ALIGN.right,0,a[0])}else{listen_show(ALIGN.right,1,lms(a.map(show).join(' ')))};return a[0]}
n_print=(a)=>{if(a.length<2){listen_show(ALIGN.right,1,lms(ls(a[0])))}else{listen_show(ALIGN.right,1,a[0]=dyad.format(a[0],lml(a.slice(1))))}return a[0]}
n_pre_listen=([a])=>{
 const ev=getev();Object.keys(li.vars).map(name=>{if(!ev.v[name])ev.v[name]=li.vars[name]})
 if(ob.sel.length&&uimode=='object')ev.v.selected=lml(ob.sel.slice(0))
 return a
}
n_post_listen=([a])=>{
 const ev=getev();Object.keys(ev.v).map(name=>{li.vars[name]=ev.v[name]})
 li.vars['_']=a,listen_show(ALIGN.right,0,a);return a
}
n_post_query=([a])=>{ms.grid=gridtab(lt(a));return a}

// Audio

let audio=null, samples_playing=0, audio_loop=null, audio_loop_playing=null
const audioContext=window.AudioContext||window.webkitAudioContext, offline=window.OfflineAudioContext||window.webkitOfflineAudioContext
initaudio=_=>{if(!audio)audio=new audioContext({sampleRate:44100})}
load_sound=(file,after)=>{
 decode_sound=data=>{
  const r=[];for(let z=0;z<data.length&&r.length<10*SFX_RATE;z+=8)r.push(sample_to_byte(data[z]));
  if(after){after(sound_make(Uint8Array.from(r)));return;}
  au.target=deck_add(deck,sound_read(ln(0))),mark_dirty()
  sound_edit(sound_make(Uint8Array.from(r))),au.sel=rect(),au.head=0,modal_enter('recording')
 }
 import_sound=buffer=>{
  initaudio(),audio.decodeAudioData(buffer,payload=>{
   const conv=new offline(1,payload.length*64000/payload.sampleRate,64000),buff=conv.createBufferSource()
   conv.oncomplete=e=>{decode_sound(e.renderedBuffer.getChannelData(0))}
   buff.buffer=payload,buff.connect(conv.destination),buff.start(0),conv.startRendering()
  })
 }
 const r=new FileReader();r.onload=_=>import_sound(r.result),r.readAsArrayBuffer(file)
}
sfx_stoploop=_=>{audio_loop_playing.onended=null,audio_loop_playing.stop(),audio_loop=audio_loop_playing=null}
sfx_doloop=clear=>{
 const a=audio_loop||NONE,b=lmblk(),pp=pending_popstate;let r=NONE, quota=LOOP_QUOTA
 blk_get(b,lms('loop')),blk_lit(b,lml([a])),blk_op(b,op.CALL)
 fire_hunk_async(ifield(deck,'card'),b)
 while(quota>0&&running()){runop(),quota--}
 if(!running())r=arg();popstate(),pending_popstate=pp
 if(clear&&audio_loop)sfx_stoploop()
 n_play([r,lms('loop')]),msg.pending_loop=0
}
n_play=([x,hint])=>{
 const prepare=sfx=>{
  const playback=audio.createBuffer(1,sfx.data.length*8,64000),dest=playback.getChannelData(0)
  for(let z=0;z<sfx.data.length;z++)for(let b=0;b<8;b++)dest[z*8+b]=MASTER_VOLUME*byte_to_sample(sfx.data[z])
  const playing=audio.createBufferSource();playing.buffer=playback,playing.connect(audio.destination);return playing
 }
 if(hint&&ls(hint)=='loop'){
  if(lis(x))x=dget(ifield(deck,"sounds"),x)
  if(x&&audio_loop==x){} // don't re-trigger
  else if(sound_is(x)&&ln(ifield(x,'size'))>0){
   if(audio_loop)sfx_stoploop()
   audio_loop=x,audio_loop_playing=prepare(x)
   audio_loop_playing.onended=_=>{sfx_doloop(1)}
   audio_loop_playing.start()
  }
  else if(audio_loop){sfx_stoploop()}
  return NONE
 }
 const sfx=!x?x: sound_is(x)?x: dget(deck.sounds,lms(ls(x)));if(!sfx||ln(ifield(sfx,'size'))<1)return NONE;initaudio()
 const playing=prepare(sfx);playing.addEventListener('ended',_=>{samples_playing--,audio_playing=samples_playing>0})
 playing.start(),samples_playing++,audio_playing=1;return NONE
}
stop_sound_pump=_=>{
 if(au.clip)au.clip.stop();au.clip=null,clearInterval(au.tick),au.mode='stopped'
 au.head=au.sel.x==au.sel.y?0:au.sel.x
}
play_sound_pump=sfx=>{
 initaudio();if(sfx.data.length<1)return
 const playback=audio.createBuffer(1,sfx.data.length*8,64000),dest=playback.getChannelData(0)
 for(let z=0;z<sfx.data.length;z++)for(let b=0;b<8;b++)dest[z*8+b]=MASTER_VOLUME*byte_to_sample(sfx.data[z])
 const playing=audio.createBufferSource();playing.buffer=playback,playing.connect(audio.destination)
 playing.addEventListener('ended',_=>stop_sound_pump),au.clip=playing;let counter=0
 au.tick=setInterval(_=>{const d=0|(8000*0.05);au.head+=d,counter+=d;if(au.mode!='playing'||counter>=sfx.data.length)stop_sound_pump()},50)
 au.mode='playing',playing.start()
}
sfx_any=_=>samples_playing>0

sound_slice=range=>{const r=[];for(let z=range.x;z<range.y;z++)r.push(au.target.data[z]);return sound_make(Uint8Array.from(r))}
sound_selected=_=>sound_slice(au.sel.x==au.sel.y?rect(0,au.target.data.length):au.sel)
sound_apply=v=>{au.target.data=Uint8Array.from(v.data),mark_dirty()}
sound_undo=_=>{const x=au.hist[--(au.hist_cursor)];sound_apply(x[0])}
sound_redo=_=>{const x=au.hist[(au.hist_cursor)++];sound_apply(x[1])}
sound_edit=v=>{au.hist=au.hist.slice(0,au.hist_cursor),au.hist.push([sound_slice(rect(0,au.target.data.length)),v]),sound_redo()}
sound_delete=_=>{
 const len=au.target.data.length, sel=au.sel.x==au.sel.y?rect(0,len):rcopy(au.sel), r=[]
 for(let z=0;z<    sel.x;z++)r.push(au.target.data[z      ])
 for(let z=0;z<len-sel.y;z++)r.push(au.target.data[z+sel.y])
 sound_edit(sound_make(Uint8Array.from(r))),au.head=au.sel.y=au.sel.x
}
sound_replace=s=>{
 r=[]
 for(let z=0       ;z<au.sel.x                                     ;z++)r.push(au.target.data[z])
 for(let z=0       ;z<s        .data.length&&r.length<(10*SFX_RATE);z++)r.push(s        .data[z]);const a=r.length
 for(let z=au.sel.y;z<au.target.data.length&&r.length<(10*SFX_RATE);z++)r.push(au.target.data[z])
 au.sel.y=a,au.head=r.length;return sound_make(Uint8Array.from(r))
}
sound_finish=_=>{
 au.head=(au.sel.x!=au.sel.y)?au.sel.x:0
 sound_undo(),sound_redo(),au.mode='stopped'
}
sound_begin_record=_=>{
 stop_sound_pump(),sound_edit(sound_slice(rect(0,au.target.data.length)))
 if(au.sel.x!=au.sel.y)au.head=au.sel.x;au.mode='recording'
}
sound_record=_=>{
 initaudio();if(au.record_stream){sound_begin_record();return}
 function resample(input,after){
  // Safari refuses to downsample to or play low sample rates,
  // so convert to 64khz as an even multiple of 8khz
  // and discretize from that. messy, but better than nothing:
  const conv=new offline(1,input.length*64000/input.sampleRate,64000), buff=conv.createBufferSource()
  conv.oncomplete=event=>{after(event.renderedBuffer.getChannelData(0))}
  buff.buffer=input,buff.connect(conv.destination),buff.start(0),conv.startRendering()
 }
 navigator.mediaDevices.getUserMedia({audio:true}).then(stream=>{
  try{
   const source=audio.createMediaStreamSource(stream)
   const scriptNode=audio.createScriptProcessor(4096,1,1)
   scriptNode.onaudioprocess=event=>{
    if(au.mode!='recording')return
    resample(event.inputBuffer,data=>{
     const r=[];for(let z=0;z<data.length;z+=8)r.push(sample_to_byte(data[z]))
     let h=au.head, end=(au.sel.x!=au.sel.y)?au.sel.y:(10*SFX_RATE), edit=au.hist[au.hist_cursor-1][1]
     const appended=min(max(au.target.data.length,h+r.length),end)
     if(appended>au.target.data.length){
      const a=new Uint8Array(appended), b=new Uint8Array(appended)
      for(let z=0;z<appended;z++)a[z]=b[z]=au.target.data[z]
      au.target.data=a,edit.data=b
     }
     for(let z=0;z<r.length&&h<end;z++){au.target.data[h]=edit.data[h]=r[z],h++}
     au.head=h;if(h>=end){sound_finish()}
    })
   }
   source.connect(scriptNode),scriptNode.connect(audio.destination)
   au.record_stream=stream,sound_begin_record()
  }catch(err){console.log(err),au.norecord=1}
 }).catch(err=>{console.log(err),au.norecord=1})
}

// Modal Helpers

table_decode=(text,format)=>ms.edit_json?monad.table(dyad.parse(lms('%j'),text)): n_readcsv(count(format)?[text,format]:[text])
transit_enumerate=_=>monad.table(monad.range(deck.transit))
sounds_enumerate=_=>{
 const r={icon:[],name:[],bytes:[],secs:[]};deck.sounds.v.map((sn,i)=>{
  r.icon .push(lmn(ICON.sound)),r.name.push(deck.sounds.k[i])
  r.bytes.push(dyad.format(lms('%.2fkb'),lmn(ln(ifield(sn,'size'))/1000.0*1.33)))
  r.secs .push(dyad.format(lms('%.2fs' ),ifield(sn,'duration')))
 });return lmt(r)
}
res_enumerate=(source)=>{
 const r={icon:[],name:[],value:[]}
 const pat=source.patterns,pp=patterns_write(pat),pa=anims_write(pat),da=dyad.parse(lms('%j'),lms(DEFAULT_ANIMS))
 if(!match(pa,da)||pp!=DEFAULT_PATTERNS)r.icon.push(lmn(ICON.pat)),r.name.push(lms('patterns')),r.value.push(pat)
 const fonts=dyad.drop(lms('mono'),dyad.drop(lms('menu'),dyad.drop(lms('body'),source.fonts)))
 fonts.v.map((font,i)=>{r.icon.push(lmn(ICON.font)),r.name.push(fonts.k[i]),r.value.push(font)})
 const sounds=source.sounds
 sounds.v.map((sound,i)=>{r.icon.push(lmn(ICON.sound)),r.name.push(sounds.k[i]),r.value.push(sound)})
 const modules=source.modules
 modules.v.map((mod,i)=>{r.icon.push(lmn(ICON.lil)),r.name.push(modules.k[i]),r.value.push(mod)})
 return lmt(r)
}

// Modal Dialogues

modal_enter=type=>{
 ev.md=ev.mu=ev.dclick=0,menus_clear()
 if(ms.type=='trans')return
 ms.from_listener=ms.type=='listen'
 ms.type=ms.subtype=type
 ms.old_wid=wid,wid=wid_state()
 if(type=='listen'){
  if(uimode=='script'){
   try{const text=ls(rtext_string(sc.f.table));parse(text),script_save(text)}
   catch(e){listen_show(align_right,1,lms('note: this script contains an error.\nexecuting under last saved version!'))}
  }
  ms.text=fieldstr(lms('')),li.scroll=RTEXT_END
 }
 if(type=='query')ms.grid=gridtab(ms.old_wid.gv.table),ms.text=fieldstr(lms('select from me.value'))
 if(type=='recording'){
  au.head=0,au.sel=rect(),au.mode='stopped',au.hist=[],au.hist_cursor=0
  ms.name=fieldstr(dkey(deck.sounds,au.target)||lms('unknown sound'))
 }
 if(type=='cards')ms.grid=gridtab(null)
 if(type=='sounds')ms.grid=gridtab(sounds_enumerate())
 if(type=='fonts'){
  const r=lmt({icon:[],name:[]});let fi=-1
  deck.fonts.v.map((font,z)=>{
   if(uimode=='object'){if(ob.sel.every(x=>ifield(x,'font')==font))fi=z}
   else if(ms.old_wid.ft){if(ifield(ms.old_wid.ft,'font')==font)fi=z}
   r.v.icon.push(lmn(ICON.font)),r.v.name.push(deck.fonts.k[z])
  }),ms.grid=gridtab(r,fi)
 }
 if(type=='resources')ms.message=null,ms.grid=gridtab(lmt({})),ms.grid2=gridtab(res_enumerate(deck))
 if(type=='link'){
  const t=ms.old_wid.fv.table,ol=t.v.arg[rtext_get(t,ms.old_wid.cursor.y)]
  ms.text=fieldstr(ol);if(count(ol))ms.old_wid.cursor=rtext_getr(t,ms.old_wid.cursor.y)
 }
 if(type=='grid'        )ms.name=fieldstr(lmn(dr.grid_size.x     )),ms.text=fieldstr(lmn(dr.grid_size.y       ))
 if(type=='deck_props'  )ms.name=fieldstr(ifield(deck     ,'name')),ms.text=fieldstr(ifield(deck     ,'author'))
 if(type=='button_props')ms.name=fieldstr(ifield(ob.sel[0],'name')),ms.text=fieldstr(ifield(ob.sel[0],'text'  ))
 if(type=='field_props' )ms.name=fieldstr(ifield(ob.sel[0],'name')),ms.text=fieldstr(ifield(ob.sel[0],'value' ))
 if(type=='grid_props'  )ms.name=fieldstr(ifield(ob.sel[0],'name')),ms.text=fieldstr(ifield(ob.sel[0],'format'))
 if(type=='grid_props'  )ms.form0=fieldstr(lms(fjson(monad.cols(ifield(ob.sel[0],'value'))))),ms.edit_json=1
 if(type=='canvas_props')ms.name=fieldstr(ifield(ob.sel[0],'name')),ms.text=fieldstr(ifield(ob.sel[0],'scale' ))
 if(type=='slider_props'){
  ms.name =fieldstr(   ifield(ob.sel[0],'name'    )    )
  ms.text =fieldstr(   ifield(ob.sel[0],'format'  )    )
  ms.form0=fieldstr(ll(ifield(ob.sel[0],'interval'))[0])
  ms.form1=fieldstr(ll(ifield(ob.sel[0],'interval'))[1])
  ms.form2=fieldstr(   ifield(ob.sel[0],'step'    )    )
 }
 if(type=='action'){
  sc.target=ob.sel[0],sc.others=[]
  ms.act_go=1,ms.act_gomode=4,ms.act_trans=0,ms.act_sound=0,ms.act_card=ln(ifield(ifield(deck,'card'),'index'))
  ms.verb   =lms('') // card name
  ms.message=lms('') // sound name
  ms.grid=gridtab(transit_enumerate())
 }
 const dname=(x,e)=>{x=x||'untitled';return lms(/\.(deck|html)$/.test(x)?x:x+e)}
 if(type=='card_props')ms.name=fieldstr(ifield(ifield(deck,'card'),'name'))
 if(type=='link'||type=='gridcell'||type=='query')wid.cursor=rect(0,RTEXT_END)
 if(type=='alert_lil'     )ms.type=type='alert'
 if(type=='confirm_lil'   )ms.type=type='confirm'
 if(type=='input_lil'     )ms.type=type='input'
 if(type=='confirm_new'   )ms.type=type='confirm'
 if(type=='confirm_script')ms.type=type='confirm'
 if(type=='multiscript'   )ms.type=type='confirm'
 if(type=='save_deck'     )ms.type=type='save',ms.text=fieldstr(dname(deck.name,'.deck')),ms.desc='Save a .deck or .html file.'
 if(type=='save_locked'   )ms.type=type='save',ms.text=fieldstr(dname(deck.name,'.html')),ms.desc='Save locked standalone deck as an .html file.'
 if(type=='export_table'  )ms.type=type='save',ms.text=fieldstr(lms(ls(ifield(ms.old_wid.gt,'name'))+'.csv')),ms.desc='Save table as a .csv file.'
 if(type=='export_script' )ms.type=type='save',ms.text=fieldstr(lms('script.lil'  )),ms.desc='Save script as a .lil file.'
 if(type=='export_image'  )ms.type=type='save',ms.text=fieldstr(lms('image.gif'   )),ms.desc='Save a .gif image file.'
 if(type=='save_lil'      )ms.type=type='save',ms.text=fieldstr(lms('untitled.txt')),ms.desc='Save a text file.'
 if(type=='input'         )ms.text=fieldstr(lms(''))
}
modal_exit=value=>{
 wid=ms.old_wid
 if(ms.type=='gridcell'&&value)grid_edit_cell(ms.cell,dyad.parse(lms(`%${ls(grid_format())[ms.cell.x]||'s'}`),rtext_string(ms.text.table)))
 if(ms.type=='card_props'){iwrite(ifield(deck,'card'),lms('name'),rtext_string(ms.name.table)),mark_dirty()}
 if(ms.type=='grid_props'){
  const t=table_decode(rtext_string(ms.form0.table),rtext_string(ms.text.table))
  if(!match(t,ifield(ob.sel[0],'value')))ob_edit_prop('value',t)
 }
 if(ms.type=='action'&&value){
  let r='on click do\n'
  if(ms.act_sound)r+=`  play[${show(ms.message)}]\n`
  if(ms.act_go){
   r+='  go['
   r+=ms.act_gomode==0?'"First"': ms.act_gomode==1?'"Prev"': ms.act_gomode==2?'"Next"': ms.act_gomode==3?'"Last"': show(ms.verb)
   if(ms.act_trans)r+=` ${show(ms.grid.table.v.value[ms.grid.row])}`
   r+=']\n'
  }r+='end',script_save(lms(r))
 }
 if(ms.type=='recording'){
  const name=rtext_string(ms.name.table);rename_sound(deck,au.target,name)
  au.mode='stopped',modal_enter('sounds'),ms.grid.row=dkix(deck.sounds,name);return
 }
 if(ms.subtype=='confirm_new'   &&value)load_deck(deck_read(''))
 if(ms.subtype=='confirm_script'&&value)finish_script()
 if(ms.subtype=='multiscript'   &&value)setscript(ob.sel)
 if(ms.subtype=='alert_lil'  )arg(),ret(lmn(value))
 if(ms.subtype=='confirm_lil')arg(),ret(lmn(value))
 if(ms.subtype=='input_lil'  )arg(),ret(rtext_string(ms.text.table))
 if(ms.subtype=='choose_lil' )arg(),ret(ms.verb.v[ms.grid.row])
 ms.type=null
 if(ms.from_listener)modal_enter('listen')
 if(ms.type==null&&uimode=='interact')msg.next_view=1
}
modals=_=>{
 ms.in_modal=1
 const pal=deck.patterns.pal.pix
 if(ms.type=='about'){
  const b=draw_modalbox(rect(190,70))
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit)modal_exit(0)
  draw_text(b,`Decker v${VERSION}`,FONT_MENU,1),b.y+=15
  draw_text(b,'by John Earnest',FONT_BODY,1),b.y+=12
 }
 else if(ms.type=='listen'){
  const size=LISTEN_SIZE(), th=li.hist.reduce((x,y)=>x+y[0].size.y+5,0), h=min(th,size.y)
  const esize=rect(0|((frame.size.x-size.x)/2),frame.size.y-49,size.x,50), tsize=rect(esize.x,esize.y-(h?h+5:0),esize.w,h)
  const bsize=rect(esize.x-5,esize.y-5-(h?tsize.h+5:0),esize.w+10,(h?tsize.h+5:0)+esize.h+20)
  draw_shadow(bsize,1,32,1),ui_codeedit(esize,1,ms.text)
  if(h){
   const sbar=scrollbar(tsize,max(0,th-size.y),10,tsize.h,li.scroll,h>=size.y,0), b=sbar.size; li.scroll=sbar.scroll
   let cy=0;li.hist.map(x=>{
    const l=x[0], t=x[1], s=l.size; let lb=rect(b.x,b.y+cy-li.scroll,s.x,s.y)
    image_paste(lb,b,l,frame.image,0),cy+=s.y+5
    lb=rclip(b,lb);const v=lis(t)&&over(lb), a=v&&dover(lb)
    if(v)uicursor=cursor.point,draw_box(inset(lb,-1),0,13); if(a&&(ev.md||ev.drag))draw_invert(pal,lb)
    if(a&&ev.mu)ms.text=fieldstr(t)
   })
  }
  if(ev.eval){
   const str=rtext_string(ms.text.table);if(count(str)<1)return
   try{
    const prog=parse(ls(str)), b=lmblk(); ms.text=fieldstr(lms('')),listen_show(ALIGN.left,1,str)
    const target=uimode=='script'?sc.target: ob.sel.length==1?ob.sel[0]: ifield(deck,'card')
    blk_opa(b,op.BUND,1),blk_lit(b,lmnat(n_pre_listen )),blk_lit(b,NONE   ),blk_op(b,op.CALL),blk_op(b,op.DROP),blk_cat(b,prog)
    blk_opa(b,op.BUND,1),blk_lit(b,lmnat(n_post_listen)),blk_op (b,op.SWAP),blk_op(b,op.CALL),blk_op(b,op.DROP),fire_hunk_async(target,b)
   }catch(e){listen_show(ALIGN.right,1,lms(`error: ${e.x}`));return}
  }if(ev.exit)modal_exit(0)
 }
 else if(ms.type=='cards'){
  const b=draw_modalbox(rect(210,frame.size.y-46)), gsize=rect(b.x,b.y+15,b.w,b.h-20-20)
  const slot=30,ch=slot*count(deck.cards); let m=0,props=0,gutter=-1,curr=ifield(deck,'card')
  draw_textc(rect(b.x,b.y-5,b.w,20),'Cards',FONT_MENU,1),draw_box(gsize,0,1)
  const sbar=scrollbar(gsize,max(0,ch-(gsize.h-2)),10,gsize.h,ms.grid.scroll,ch>=gsize.h,0),bb=sbar.size;ms.grid.scroll=sbar.scroll,bb.y++
  const oc=frame.clip;frame.clip=bb;for(let z=0;z<count(deck.cards);z++){
   const c=rect(bb.x,bb.y+(z*slot)-ms.grid.scroll,bb.w,slot), card=deck.cards.v[z]
   if(c.y>bb.y+bb.h||c.y+c.h<bb.y)continue; const cb=rclip(c,bb) // coarse clip
   const p=rect(c.x+2,c.y+1,40,28), t=rect(p.x+p.w+5,p.y,bb.w-(2+p.w+5+5),font_h(FONT_MENU)), s=rect(t.x,t.y+t.h+2,t.w,font_h(FONT_BODY))
   const desc=has_parent(card)?`child of '${ls(ifield(ifield(card,'parent'),'name'))}'`:`${count(card.widgets)} widgets`
   if(ev.md&&dover(cb)){m=1,n_go([card],deck),curr=card,ms.grid.row=z}if(ev.dclick&&over(cb))props=1
   const col=ev.drag&&ms.grid.row==z?13:1
   draw_text_fit(t,ls(ifield(card,'name')),FONT_MENU,col),draw_text_fit(s,desc,FONT_BODY,col),draw_box(p,0,col)
   if(card==curr&&col==1)draw_invert(pal,c);draw_thumbnail(card,p)
   if((ev.drag||ev.mu)&&ms.grid.row!=-1){
    {const g=rect(c.x,c.y-3    ,c.w,7);if(over(g)){draw_hline(c.x,c.x+c.w,c.y      ,13),gutter=z  }}
    {const g=rect(c.x,c.y-3+c.h,c.w,7);if(over(g)){draw_hline(c.x,c.x+c.w,c.y+c.h-1,13),gutter=z+1}}
   }
  };frame.clip=oc
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit||ev.action)modal_exit(0)
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'New',1)){
   const c=deck_add(deck,lms('card')),n=ln(ifield(curr,'index'))
   iwrite(c,lms('index'),lmn(n+1)),m=1,n_go([c],deck)
  }c.x+=65
  if(ui_button(rect(c.x,c.y,80,20),'New Child',1)){
   const c=deck_add(deck,lms('card'),ifield(curr,'name'),curr),n=ln(ifield(curr,"index"))
   iwrite(c,lms('index'),lmn(n+1)),m=1,n_go([c],deck)
  }
  if(ev.mu){
   if(ms.grid.row!=-1&&gutter!=-1){
    const s=deck.cards.v[ms.grid.row], oi=ln(ifield(s,'index'))
    iwrite(s,lms('index'),lmn(gutter>oi?gutter-1:gutter)),m=1,n_go([s],deck)
   }ms.grid.row=-1
  }
  else if(ev.drag&&ms.grid.row!=-1){const r=rect(ev.pos.x-5,ev.pos.y-5,10,10);draw_rect(r,0),draw_box(r,0,1),uicursor=cursor.drag}
  else if(ev.dir=='up'  &&ev.shift){iwrite(curr,lms('index'),lmn(ln(ifield(curr,'index'))-1)),m=1,n_go([curr],deck)}
  else if(ev.dir=='down'&&ev.shift){iwrite(curr,lms('index'),lmn(ln(ifield(curr,'index'))+1)),m=1,n_go([curr],deck)}
  else if(ev.dir=='left' ||ev.dir=='up'  ){m=1,n_go([lms('Prev')],deck)}
  else if(ev.dir=='right'||ev.dir=='down'){m=1,n_go([lms('Next')],deck)}
  if(m){
   curr=ifield(deck,'card');const y=(ln(ifield(curr,'index'))*slot)-ms.grid.scroll
   if(y<0){ms.grid.scroll+=y}if(y+slot>=bb.h){ms.grid.scroll+=(y+slot)-bb.h}
  }if(props)modal_enter('card_props')
 }
 else if(ms.type=='sounds'){
  const b=draw_modalbox(rect(250,frame.size.y-16-30)), gsize=rect(b.x,b.y+15,b.w,b.h-16-(2*25))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Sounds',FONT_MENU,1)
  const s=ms.grid.row>=0?dget(deck.sounds,ms.grid.table.v.name[ms.grid.row]):null
  if(ui_table(gsize,[16,130],'Isss',ms.grid))n_play([s])
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit){
   if(ms.from_action){
    if(ms.grid.row>=0)ms.message=deck.sounds.k[ms.grid.row]
    ms.grid=gridtab(transit_enumerate(),ms.act_transno),ms.type='action',ms.from_action=0
   }else{modal_exit(1)}
  }
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Edit...',s!=null))au.target=s,modal_enter('recording')
  if(ui_button(rect(c.x,c.y-25,60,20),'New...',1))au.target=deck_add(deck,lms('sound')),mark_dirty(),modal_enter('recording');c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),"Delete",s!=null))deck_remove(deck,s),mark_dirty(),ms.grid=gridtab(sounds_enumerate())
 }
 else if(ms.type=='recording'){
  const b=draw_modalbox(rect(frame.size.x-50,130)), samples=max(1,ln(ifield(au.target,'size')))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Audio Editor',FONT_MENU,1)
  au.head=clamp(0,au.head,samples), au.sel.x=clamp(0,au.sel.x,samples), au.sel.y=clamp(0,au.sel.y,samples)
  const gsize=rect(b.x,b.y+15,b.w,64), lsize=rint(rect(b.x,gsize.y+gsize.h+2,b.w/2,font_h(FONT_BODY)))
  const sndpos=x=>0|(x*((gsize.w*1.0)/samples))
  const possnd=x=>0|max(0,min(samples-1,((x/gsize.w)*samples)))
  if((ev.mu||ev.drag)&&dover(gsize)&&au.mode=='stopped'){
   const x=possnd(ev.dpos.x-gsize.x),y=possnd(ev.pos.x-gsize.x);field_exit()
   if(ev.mu)au.head=y;if(ev.drag)au.sel=rect(min(x,y),max(x,y)),au.head=au.sel.x
  }
  const sel=au.sel, sc=sel.y-sel.x, oc=frame.clip
  frame.clip=gsize;for(let z=0;z<gsize.w;z++){
   let v=0,base=possnd(z);for(let d=-1;d<=1;d++){
    if(base+d<0||base+d>=samples)continue;let s=byte_to_sample(au.target.data[base+d]);if(Math.abs(s)>Math.abs(v))v=s
   }let vp=0|(v*64);draw_vline(gsize.x+z,gsize.y+32,gsize.y+32+vp,1),draw_vline(gsize.x+z,gsize.y+32-vp,gsize.y+32,1)
  }frame.clip=oc
  if(sc)draw_invert(pal,rect(gsize.x+sndpos(sel.x),gsize.y,sndpos(sel.y)-sndpos(sel.x),gsize.h))
  draw_invert(pal,rect(gsize.x+sndpos(au.head)-1,gsize.y,3,gsize.h)),draw_box(gsize,0,1)
  const t=sc==0?ls(dyad.format(lms('%0.2fkb, %0.2fs'         ),lml([lmn(samples/1000.0*1.33),ifield(au.target,'duration')]))):
                ls(dyad.format(lms('%0.2fkb, %0.2fs selected'),lml([lmn(sc     /1000.0*1.33),lmn(sc/SFX_RATE            )])))
  draw_text_fit(lsize,t,FONT_BODY,1)
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit)modal_exit(0)
  const c=rect(b.x,b.y+b.h-20)
  if(ui_toggle(rect(c.x,c.y,60,20),'Play',au.mode=='playing',1)){
   if(au.mode=='recording')sound_finish()
   au.head=sc?au.sel.x:0;if(au.mode=='playing'){stop_sound_pump()}else{play_sound_pump(sound_selected())}
  };c.x+=65
  if(ui_toggle(rect(c.x,c.y,60,20),'Record',au.mode=='recording',!au.norecord)){
   if(au.mode=='recording'){sound_finish()}else{sound_record()}
  };c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),'Crop',sc&&au.mode=='stopped'))sound_edit(sound_slice(au.sel)),au.sel=rect(),au.head=0
  draw_text(rect(b.x+(b.w/2),gsize.y+gsize.h+9,37,20),'Name',FONT_MENU,1)
  ui_field(rint(rect(b.x+(b.w/2)+37,gsize.y+gsize.h+5,(b.w/2)-37,20)),ms.name)
 }
 else if(ms.type=='fonts'){
  const b=draw_modalbox(rect(170,170))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Fonts',FONT_MENU,1)
  const gsize=rect(b.x,b.y+15,b.w,b.h-20-50-25), choose=ui_table(gsize,[16],'Is',ms.grid)
  let psize=rect(b.x,gsize.y+gsize.h+5,b.w,50);draw_box(psize,0,1),psize=inset(psize,2)
  if(ms.grid.row>=0){
   const l=layout_plaintext(PANGRAM,deck.fonts.v[ms.grid.row],ALIGN.left,rect(psize.w,psize.h));draw_text_wrap(psize,l,1)}
  const c=rect(b.x+b.w-60,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'OK',ms.grid.row>=0)||choose){
   const nf=ms.grid.table.v.name[ms.grid.row];modal_exit(1)
   if(uimode=='object'){ob_edit_prop('font',nf)}
   else if(wid.ft&&wid.cursor.x!=wid.cursor.y){const c=wid.cursor;field_stylespan(nf,lms('')),wid.cursor=c,mark_dirty()}
   else if(wid.ft){iwrite(wid.ft,lms('font'),nf),wid.f=unpack_field(ms.old_wid.ft),wid.fv=unpack_field_value(ms.old_wid.ft),mark_dirty()}
  };c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='resources'){
  const b=draw_modalbox(rect(280,190))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Font/Deck Accessory Mover',FONT_MENU,1)
  const lgrid=rect(b.x            ,b.y+15 ,100    ,b.h-(15+15+5+20))
  const rgrid=rect(b.x+b.w-lgrid.w,lgrid.y,lgrid.w,lgrid.h         )
  if(ui_button(rect(rgrid.x+(rgrid.w-80)/2,b.y+b.h-20,80,20),'OK',1)||ev.exit)modal_exit(0)
  ui_table(lgrid,[16,lgrid.w-38],'Is',ms.grid );if(ms.grid .row>-1)ms.grid2.row=-1
  ui_table(rgrid,[16,rgrid.w-38],'Is',ms.grid2);if(ms.grid2.row>-1)ms.grid .row=-1
  draw_vline(lgrid.x+lgrid.w,lgrid.y+lgrid.h+5,b.y+b.h,18)
  draw_vline(rgrid.x        ,rgrid.y+rgrid.h+5,b.y+b.h,18)
  draw_textc(rect(lgrid.x,lgrid.y+lgrid.h+3,lgrid.w,15),ms.message?ls(ifield(ms.message,'name')):'(Choose a Deck)',FONT_BODY,1)
  draw_textc(rect(rgrid.x,rgrid.y+rgrid.h+3,rgrid.w,15),ls(ifield(deck,'name')),FONT_BODY,1)
  const cb=rect(lgrid.x+lgrid.w+5,lgrid.y+5,b.w-(lgrid.w+5+5+rgrid.w),20)
  const rvalue=(g,k)=>g.table.v[k][g.row]
  const sel=(ms.grid.table&&ms.grid.row>-1)?rvalue(ms.grid,'value'): ms.grid2.row>-1?rvalue(ms.grid2,'value'): null
  if(ui_button(cb,'>> Copy >>',ms.grid.row>-1)){
   if(patterns_is(sel)){const dst=ifield(deck,'patterns');for(let z=2;z<=31;z++)iindex(dst,z,iindex(sel,z))}
   else{deck_add(deck,sel,rvalue(ms.grid,'name'))}
   ms.grid2=gridtab(res_enumerate(deck)),mark_dirty();if(module_is(sel))validate_modules()
  }cb.y+=25
  if(ui_button(cb,'Remove',ms.grid2.row>-1)){
   if(patterns_is(sel)){deck.patterns=patterns_read(lmd())}
   else{deck_remove(deck,rvalue(ms.grid2,'value'))}
   ms.grid2=gridtab(res_enumerate(deck)),mark_dirty()
  }cb.y+=25
  const pre=rect(cb.x,cb.y,cb.w,b.h-(cb.y-b.y))
  if(sel&&font_is(sel)){const l=layout_plaintext(PANGRAM,sel,ALIGN.center,rect(pre.w,pre.h));draw_text_wrap(pre,l,1)}
  if(sel&&module_is(sel)){const l=layout_plaintext(ls(ifield(sel,'description')),FONT_BODY,ALIGN.center,rect(pre.w,pre.h));draw_text_wrap(pre,l,1)}
  if(sel&&sound_is(sel)){if(ui_button(cb,'Play',1))n_play([sel])}
  if(sel&&patterns_is(sel)){
   const c=frame.clip,pal=sel.pal.pix;frame.clip=pre;
   const anim_ants   =(x,y)=>(0|((x+y+(0|(frame_count/2)))/3))%2?15:0
   const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1
   const draw_color  =(pix,x,y)=>pix==ANTS?anim_ants(x,y): pix>47?0: pix>31?pix-32: draw_pattern(pix,x,y)?15:0
   for(let z=0;z<32;z++)for(let y=0;y<16;y++)for(let x=0;x<16;x++){
    const h=rect(3+x+pre.x+16*(z%(0|(pre.w/16))), y+pre.y+16*(0|(z/(0|(pre.w/16)))))
    if(inclip(h))pix(h,32+draw_color(z,x,y))
   }frame.clip=c
  }
  ui_button(rint(rect(lgrid.x+(lgrid.w-80)/2,b.y+b.h-20,80,20)),'Choose...',1,_=>open_text('.html,.deck',text=>{
   ms.message=deck_read(text),ms.grid=gridtab(res_enumerate(ms.message))
  }))
 }
 else if(ms.type=='query'){
  const b=draw_modalbox(rect(frame.size.x-30,frame.size.y-16-30)),t=ms.grid.table
  const desc=t?`${Object.keys(t.v).length} column${Object.keys(t.v).length==1?'':'s'}, ${count(t)} row${count(t)==1?'':'s'}.`:'Executing Query.'
  let compiles=0,error=' ';try{parse(ls(rtext_string(ms.text.table))),compiles=1}catch(e){error=e.x}
  const dsize=font_textsize(FONT_BODY,desc);draw_text(b,desc,FONT_BODY,1)
  const msize=font_textsize(FONT_BODY,error), gsize=rint(rect(b.x,b.y+dsize.y,b.w,(b.h-(2*5)-dsize.y-20)/2))
  const esize=rect(b.x,gsize.y+gsize.h+5,b.w,gsize.h-msize.y);ui_codeedit(esize,1,ms.text)
  if(!compiles)draw_text_fit(rect(b.x,esize.y+esize.h,b.w,msize.y),error,FONT_BODY,1)
  const c=rect(b.x+b.w-60,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Run',compiles)||ev.eval){
   try{
    const prog=parse(ls(rtext_string(ms.text.table)))
    blk_opa(prog,op.BUND,1),blk_lit(prog,lmnat(n_post_query)),blk_op(prog,op.SWAP),blk_op(prog,op.CALL)
    fire_hunk_async(ms.old_wid.gt,prog),ms.grid=gridtab(null)
   }catch(e){console.log(e)}
  }c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Apply',ms.grid.table!=null&&ms.grid.table!=ms.old_wid.gv.table&&!ms.old_wid.g.locked)){
   const t=ms.grid.table;modal_exit(0),grid_edit(t)
   listen_show(ALIGN.right,1,lms('applied table query:')),listen_show(ALIGN.left,1,rtext_string(ms.text.table))
  }c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Close',1)||ev.exit)modal_exit(0)
  if(ms.grid.table){widget_grid(null,{size:gsize,font:FONT_MONO,widths:[],format:'',headers:1,scrollbar:1,lines:1,show:'solid',locked:1},ms.grid)}
  else{draw_box(gsize,0,1),draw_textc(gsize,'Working...',FONT_BODY,1)}
 }
 else if(ms.type=='url'){
  const b=draw_modalbox(rect(170,90))
  draw_textc(rect(b.x,b.y,b.w,20),'Do you wish to open this URL?',FONT_BODY,1)
  ui_textedit(rect(b.x,b.y+20+5,b.w,40),1,ms.text)
  const c=rect(b.x+b.w-(b.w-(2*60+5))/2-60,b.y+b.h-20)
  ui_button(rect(c.x,c.y,60,20),'Open',1,_=>{open_url(ls(rtext_string(ms.text.table)));modal_exit(0)});c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='link'){
  const b=draw_modalbox(rect(170,70))
  draw_textc(rect(b.x,b.y,b.w,20),'Enter a link string for\nthe selected text span:',FONT_BODY,1)
  ui_field(rect(b.x,b.y+20+5,b.w,20),ms.text)
  const c=rint(rect(b.x+b.w-(b.w-(2*60+5))/2-60,b.y+b.h-20))
  if(ui_button(rect(c.x,c.y,60,20),'OK',1)){
   const l=rtext_string(ms.text.table);modal_exit(0)
   const c=rcopy(wid.cursor);field_stylespan(lms(''),l),wid.cursor=c
  };c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='gridcell'){
  const b=draw_modalbox(rect(170,70))
  draw_textc(rect(b.x,b.y,b.w,20),'Enter a new value for\nthe selected cell:',FONT_BODY,1)
  ui_field(rect(b.x,b.y+20+5,b.w,20),ms.text)
  const c=rect(b.x+b.w-(b.w-(2*60+5))/2-60,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'OK',1))modal_exit(1);c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='save'){
  const l=layout_plaintext(ms.desc,FONT_BODY,ALIGN.center,rect(250,100))
  const b=draw_modalbox(radd(l.size,rect(0,20+5+20+5+20))), tbox=rect(b.x,b.y+20,b.w,l.size.y)
  draw_textc(rect(b.x,b.y-5,b.w,20),'Save File',FONT_MENU,1),draw_text_wrap(tbox,l,1)
  draw_text(rect(b.x   ,tbox.y+tbox.h+7,    56,20),'Filename',FONT_MENU,1)
  ui_field (rect(b.x+56,tbox.y+tbox.h+5,b.w-56,18),ms.text)
  const c=rect(b.x+b.w-60,b.y+b.h-20),subtype=ms.subtype
  const grid=ms.old_wid.gv, format=subtype!='export_table'?null:lms(ms.old_wid.g.format.length?ms.old_wid.g.format:'s'.repeat(Object.keys(grid.table.v).length))
  if(ui_button(rect(c.x,c.y,60,20),'Save',1,_=>{
   const name=ls(rtext_string(ms.text.table))
   const savedeck=_=>{
    let d=deck_write(deck)
    if(/\.html$/i.test(name)){q('script[language="decker"]').innerHTML='\n'+d,d=q('body').innerHTML}
    dirty=0,save_text(name,`<body>${d}</body>`)
   }
   const save_image=_=>{
    if(bg_has_sel()){const s=rcopy(dr.sel_here);bg_end_selection(),dr.sel_here=s}
    let i=draw_card(ifield(deck,'card'),1),off=rect(),f=1,a=0, bg=dr.trans?0:32, frames=[]
    const anim=deck.patterns.anim
    const anim_pattern=(pix,x,y,f)=>pix<28||pix>31?pix: anim[pix-28][f%max(1,anim[pix-28].length)]
    const draw_pattern=(pix,x,y  )=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal[(x%8)+(8*(y%8))+(8*8*pix)]&1
    if(bg_has_sel()||bg_has_lasso())i=image_copy(i,dr.sel_here),off=rcopy(dr.sel_here)
    for(let z=0;z<4&&dr.show_anim;z++){const c=anim[z].length;if(c)f=lcm(f,c)}
    for(let z=0;z<f;z++){
     const frame=image_copy(i)
     for(let y=0;y<i.size.y;y++)for(let x=0;x<i.size.x;x++){
      const v=frame.pix[x+(i.size.x*y)];if(v>=28&&v<=31)a=1
      const c=anim_pattern(v,x,y,z),p=draw_pattern(c,x+off.x,y+off.y)
      frame.pix[x+(i.size.x*y)]=c>=32?c: p?1:bg
     }frames.push(bg_has_lasso()?image_mask(frame,dr.mask):frame)
    }save_bin(name,writegif(a?frames:[frames[0]]))
   }
   if(subtype=='save_deck'    )savedeck()
   if(subtype=='save_locked'  )iwrite(deck,lms('locked'),ONE),savedeck(),iwrite(deck,lms('locked'),NONE)
   if(subtype=='export_script')save_text(name,ls(rtext_string(sc.f.table)))
   if(subtype=='export_table' )field_exit(),save_text(name,ls(n_writecsv([grid.table,format])))
   if(subtype=='export_image' )save_image()
   if(subtype=='save_lil'){
    let x=ms.verb;ret(ONE);if(image_is(x))x=lml([x])
    if(lil(x)&&ll(x).some(image_is)&&count(x)>0){save_bin(name,writegif(ll(x).filter(image_is)))}
    else if(sound_is(x))                        {save_bin(name,writewav(x))}
    else if(array_is(x))                        {save_bin(name,writearray(x))}
    else                                        {save_text(name,ls(x))}
   }
  }))modal_exit(1);c.x-=65;
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='alert'){
  const b=draw_modal_rtext(rect(0,5+20))
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='confirm'){
  const b=draw_modal_rtext(rect(0,5+20)),v=ms.verb?ls(ms.verb):'OK'
  const vs=font_textsize(FONT_MENU,v);vs.x=min(max(60,vs.x+10),200-65);const c=rect(b.x+b.w-vs.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,vs.x,20),v,1))modal_exit(1);c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='input'){
  const b=draw_modal_rtext(rect(0,5+20+5+20))
  ui_field(rect(b.x,b.y+b.h-(20+5+20),b.w,20),ms.text)
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='choose_lil'){
  const b=draw_modal_rtext(rect(0,5+60+5+20))
  const choose=ui_table(rect(b.x,b.y+b.h-(20+5+60),b.w,60),[],'s',ms.grid)
  if(ui_button(rect(b.x+b.w-60,b.y+b.h-20,60,20),'OK',ms.grid.row>=0)||choose)modal_exit(1)
 }
 else if(ms.type=='open_lil'){
  const b=draw_modalbox(rect(125,45))
  draw_textc(rect(b.x,b.y,b.w,16),'Click to open a file:',FONT_BODY,1)
  const c=rect(b.x+b.w-60,b.y+b.h-20)
  ui_button(rect(c.x,c.y,60,20),'Open',1,_=>{
   if     (ls(ms.verb)=='array'  )open_file(ms.filter,file=>{load_array(file,        array=>{arg(),ret(array)    ,modal_exit(1)})})
   else if(ms.filter=='image/*'  )open_file(ms.filter,file=>{load_image(file,ms.verb,image=>{arg(),ret(image)    ,modal_exit(1)})})
   else if(ms.filter=='audio/*'  )open_file(ms.filter,file=>{load_sound(file,        sound=>{arg(),ret(sound)    ,modal_exit(1)})})
   else if(ms.filter=='.csv,.txt')open_text(ms.filter,                               text =>{arg(),ret(lms(text)),modal_exit(1)  })
   else                           open_text(''       ,                               text =>{arg(),ret(lms(text)),modal_exit(1)  })
  }),c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
 }
 else if(ms.type=='brush'){
  const grid=rect(6,4), ss=25, gs=ss+4, m=5, lh=font_h(FONT_BODY)
  const b=draw_modalbox(rect(m+(grid.x*gs)+m,m+(grid.y*gs)+lh+m))
  draw_textc(rect(b.x,b.y+b.h-lh,b.w,lh),'Choose a brush shape.',FONT_BODY,1)
  for(let z=0;z<grid.x*grid.y;z++){
   const s=rect(b.x+m+2+gs*(z%grid.x),b.y+m+2+gs*(0|(z/grid.x)),ss,ss);
   const c=rint(rect(s.x+s.w/2,s.y+s.h/2));draw_line(rpair(c,c),z,1)
   if(z==dr.brush)draw_box(inset(s,-2),0,1)
   const a=dover(s)&&over(s), cs=(z==dr.brush&&ev.action), cl=cs||((ev.md||ev.drag)&&a), cr=cs||(ev.mu&&a)
   if(cl)draw_invert(pal,inset(s,-1)); if(cr){dr.brush=z,modal_exit(z);break}
  }
  if(ev.exit||(ev.mu&&!dover(b)&&!over(b)))modal_exit(-1),ev.mu=0
  if(ev.dir=='left' )dr.brush=((0|(dr.brush/grid.x))*grid.x)+((dr.brush+grid.x-1)%grid.x)
  if(ev.dir=='right')dr.brush=((0|(dr.brush/grid.x))*grid.x)+((dr.brush+       1)%grid.x)
  if(ev.dir=='up'   )dr.brush=(dr.brush+(grid.x*(grid.y-1)))%(grid.x*grid.y)
  if(ev.dir=='down' )dr.brush=(dr.brush+grid.x             )%(grid.x*grid.y)
 }
 else if(ms.type=='pattern'||ms.type=='fill'){
  const grid=rect(8,4), ss=25, gs=ss+4, m=5, lh=font_h(FONT_BODY), v=ms.type=='pattern'?dr.pattern:dr.fill
  const setv=x=>ms.type=='pattern'?dr.pattern=x:dr.fill=x
  const b=draw_modalbox(rect(m+(grid.x*gs)+m,m+(grid.y*gs)+lh+m))
  draw_textc(rect(b.x,b.y+b.h-lh,b.w,lh),`Choose a ${ms.type=='fill'?'fill':'stroke'} pattern.`,FONT_BODY,1)
  for(let z=0;z<grid.x*grid.y;z++){
   const s=rint(rect(b.x+m+2+gs*(z%grid.x),b.y+m+2+gs*(0|(z/grid.x)),ss,ss))
   draw_rect(s,z); if(z==v)draw_box(inset(s,-2),0,1)
   const a=dover(s)&&over(s), cs=(z==v&&ev.action), cl=cs||((ev.md||ev.drag)&&a), cr=cs||(ev.mu&&a)
   if(cl)draw_invert(pal,inset(s,-1)); if(cr){setv(z),modal_exit(z);break}
  }
  if(ev.exit||(ev.mu&&!dover(b)&&!over(b)))modal_exit(-1),ev.mu=0
  if(ev.dir=='left' )setv(((0|(v/grid.x))*grid.x)+((v+grid.x-1)%grid.x))
  if(ev.dir=='right')setv(((0|(v/grid.x))*grid.x)+((v+       1)%grid.x))
  if(ev.dir=='up'   )setv((v+(grid.x*(grid.y-1)))%(grid.x*grid.y))
  if(ev.dir=='down' )setv((v+grid.x             )%(grid.x*grid.y))
 }
 else if(ms.type=='grid'){
  const b=draw_modalbox(rect(120,100))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Grid Size',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Width' ,FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,20),'Height',FONT_MENU,1)
  ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  ui_field(rect(b.x+42,b.y+40,b.w-42,18),ms.text)
  dr.grid_size.x=ln(rtext_string(ms.name.table)),dr.grid_size.x=0|max(1,dr.grid_size.x)
  dr.grid_size.y=ln(rtext_string(ms.text.table)),dr.grid_size.y=0|max(1,dr.grid_size.y)
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='deck_props'){
  const b=draw_modalbox(rect(220,100))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Deck Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name',FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,20),'Author',FONT_MENU,1)
  ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  ui_field(rect(b.x+42,b.y+40,b.w-42,18),ms.text)
  iwrite(deck,lms('name'  ),rtext_string(ms.name.table))
  iwrite(deck,lms('author'),rtext_string(ms.text.table)),mark_dirty()
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(deck),modal_exit(0);c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),'Protect...',1))modal_enter('save_locked')
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='card_props'){
  const b=draw_modalbox(rect(220,100)),card=ifield(deck,'card')
  draw_textc(rect(b.x,b.y-5,b.w,20),'Card Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name',FONT_MENU,1),ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  const parent=ifield(card,'parent');if(card_is(parent)){
   const t=`This card is a child of \"${ls(ifield(parent,'name'))}\"`
   const l=layout_plaintext(t,FONT_BODY,ALIGN.left,rect(b.w,30));draw_text_wrap(rect(b.x,b.y+45,b.w,30),l,1)
  }
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(card),modal_exit(0)
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='button_props'){
  const b=draw_modalbox(rect(220,170)),button=ob.sel[0]
  draw_textc(rect(b.x,b.y-5,b.w,20),'Button Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name',FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,20),'Text',FONT_MENU,1)
  ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  ui_field(rect(b.x+42,b.y+40,b.w-42,18),ms.text)
  iwrite(button,lms('name'),rtext_string(ms.name.table))
  iwrite(button,lms('text'),rtext_string(ms.text.table)),mark_dirty()
  const style=ls(ifield(button,'style')), sb=rect(b.x,b.y+70)
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Round'    ,1,style=='round'    )){iwrite(button,lms('style'),lms('round'    )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Rectangle',1,style=='rect'     )){iwrite(button,lms('style'),lms('rect'     )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Checkbox' ,1,style=='check'    )){iwrite(button,lms('style'),lms('check'    )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Invisible',1,style=='invisible')){iwrite(button,lms('style'),lms('invisible')),mark_dirty()}sb.y+=16
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(button),modal_exit(0); c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),'Action...',1))modal_enter('action')
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='field_props'){
  const b=draw_modalbox(rect(220,170+60)),f=ob.sel[0], p=unpack_field(f)
  draw_textc(rect(b.x,b.y-5,b.w,20),'Field Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name',FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,60),'Text',FONT_MENU,1)
  ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  widget_field(null,{size:rect(b.x+42,b.y+40,b.w-42,58),font:p.font,show:'solid',scrollbar:1,border:1,style:'plain',align:p.align,locked:0},ms.text)
  iwrite(f,lms('name' ),rtext_string(ms.name.table))
  iwrite(f,lms('value'),ms.text.table),mark_dirty()
  let border=lb(ifield(f,'border')), scrollbar=lb(ifield(f,"scrollbar")), cb=rect(b.x,b.y+50+60)
  if(ui_checkbox(rect(cb.x,cb.y,b.w,16),'Border'   ,1,border   )){border   ^=1,iwrite(f,lms('border'   ),lmn(border   )),mark_dirty()}cb.y+=16
  if(ui_checkbox(rect(cb.x,cb.y,b.w,16),'Scrollbar',1,scrollbar)){scrollbar^=1,iwrite(f,lms('scrollbar'),lmn(scrollbar)),mark_dirty()}cb.y+=16
  const style=ls(ifield(f,'style')), sb=rect(b.x,cb.y+10);let cp=0
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Rich Text' ,1,style=='rich' )){iwrite(f,lms('style'),lms('rich' )),mark_dirty()     }sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Plain Text',1,style=='plain')){iwrite(f,lms('style'),lms('plain')),mark_dirty(),cp=1}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Code'      ,1,style=='code' )){iwrite(f,lms('style'),lms('code' )),mark_dirty(),cp=1}sb.y+=16
  if(cp&&!rtext_is_plain(ms.text.table))ms.text.table=rtext_cast(rtext_string(ms.text.table))
  const align=ls(ifield(f,'align')), ab=rect(b.x+(b.w/2),cb.y+10)
  if(ui_radio(rint(rect(ab.x,ab.y,b.w/2,16)),'Align Left' ,1,align=='left'  )){iwrite(f,lms('align'),lms('left'  )),mark_dirty()}ab.y+=16
  if(ui_radio(rint(rect(ab.x,ab.y,b.w/2,16)),'Center'     ,1,align=='center')){iwrite(f,lms('align'),lms('center')),mark_dirty()}ab.y+=16
  if(ui_radio(rint(rect(ab.x,ab.y,b.w/2,16)),'Align Right',1,align=='right' )){iwrite(f,lms('align'),lms('right' )),mark_dirty()}ab.y+=16
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(f),modal_exit(0)
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='slider_props'){
  const b=draw_modalbox(rect(220,170)),f=ob.sel[0]
  draw_textc(rect(b.x,b.y-5,b.w,20),'Slider Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name'  ,FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,20),'Format',FONT_MENU,1)
  ui_field(rect(b.x+50,b.y+20,b.w-50,18),ms.name)
  ui_field(rect(b.x+50,b.y+40,b.w-50,18),ms.text)
  iwrite(f,lms('name'  ),rtext_string(ms.name.table))
  iwrite(f,lms('format'),rtext_string(ms.text.table)),mark_dirty()
  const style=ls(ifield(f,'style')), sb=rect(b.x,b.y+70)
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Horizontal',1,style=='horiz'  )){iwrite(f,lms('style'),lms('horiz'  )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Vertical'  ,1,style=='vert'   )){iwrite(f,lms('style'),lms('vert'   )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Bar'       ,1,style=='bar'    )){iwrite(f,lms('style'),lms('bar'    )),mark_dirty()}sb.y+=16
  if(ui_radio(rint(rect(sb.x,sb.y,b.w/2,16)),'Compact'   ,1,style=='compact')){iwrite(f,lms('style'),lms('compact')),mark_dirty()}sb.y+=16
  const ib=rint(rect(b.x+b.w/2,b.y+70))
  draw_text(rect(ib.x+5,ib.y+2,40,20),'Min' ,FONT_MENU,1),ui_field(rint(rect(ib.x+40,ib.y,b.w/2-40,18)),ms.form0),ib.y+=20
  draw_text(rect(ib.x+5,ib.y+2,40,20),'Max' ,FONT_MENU,1),ui_field(rint(rect(ib.x+40,ib.y,b.w/2-40,18)),ms.form1),ib.y+=20
  draw_text(rect(ib.x+5,ib.y+2,40,20),'Step',FONT_MENU,1),ui_field(rint(rect(ib.x+40,ib.y,b.w/2-40,18)),ms.form2),ib.y+=20
  const c=rect(b.x,b.y+b.h-20)
  iwrite(f,lms('interval'),lml([rtext_string(ms.form0.table),rtext_string(ms.form1.table)]))
  iwrite(f,lms('step'),rtext_string(ms.form2.table)),mark_dirty()
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(f),modal_exit(0)
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='canvas_props'){
  const b=draw_modalbox(rect(220,141)),canvas=ob.sel[0]
  draw_textc(rect(b.x,b.y-5,b.w,20),'Canvas Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,42,20),'Name' ,FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,42,20),'Scale',FONT_MENU,1)
  ui_field(rect(b.x+42,b.y+20,b.w-42,18),ms.name)
  ui_field(rect(b.x+42,b.y+40,b.w-42,18),ms.text)
  iwrite(canvas,lms('name' ),rtext_string(ms.name.table))
  iwrite(canvas,lms('scale'),rtext_string(ms.text.table)),mark_dirty()
  let border=lb(ifield(canvas,'border')),draggable=lb(ifield(canvas,'draggable')),cb=rect(b.x,b.y+50+20)
  if(ui_checkbox(rect(cb.x,cb.y,b.w,16),'Border'   ,1,border   ))border   ^=1,iwrite(canvas,lms('border'   ),lmn(border   )),mark_dirty();cb.y+=16
  if(ui_checkbox(rect(cb.x,cb.y,b.w,16),'Draggable',1,draggable))draggable^=1,iwrite(canvas,lms('draggable'),lmn(draggable)),mark_dirty()
  const c=rect(b.x,b.y+b.h-20)
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))setscript(canvas),modal_exit(0)
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='grid_props'){
  const b=draw_modalbox(rect(220,140+70)),grid=ob.sel[0]
  draw_textc(rect(b.x,b.y-5,b.w,20),'Grid Properties',FONT_MENU,1)
  draw_text(rect(b.x,b.y+22,47,20),'Name'  ,FONT_MENU,1)
  draw_text(rect(b.x,b.y+42,47,20),'Format',FONT_MENU,1)
  draw_text(rect(b.x,b.y+62,47,20),'Value' ,FONT_MENU,1)
  ui_field   (rect(b.x+47,b.y+20,b.w-47,18),ms.name)
  ui_field   (rect(b.x+47,b.y+40,b.w-47,18),ms.text)
  ui_codeedit(rect(b.x+47,b.y+60,b.w-47,58),1,ms.form0)
  const etext=rtext_string(ms.form0.table),format=rtext_string(ms.text.table),val=table_decode(etext,format),cn=Object.keys(val.v).length,rn=count(val)
  draw_text(rect(b.x+47,b.y+60+60,b.w-47,18),`${cn} column${cn==1?'':'s'}, ${rn} row${rn==1?'':'s'}.`,FONT_BODY,1)
  iwrite(grid,lms('name'  ),rtext_string(ms.name.table))
  iwrite(grid,lms('format'),format),mark_dirty()
  let headers=lb(ifield(grid,'headers')), scrollbar=lb(ifield(grid,'scrollbar')), lines=lb(ifield(grid,'lines')), cb=rect(b.x,b.y+70+70)
  if(ui_checkbox(rint(rect(cb.x,cb.y,b.w/2,16)),'Column Headers',1,headers  )){headers  ^=1,iwrite(grid,lms('headers'  ),lmn(headers  )),mark_dirty()}cb.y+=16
  if(ui_checkbox(rint(rect(cb.x,cb.y,b.w/2,16)),'Scrollbar'     ,1,scrollbar)){scrollbar^=1,iwrite(grid,lms('scrollbar'),lmn(scrollbar)),mark_dirty()}cb.y+=16
  if(ui_checkbox(rint(rect(cb.x,cb.y,b.w/2,16)),'Grid Lines'    ,1,lines    )){lines    ^=1,iwrite(grid,lms('lines'    ),lmn(lines    )),mark_dirty()}cb.y+=16
  const eb=rint(rect(b.x+(b.w/2),b.y+70+70))
  if(ui_radio(rint(rect(eb.x,eb.y,b.w/2,16)),'Edit as JSON',1,ms.edit_json==1)){ms.form0=fieldstr(lms(fjson(monad.cols(val)))),ms.edit_json=1}eb.y+=16
  if(ui_radio(rint(rect(eb.x,eb.y,b.w/2,16)),'Edit as CSV' ,1,ms.edit_json==0))ms.form0=fieldstr(n_writecsv(count(format)?[val,format]:[val])),ms.edit_json=0
  const c=rect(b.x,b.y+b.h-20), w=ll(ifield(grid,'widths'))
  if(ui_button(rect(c.x,c.y,60,20),'Script...',1))modal_exit(0),setscript(grid);c.x+=65
  if(ui_button(rect(c.x,c.y,90,20),'Reset Widths',w.length))iwrite(grid,lms('widths'),lml([])),mark_dirty()
  if(ui_button(rect(b.x+b.w-60,c.y,60,20),'OK',1)||ev.exit)modal_exit(1)
 }
 else if(ms.type=='action'){
  const b=draw_modalbox(rect(220,180))
  draw_textc(rect(b.x,b.y-5,b.w,20),'Button Action',FONT_MENU,1)
  const c=rect(b.x+b.w-60,b.y+b.h-20), cr=rect(b.x,b.y+36)
  const ready=(ms.act_go||ms.act_sound)&&(ms.act_go?(ms.act_gomode!=4||count(ms.verb)):1)&&(ms.act_sound?count(ms.message):1)
  if(ui_button(rect(c.x,c.y,60,20),'OK',ready))modal_exit(1);c.x-=65
  if(ui_button(rect(c.x,c.y,60,20),'Cancel',1)||ev.exit)modal_exit(0)
  if(ui_checkbox(rint(rect(b.x,b.y+20,b.w/2,16)),'Go to Card',1,ms.act_go))ms.act_go^=1
  if(ui_radio(rect(cr.x+5,cr.y,80,16),'First'   ,ms.act_go,ms.act_gomode==0))ms.act_gomode=0;cr.y+=16
  if(ui_radio(rect(cr.x+5,cr.y,80,16),'Previous',ms.act_go,ms.act_gomode==1))ms.act_gomode=1;cr.y+=16
  if(ui_radio(rect(cr.x+5,cr.y,80,16),'Next'    ,ms.act_go,ms.act_gomode==2))ms.act_gomode=2;cr.y+=16
  if(ui_radio(rect(cr.x+5,cr.y,80,16),'Last'    ,ms.act_go,ms.act_gomode==3))ms.act_gomode=3;cr.y+=16
  if(ui_radio(rect(cr.x+5,cr.y,45,16),'Pick:'   ,ms.act_go,ms.act_gomode==4))ms.act_gomode=4
  if(ms.act_go&&ms.act_gomode==4){
   const l=rect(cr.x+5+45,cr.y,b.w-5-45-5-60,16)
   draw_hline(l.x,l.x+l.w,l.y+l.h,13),draw_text_fit(inset(l,1),ls(ms.verb),FONT_BODY,1)
   if(ui_button(rect(b.x+b.w-60,cr.y,60,20),'Choose...',ms.act_go&&ms.act_gomode==4))ms.type='pick_card'
  }cr.y+=26;
  if(ui_checkbox(rect(cr.x,cr.y,80,16),'Play a Sound',1,ms.act_sound))ms.act_sound^=1
  if(ms.act_sound){
   const l=rect(cr.x+5+75,cr.y,b.w-5-75-5-60,16)
   draw_hline(l.x,l.x+l.w,l.y+l.h,13),draw_text_fit(inset(l,1),ls(ms.message),FONT_BODY,1)
   if(ui_button(rect(b.x+b.w-60,cr.y,60,20),'Choose...',ms.act_sound)){
    ms.act_transno=ms.grid.row,ms.grid=gridtab(sounds_enumerate()),ms.from_action=1,ms.type='sounds'
   }
  }
  if(ms.act_go){
   if(ui_checkbox(rint(rect(b.x+b.w/2,b.y+20,b.w/2,16)),'With Transition',1,ms.act_trans))ms.act_trans^=1
   if(ms.act_trans)ui_list(rect(b.x+b.w/2,b.y+36,b.w/2,55),ms.grid)
  }
 }
 else if(ms.type=='pick_card'){
  const b=draw_modalbox(rect(220,45))
  draw_textc(rect(b.x,b.y,b.w,16),'Pick a card- any card.',FONT_BODY,1)
  const c=rint(rect(b.x+(b.w-60-5-60-5-60)/2,b.y+b.h-20))
  if(ui_button(rect(c.x,c.y,60,20),'Previous',1)||ev.dir=='left')n_go([lms('Prev')],deck);c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),'Choose',1))ms.verb=ifield(ifield(deck,'card'),'name'),n_go([lmn(ms.act_card)],deck),ms.type='action';c.x+=65
  if(ui_button(rect(c.x,c.y,60,20),'Next',1)||ev.dir=='right')n_go([lms('Next')],deck)
 }
 else if(ms.type=='trans'){
  const now=new Date().getTime()/1000
  const sofar=ms.time_start==-1?0:now-ms.time_start;if(ms.time_start==-1)ms.time_start=now
  let f=frame, tween=min(sofar*(ms.time_end/15),1.0)
  ms.canvas.image.pix=frame.image.pix;frame.image.pix.fill(0)
  const a=lml([ms.canvas,ms.carda,ms.cardb,lmn(tween)]), p=lmblk();blk_lit(p,ms.trans),blk_lit(p,a),blk_op(p,op.CALL)
  const e=lmenv();pushstate(e),issue(e,p);let quota=TRANS_QUOTA;while(quota&&running())runop(),quota--
  if(running()){listen_show(ALIGN.right,1,lms(`warning: transition ${ms.trans.n} exceeded quota and was halted.`)),tween=2}
  popstate(),frame=f,sleep_play=0,sleep_frames=0;if(tween>=1)modal_exit(0)
 }
 ms.in_modal=0
}
n_open=([type,hint])=>{
 modal_enter('open_lil');let t=type?ls(type):'',r=lms('');ms.filter=''
 if(t=='array')r=array_make(0,'u8',0)
 if(t=='sound')ms.filter='audio/*',r=sound_make(new Uint8Array(0))
 if(t=='image')ms.filter='image/*',r=image_make(rect())
 if(t=='text')ms.filter='.csv,.txt'
 ms.verb=t=='array'?lms(t): hint?ls(hint):'';return r
}
n_save=([x])=>{
 modal_enter('save_lil');x=x||NONE
 if(array_is(x)                              )ms.desc='Save a binary file.'    ,ms.text=fieldstr(lms('untitled.bin'))
 if(sound_is(x)                              )ms.desc='Save a .wav sound file.',ms.text=fieldstr(lms('sound.wav'))
 if(image_is(x)||(lil(x)&&x.v.some(image_is)))ms.desc='Save a .gif image file.',ms.text=fieldstr(lms('image.gif'))
 ms.verb=x;return NONE
}
n_alert=([t,p,x,y])=>{
 if(ls(p)=='bool'){modal_enter('confirm_lil'),ms.verb=x?lms(ls(x)):null}
 else if(ls(p)=='string'){modal_enter('input_lil');if(x)ms.text=fieldstr(x)}
 else if(ls(p)=='choose'){
  modal_enter('choose_lil')
  ms.verb=!x?ld(NONE): lil(x)?dyad.dict(x,x): ld(x); if(count(ms.verb)<1)ms.verb=ld(monad.list(NONE))
  ms.grid=gridtab(lt(monad.range(ms.verb)), y?dvix(ms.verb,y):-1)
 }else{modal_enter('alert_lil')}
 ms.message=plain_or_rich(t);return NONE
}
free_canvas=deck=>{ // make a drawing surface that isn't attached to the parent deck, but is aware of its resources:
 const d=deck_read('{deck}\n{card:home}\n{widgets}\nc:{"type":"canvas"}')
 const c=d.cards.v[0], r=c.widgets.v[0]
 iwrite(r,lms('size'),lmpair(deck.size)),d.fonts=deck.fonts,d.patterns=deck.patterns,r.free=1
 canvas_image(r,1);return r
}
go_notify=(deck,x,t,url)=>{
 if(url&&/(http|https|ftp|gopher|gemini):\/\//.test(url))modal_enter('url'),ms.text=fieldstr(lms(url))
 if(ms.type!='trans'&&x>=0&&t&&dget(deck.transit,lms(t))){
  modal_enter('trans'),ms.time_curr=0,ms.time_end=30,ms.time_start=-1;
  ms.trans=dget(deck.transit,lms(t)), ms.canvas=free_canvas(deck)
  ms.carda=draw_card(ifield(deck,'card')), ms.cardb=draw_card(ifield(deck,'cards').v[x])
 }
 const moved=x!=ln(ifield(ifield(deck,'card'),'index'))
 if(moved&&uimode=='interact')msg.pending_loop=1
 if(moved||t){
  grid_exit(),field_exit(),bg_end_selection(),bg_end_lasso(),ob.sel=[],wid.active=ms.type=='listen'?0:-1,mark_dirty()
 }
 if(uimode=='interact')msg.next_view=1
}

// General Purpose Edit History

let doc_hist=[], doc_hist_cursor=0
has_undo=_=>doc_hist_cursor>0
has_redo=_=>doc_hist_cursor<doc_hist.length
undo=_=>{const x=doc_hist[--(doc_hist_cursor)];apply(0,x)}
redo=_=>{const x=doc_hist[(doc_hist_cursor)++];apply(1,x)}
edit=x=>{doc_hist=doc_hist.slice(0,doc_hist_cursor),doc_hist.push(x),redo()}
apply=(fwd,x)=>{
 let card=ifield(deck,'card'), cn=ln(ifield(card,'index')), tn=x.card, t=x.type
 if(cn!=tn)n_go([lmn(tn)],deck),card=ifield(deck,'card')
 const wids=card.widgets
 if(t=='ob_create'){fwd=!fwd,t='ob_destroy'}
 if(t=='bg_block'){
  if(uimode!='draw')setmode('draw')
  const r=x.pos, p=x[fwd?'after':'before']
  let bg=canvas_image(card,1), s=bg.size
  const cs=getpair(ifield(card,'size'))
  if(s.x!=cs.x||s.y!=cs.y){bg=image_resize(bg,cs),iwrite(card,lms('image'),bg),s=cs}
  const cb=x.clr_before, ca=x.clr_after, clip=rect(0,0,s.x,s.y)
  if(fwd&&ca)image_paste(x.clr_pos,clip,ca,bg,1)
  image_paste(r,clip,p,bg,1)
  if(!fwd&&cb)image_paste(x.clr_pos,clip,cb,bg,1)
 }
 else if(t=='ob_props'){
  if(uimode!='object')setmode('object')
  const p=x[fwd?'after':'before']
  Object.keys(p).map(k=>{const d=p[k], w=dget(wids,lms(k));if(w)Object.keys(d).map(i=>iwrite(w,lms(i),d[i]))})
 }
 else if(t=='ob_destroy'){
  if(uimode!='object')setmode('object')
  if(fwd){
   const w=x.props.map(z=>dget(wids,dget(z,lms('name')))).filter(x=>x!=null)
   x.props=card_copy_raw(card,w),w.map(z=>card_remove(card,z))
  }
  else{
   ob.sel=[];const w=card_paste_raw(card,x.props);w.map((z,i)=>{
    dset(x.props[i],lms('name'),ifield(z,'name')),ob.sel.push(z)
    if(!dget(x.props[i],lms('pos')))iwrite(z,lms('pos'),lmpair(rcenter(frame.clip,getpair(ifield(z,'size')))))
   })
  }
 }mark_dirty()
}

// Draw Mode

image_overlay=(dst,src,mask)=>{for(let z=0;z<src.pix.length;z++)if(src.pix[z]!=mask)dst.pix[z]=src.pix[z]}
image_mask=(src,mask)=>{const r=image_copy(src);for(let z=0;z<mask.pix.length;z++)if(!mask.pix[z])r.pix[z]=0;return r}
bg_scratch_clear=_=>dr.scratch.pix.fill(BG_MASK)
bg_scratch=_=>{
 if(!dr.scratch)dr.scratch=image_make(frame.size)
 const s=dr.scratch.size;if(s.x!=frame.size.x||s.y!=frame.size.y)dr.scratch=image_make(frame.size)
 bg_scratch_clear()
}
bg_edit=_=>{
 const s=dr.scratch.size, d=rcopy(s)
 for(let z=0;z<dr.scratch.pix.length;z++){
  if(dr.scratch.pix[z]==BG_MASK)continue;const x=z%s.x, y=0|(z/s.x)
  d.x=min(d.x,x), d.y=min(d.y,y), d.w=max(d.w,x), d.h=max(d.h,y)
 }d.w-=d.x,d.h-=d.y,d.w++,d.h++
 const card=ifield(deck,'card'), back=ifield(card,'image')
 const after=image_copy(back,d);image_overlay(after,image_copy(dr.scratch,d),BG_MASK)
 edit({type:'bg_block',card:ln(ifield(card,'index')),pos:d,before:image_copy(back,d),after})
}
draw_limbo=(clip,scale)=>{
 if(!dr.limbo){/*nothing*/}
 else if(scale&&dr.limbo_dither){draw_rect      (card_to_fat(clip),21)}
 else if(scale                 ){draw_fat_scaled(clip,dr.limbo,!dr.trans,deck.patterns.pal.pix,frame_count,FAT,dr.offset)}
 else if(       dr.limbo_dither){draw_dithered  (clip,dr.limbo,!dr.trans,dr.omask,dr.dither_threshold)}
 else                           {draw_scaled    (clip,dr.limbo,!dr.trans)}
}
bg_scaled_limbo=_=>{
 const d=dr.sel_here, card=ifield(deck,'card'), back=ifield(card,'image')
 const r=dr.trans||dr.omask?image_copy(back,d):image_make(rect(d.w,d.h)), s=r.size, t=frame;frame=draw_frame(r)
 if(dr.trans){const c=dr.sel_start;draw_rect(rect(c.x-d.x,c.y-d.y,c.w,c.h),dr.fill)}
 draw_limbo(frame.clip,0),frame=t;return r
}
bg_edit_sel=_=>{
 if(!dr.limbo)return
 const d=rcopy(dr.sel_here), card=ifield(deck,'card'), back=ifield(card,'image')
 const r={type:'bg_block',card:ln(ifield(card,'index')),pos:d,before:image_copy(back,d),after:bg_scaled_limbo()}
 dr.limbo=null,dr.limbo_dither=0
 if(dr.sel_start.w>0||dr.sel_start.h>0){
  const after=image_make(rect(dr.sel_start.w,dr.sel_start.h));for(let z=0;z<after.pix.length;z++)after.pix[z]=dr.fill
  r.clr_pos=rcopy(dr.sel_start), r.clr_before=image_copy(back,dr.sel_start), r.clr_after=after, dr.sel_start=rect()
 }edit(r)
}
bg_copy_selection=s=>image_copy(canvas_image(ifield(deck,'card'),1),s)
bg_scoop_selection=_=>{if(dr.limbo)return;dr.sel_start=rcopy(dr.sel_here);dr.limbo=bg_copy_selection(dr.sel_start),dr.limbo_dither=0}
bg_draw_lasso=(r,show_ants,fill)=>{
 const o=dr.sel_start
 if(dr.omask)for(let a=0;a<o.h;a++)for(let b=0;b<o.w;b++){const h=rect(b+o.x,a+o.y);if(inclip(h)&&dr.omask.pix[b+a*o.w])pix(h,fill)}
 if(dr.mask)for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){const h=rect(b+r.x,a+r.y);if(inclip(h)&&dr.mask.pix[b+a*r.w]){
  const c=show_ants&&ANTS==(0xFF&dr.mask.pix[b+a*r.w]),p=c?ANTS:dr.limbo.pix[b+a*r.w];if(p||!dr.trans)pix(h,p)
 }}
}
bg_lasso_preview=_=>{
 if(!bg_has_lasso())return
 const pos=dr.fatbits?fat_to_card(ev.pos):ev.pos, dpos=dr.fatbits?fat_to_card(ev.dpos):ev.dpos
 const d=rect(pos.x-dpos.x,pos.y-dpos.y), dh=rect(dr.sel_here.x+d.x,dr.sel_here.y+d.y,dr.sel_here.w,dr.sel_here.h)
 const dd=rect(pos.x-dh.x,pos.y-dh.y), insel=dr.mask!=null&&rin(dh,pos)&&dr.mask.pix[dd.x+dd.y*dh.w], r=ev.drag&&insel?dh:dr.sel_here
 if(!dr.fatbits){bg_draw_lasso(r,1,dr.fill);return}
 const o=dr.sel_start, anim=deck.patterns.anim, pal=deck.patterns.pal.pix
 const anim_pattern=(pix,x,y)=>pix<28||pix>31?pix: anim[pix-28][(0|(frame_count/4))%max(1,anim[pix-28].length)]
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  if(!dr.omask.pix[b+a*o.w])continue
  draw_rect(rmul(rect(b+o.x-dr.offset.x,a+o.y-dr.offset.y,1,1),FAT),dr.fill)
 }
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  if(!dr.mask.pix[b+a*r.w])continue
  const v=dr.limbo.pix[b+a*r.w],c=anim_pattern(v,r.x+b,r.y+a),pat=draw_pattern(c,r.x+b,r.y+a)
  if(c||!dr.trans)draw_rect(rmul(rect(b+r.x-dr.offset.x,a+r.y-dr.offset.y,1,1),FAT),c>=32?c: c==0?c: pat?1:32)
 }
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++){
  if((0xFF&dr.mask.pix[b+a*r.w])!=ANTS)continue
  const p=rmul(rect(b+r.x-dr.offset.x,a+r.y-dr.offset.y),FAT)
  if(b<=    0||!dr.mask.pix[(b-1)+a*r.w])draw_vline(p.x      ,p.y,p.y+FAT-1,ANTS)
  if(b>=r.w-1||!dr.mask.pix[(b+1)+a*r.w])draw_vline(p.x+FAT-1,p.y,p.y+FAT-1,ANTS)
  if(a<=    0||!dr.mask.pix[b+(a-1)*r.w])draw_hline(p.x,p.x+FAT-1,p.y      ,ANTS)
  if(a>=r.h-1||!dr.mask.pix[b+(a+1)*r.w])draw_hline(p.x,p.x+FAT-1,p.y+FAT-1,ANTS)
 }
}
bg_tools=_=>{
 if     (!dr.fatbits&&ev.mu&&ev.alt){dr.fatbits=1,fat_offset(ev.pos);return}
 else if( dr.fatbits&&ev.mu&&ev.alt){dr.fatbits=0;return}if(ev.alt)return
 const pp=rcopy(pointer.prev), te=copy_object(ev)
 if(!dover(frame.clip))ev.md=ev.mu=ev.drag=0
 if(dr.fatbits){ev.pos=fat_to_card(ev.pos),ev.dpos=fat_to_card(ev.dpos),pointer.prev=fat_to_card(pointer.prev)}
 if(dr.tool=='pencil'||dr.tool=='line'||dr.tool=='rect'||dr.tool=='fillrect'||dr.tool=='ellipse'||dr.tool=='fillellipse'){
  let clear=0;if(!dr.scratch)bg_scratch()
  if(ev.md){bg_scratch(),dr.erasing=ev.rdown||ev.shift}
  else if(ev.mu||ev.drag){
   const t=frame;frame=draw_frame(dr.scratch)
   if(dr.tool=='pencil'||dr.erasing){
    draw_line(rpair(pointer.prev,ev.pos),dr.brush,dr.erasing?0:bg_pat())
   }
   else if(dr.tool=='line'){
    let b=rcopy(snap(ev.dpos)),t=rcopy(snap(ev.pos));if(ev.shift){ // snap to isometric angles
     const d=rsub(t,b);t=b
     if     (Math.abs(d.x)*4<Math.abs(d.y)){t.y+=d.y;}
     else if(Math.abs(d.y)*4<Math.abs(d.x)){t.x+=d.x;}
     else if(Math.abs(d.x)*2<Math.abs(d.y)){t.x+=sign(d.x)*Math.abs(d.y)/2,t.y+=d.y}
     else if(Math.abs(d.y)*2<Math.abs(d.x)){t.y+=sign(d.y)*Math.abs(d.x)/2,t.x+=d.x}
     else {t.x+=sign(d.x)*Math.abs(max(d.x,d.y)),t.y+=sign(d.y)*Math.abs(max(d.x,d.y))}
    }bg_scratch_clear(),draw_line(rpair(rint(b),rint(t)),dr.brush,bg_pat())
   }
   else if(dr.tool=='rect'||dr.tool=='fillrect'){
    const b=snap(ev.dpos),a=snap(ev.pos),t=rsub(a,b);if(ev.shift){t.x=t.y=max(t.x,t.y)} // snap to square
    bg_scratch_clear();const r=rnorm(rpair(b,t));r.w++,r.h++
    if(dr.tool=='fillrect')draw_rect(r,bg_fill());draw_box(r,dr.brush,bg_pat())
   }
   else if(dr.tool=='ellipse'||dr.tool=='fillellipse'){
    const b=snap(ev.dpos),a=snap(ev.pos),t=rsub(a,b);if(ev.shift){t.x=t.y=max(t.x,t.y)} // snap to circle
    bg_scratch_clear();const r=rnorm(rpair(radd(b,rect(1,1)),t));r.w--,r.h--
    const c=rect(r.x+(r.w/2.0),r.y+(r.h/2.0)), divs=100, poly=range(divs).map(z=>{
     const a=z*(2*Math.PI)/divs;return rint(rect(c.x+(0.5+r.w/2.0)*Math.cos(a),c.y+(0.5+r.h/2.0)*Math.sin(a)))
    });poly.push(poly[0])
    if(dr.tool=='fillellipse')draw_poly(poly,bg_fill());draw_lines(poly,dr.brush,bg_pat())
   }
   frame=t;if(ev.mu)bg_edit(),clear=1
  }
  if(dr.scratch){
   if(dr.fatbits){draw_fat(dr.scratch,deck.patterns.pal.pix,frame_count,BG_MASK,FAT,dr.offset)}
   else{image_overlay(frame.image,dr.scratch,BG_MASK);}
  }
  if(clear)bg_scratch_clear()
 }
 if(dr.tool=='lasso'){
  const d=rect(ev.pos.x-ev.dpos.x,ev.pos.y-ev.dpos.y)
  const dh=rect(dr.sel_here.x+d.x,dr.sel_here.y+d.y,dr.sel_here.w,dr.sel_here.h)
  const dd=rect(ev.pos.x-dh.x,ev.pos.y-dh.y), insel=dr.mask!=null&&over(dh)&&dr.mask.pix[dd.x+dd.y*dh.w]
  if(ev.md&&!insel){bg_lasso_preview(),bg_end_lasso(),dr.poly.push(rcopy(ev.dpos))}
  else if(ev.drag&&!insel&&dr.poly.length>0){const l=last(dr.poly);if(ev.pos.x!=l.x||ev.pos.y!=l.y)dr.poly.push(rcopy(ev.pos))}
  else if(ev.mu&&insel){dr.sel_here=radd(dr.sel_here,d)}
  else if(ev.mu){
   const r=poly_bounds(dr.poly);if(r.w>1&&r.h>1){
    dr.mask=image_make(rect(r.w,r.h))
    const t=frame;frame=draw_frame(dr.mask)
    for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++)if(poly_in(dr.poly,rect(b+r.x,a+r.y)))pix(rect(b,a),1); dr.omask=image_copy(dr.mask)
    if(dr.poly.length>0)draw_lines(dr.poly.concat(dr.poly[0]).map(p=>rsub(p,rect(r.x,r.y))),0,ANTS)
    frame=t,dr.sel_here=rcopy(r),dr.sel_start=rcopy(r),bg_scoop_selection()
   }dr.poly=[]
  }
  if(dr.mask&&dr.limbo){
   if(ev.dir=='left' ){ev.dir=0,dr.sel_here.x--}
   if(ev.dir=='up'   ){ev.dir=0,dr.sel_here.y--}
   if(ev.dir=='right'){ev.dir=0,dr.sel_here.x++}
   if(ev.dir=='down' ){ev.dir=0,dr.sel_here.y++}
   if(ev.exit){dr.limbo=dr.mask=dr.omask=null,bg_end_lasso()}
  }
 }
 if(dr.tool=='poly'){
  if(ev.md){dr.poly=[rect(ev.dpos.x,ev.dpos.y)]}
  else if(ev.drag&&dr.poly.length>0){const l=last(dr.poly);if(ev.pos.x!=l.x||ev.pos.y!=l.y)dr.poly.push(rcopy(ev.pos))}
  else if(ev.mu){
   dr.poly.push(rcopy(ev.dpos)),bg_scratch();const t=frame;frame=draw_frame(dr.scratch)
   draw_poly(dr.poly,bg_fill()),draw_lines(dr.poly,dr.brush,bg_pat()),frame=t,bg_edit(),bg_scratch_clear(),dr.poly=[]
  }
 }
 if(dr.tool=='lasso'||dr.tool=='poly'){
  const o=rsub(dr.sel_here,dr.sel_start)
  draw_lines(dr.poly.map(p=>radd(o,card_to_fat(p))),dr.tool=='lasso'?0:dr.brush,dr.tool=='lasso'?ANTS:bg_pat())
 }
 if(dr.tool=='fill'&&ev.mu){
  const bg=canvas_image(ifield(deck,'card'),1), t=frame;bg_scratch(),frame=draw_frame(dr.scratch)
  draw_fill(ev.pos,ev.rup?bg_fill():bg_pat(),bg),frame=t,bg_edit(),bg_scratch_clear()
 }
 if(!bg_has_sel()&&!bg_has_lasso()){
  if(dr.fatbits){
   if(ev.dir=='left' )dr.offset.x-=ev.shift?dr.grid_size.x:1
   if(ev.dir=='right')dr.offset.x+=ev.shift?dr.grid_size.x:1
   if(ev.dir=='up'   )dr.offset.y-=ev.shift?dr.grid_size.y:1
   if(ev.dir=='down' )dr.offset.y+=ev.shift?dr.grid_size.y:1
   dr.offset=rint(rect(max(0,min(dr.offset.x,frame.size.x-(frame.size.x/8))),max(0,min(dr.offset.y,frame.size.y-(frame.size.y/8)))))
   if(ev.exit)dr.fatbits=0
  }
  else{
   if(ev.dir=='left' )n_go([lms('Prev')],deck)
   if(ev.dir=='right')n_go([lms('Next')],deck)
  }
 }
 ev=te,pointer.prev=pp
}
bg_end_lasso=_=>{
 if(uimode!='draw'||dr.tool!='lasso')return
 const data=dr.mask&&dr.limbo, diffrect=!requ(dr.sel_here,dr.sel_start);let diffmask=dr.omask==null;
 if(dr.omask)for(let z=0;data&&z<dr.mask.pix.length;z++)if((dr.mask.pix[z]>0)!=(dr.omask.pix[z]>0)){diffmask=1;break}
 if(data&&(diffrect||diffmask)){
  bg_scratch();const t=frame;frame=draw_frame(dr.scratch),bg_draw_lasso(dr.sel_here,0,dr.fill),frame=t,bg_edit(),bg_scratch_clear()
 }dr.poly=[],dr.mask=null,dr.omask=null,dr.limbo=null,dr.sel_here=rect(),dr.sel_start=rect()
}
bg_end_selection=_=>{
 if(uimode!='draw'||dr.tool!='select')return
 if(dr.sel_here.w<=0&&dr.sel_here.h<=0)return
 bg_edit_sel(),dr.sel_start=rcopy(ev.dpos),dr.sel_here=rcopy(ev.dpos)
}
bg_delete_selection=_=>{
 if(bg_has_lasso()){
  dr.mask=null,bg_scratch();const t=frame;frame=draw_frame(dr.scratch),bg_draw_lasso(dr.sel_here,0,dr.fill),frame=t
  bg_edit(),bg_scratch_clear(),bg_end_lasso();return
 }
 if(!bg_has_sel())return
 if(dr.limbo!=null&&dr.sel_start.w<=0&&dr.sel_start.h<=0){dr.sel_here=rect(),dr.limbo=null,dr.limbo_dither=0;return}
 if(dr.sel_start.w<=0&&dr.sel_start.h<=0)dr.sel_start=rcopy(dr.sel_here)
 dr.sel_here=rect(),dr.limbo=image_make(rect(1,1)),dr.limbo_dither=0,bg_edit_sel(),dr.sel_start=rcopy(ev.dpos),dr.sel_here=rcopy(ev.dpos)
}
bg_paste=image=>{
 const clip=fat_clip(), f=rect(clip.w*.75,clip.h*.75);let s=image.size
 if(s.x>f.x||s.y>f.y){const scale=min(f.x/s.x,f.y/s.y);s=rect(s.x*scale,s.y*scale)}if(!s.x)return
 if(bg_has_sel()){bg_scoop_selection(),dr.limbo=image,dr.limbo_dither=0}
 else{settool('select'),dr.sel_start=rect(),dr.sel_here=rcenter(clip,s),dr.limbo=image,dr.limbo_dither=0}
}
draw_handles=r=>{
 const h=5, pal=deck.patterns.pal.pix
 const x0=r.x+1-h, x2=r.x+r.w-1, y0=r.y+1-h, y2=r.y+r.h-1, x1=0|((x2-x0)/2+x0), y1=0|((y2-y0)/2+y0)
 draw_invert(pal,rclip(rect(x0,y0,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x0,y2,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x2,y0,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x2,y2,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x2,y1,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x1,y2,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x1,y0,h,h),frame.clip))
 draw_invert(pal,rclip(rect(x0,y1,h,h),frame.clip))
}
in_handle=r=>{
 const h=5, x0=r.x+1-h, x2=r.x+r.w-1, y0=r.y+1-h, y2=r.y+r.h-1, x1=0|((x2-x0)/2+x0), y1=0|((y2-y0)/2+y0)
 if(over(rect(x0,y0,h,h)))return 4
 if(over(rect(x0,y2,h,h)))return 6
 if(over(rect(x2,y0,h,h)))return 2
 if(over(rect(x2,y2,h,h)))return 0
 if(over(rect(x2,y1,h,h)))return 1
 if(over(rect(x1,y0,h,h)))return 3
 if(over(rect(x0,y1,h,h)))return 5
 if(over(rect(x1,y2,h,h)))return 7;return -1
}
bg_select=_=>{
 if(uimode!='draw'||dr.tool!='select')return rect(0,0,0,0)
 const te=copy_object(ev)
 if(dr.fatbits){ev.pos=fat_to_card(ev.pos),ev.dpos=fat_to_card(ev.dpos),pointer.prev=fat_to_card(pointer.prev)}
 let s=rcopy(dr.sel_here), has_sel=s.w>0||s.h>0, in_sel=has_sel&&dover(s)
 const ax=min(ev.dpos.x,ev.pos.x), bx=max(ev.dpos.x,ev.pos.x), ay=min(ev.dpos.y,ev.pos.y), by=max(ev.dpos.y,ev.pos.y), h=5
 const x0=s.x+1-h, x2=s.x+s.w-1, y0=s.y+1-h, y2=s.y+s.h-1, x1=0|((x2-x0)/2+x0), y1=0|((y2-y0)/2+y0), dx=ev.pos.x-ev.dpos.x, dy=ev.pos.y-ev.dpos.y
 const sz=dr.limbo?dr.limbo.size:rect(dr.sel_start.w,dr.sel_start.h)
 handle=(rw,rh,ox,oy,ow,oh)=>{if(has_sel&&(ev.mu||ev.drag)&&dover(rect(rw,rh,h,h))){
  s=rnorm(keep_ratio(rect(s.x+ox,s.y+oy,s.w+ow,s.h+oh),sz))
  if(ev.mu)dr.sel_here=snapr(s);bg_scoop_selection(),uicursor=cursor.drag;return 1
 };return 0}
 if(in_layer()){
  if     (handle(x2,y2,  0, 0, dx, dy)){} // se
  else if(handle(x0,y2, dx, 0,-dx, dy)){} // sw
  else if(handle(x2,y0,  0,dy, dx,-dy)){} // ne
  else if(handle(x0,y0, dx,dy,-dx,-dy)){} // nw
  else if(handle(x2,y1,  0, 0, dx,  0)){} // e
  else if(handle(x1,y0,  0,dy,  0,-dy)){} // n
  else if(handle(x0,y1, dx, 0,-dx,  0)){} // w
  else if(handle(x1,y2,  0, 0,  0, dy)){} // s
  else if(ev.md&&in_sel){bg_scoop_selection()} // begin move
  else if((ev.mu||ev.drag)&&in_sel){s.x+=dx, s.y+=dy;if(ev.mu)dr.sel_here=snap(s)} // move/finish
  else if(ev.md&&!in_sel){if(has_sel){draw_limbo(card_to_fat(s),dr.fatbits),bg_end_selection(),has_sel=0}s=rcopy(dr.sel_here)} // begin create
  else if(ev.mu||ev.drag){s=snapr(rect(ax,ay,bx-ax,by-ay));if(ev.mu)dr.sel_here=s} // size/finish
 }
 if(has_sel)draw_limbo(s,dr.fatbits)
 if(in_layer()){
  let nudge=0
  if(has_sel&&ev.dir=='left' ){ev.dir=0,bg_scoop_selection(),dr.sel_here.x-=ev.shift?dr.grid_size.x:1,nudge=1}
  if(has_sel&&ev.dir=='up'   ){ev.dir=0,bg_scoop_selection(),dr.sel_here.y-=ev.shift?dr.grid_size.y:1,nudge=1}
  if(has_sel&&ev.dir=='right'){ev.dir=0,bg_scoop_selection(),dr.sel_here.x+=ev.shift?dr.grid_size.x:1,nudge=1}
  if(has_sel&&ev.dir=='down' ){ev.dir=0,bg_scoop_selection(),dr.sel_here.y+=ev.shift?dr.grid_size.y:1,nudge=1}
  if(nudge&&ev.shift)dr.sel_here=snap(dr.sel_here)
  if(ev.exit){dr.limbo=null,dr.limbo_dither=0,bg_end_selection()}
 }
 ev=te;return card_to_fat(s)
}
bg_tighten=_=>{
 const r=dr.sel_here
 const set=(p,v)=>dr.mask.pix[p.x+p.y*dr.sel_here.w]=v
 const get=p=>(p.x<0||p.y<0||p.x>=dr.sel_here.w||p.y>=dr.sel_here.h)?0:dr.mask.pix[p.x+p.y*dr.sel_here.w]
 if(dr.tool=='select'){ // convert box selections into masked lasso selections
  dr.tool='lasso',bg_scoop_selection();const s=dr.sel_start, l=dr.limbo, t=frame
  dr.limbo=image_make(rect(r.w,r.h)),frame=draw_frame(dr.limbo)
  if(dr.limbo_dither){draw_dithered(frame.clip,l,1,dr.omask,dr.dither_threshold),dr.limbo_dither=0}else{draw_scaled(frame.clip,l,1)}frame=t
  dr.mask=image_make(rect(r.w,r.h)),dr.mask.pix.fill(1)
  if(s.w>0&&s.h>0){dr.omask=image_make(rect(s.w,s.h)),dr.omask.pix.fill(1)}else{dr.omask=null}
 }
 let changed=1,background=bg_fill();while(changed){changed=0 // erode the mask, iterating to a fixed point
  for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++)if(get(rect(b,a))&&dr.limbo.pix[b+a*r.w]==background){
   const n=get(rect(b-1,a))&&get(rect(b,a-1))&&get(rect(b+1,a))&&get(rect(b,a+1));if(!n)set(rect(b,a),0),changed=1
  }
 }
 for(let a=0;a<r.h;a++)for(let b=0;b<r.w;b++)if(get(rect(b,a))){ // regenerate the ANTS outline
  const n=get(rect(b-1,a))&&get(rect(b,a-1))&&get(rect(b+1,a))&&get(rect(b,a+1));if(!n)set(rect(b,a),ANTS)
 }
}

// Object Edit Mode

ob_order=_=>{ob.sel.sort((av,bv)=>ln(ifield(av,'index'))-ln(ifield(bv,'index')))}
ob_edit_prop=(key,value)=>{
 const before={}, after={}
 ob.sel.map(w=>{const n=ls(ifield(w,'name')),bp={},ap={}; bp[key]=ifield(w,key),ap[key]=value,before[n]=bp,after[n]=ap})
 edit({type:'ob_props',card:ln(ifield(ifield(deck,'card'),'index')),before,after})
}
ob_create=props=>{
 edit({type:'ob_create',card:ln(ifield(ifield(deck,'card'),'index')),props})
}
ob_destroy=_=>{
 if(ob.sel.length<1)return
 const props=ob.sel.map(w=>lmd([lms('name')],[ifield(w,'name')]))
 edit({type:'ob_destroy',card:ln(ifield(ifield(deck,'card'),'index')),props}),ob.sel=[]
}
can_coalesce=move=>{
 if(has_redo()||doc_hist.length==0)return false
 const prev=doc_hist[doc_hist_cursor-1];if(prev.type!='ob_props')return false
 if(Object.keys(prev.after).length!=ob.sel.length)return false
 for(let z=0;z<ob.sel.length;z++){
  const key=Object.keys(prev.after)[z], val=prev.after[key]
  if(ls(ifield(ob.sel[z],'name'))!=key)return false
  if(Object.keys(val).length!=(move?1:2))return false
  const keys=Object.keys(val)
  if(keys[0]!='pos')return false
  if(!move&&keys[1]!='size')return false
 }return true
}
ob_move=(delta,coalesce)=>{
 if(ob.sel.length<1)return
 if(coalesce&&can_coalesce(1)){
  const r=doc_hist[doc_hist_cursor-1];
  Object.values(r.after).map(z=>z.pos=lmpair(radd(getpair(z.pos),delta))),undo(),redo()
 }else{
  const before={},after={};ob.sel.map(w=>{
   const n=ls(ifield(w,'name')),f=ifield(w,'pos'),fv=getpair(f)
   before[n]={pos:f},after[n]={pos:lmpair(radd(fv,delta))}
  }),edit({type:'ob_props',card:ln(ifield(ifield(deck,'card'),'index')),before,after})
 }
}
ob_resize=(size,coalesce)=>{
 if(ob.sel.length!=1)return;const w=ob.sel[0]
 if(coalesce&&can_coalesce(0)){
  const r=doc_hist[doc_hist_cursor-1],after=Object.values(r.after)[0]
  after.pos =lmpair(rect(size.x,size.y))
  after.size=lmpair(rect(size.w,size.h)),undo(),redo()
 }else{
  const bp={pos:ifield(w,'pos')            ,size:ifield(w,'size')           }
  const ap={pos:lmpair(rect(size.x,size.y)),size:lmpair(rect(size.w,size.h))}
  const before={},after={},n=ls(ifield(w,'name'));before[n]=bp,after[n]=ap
  edit({type:'ob_props',card:ln(ifield(ifield(deck,'card'),'index')),before,after})
 }
}
object_properties=x=>{
 ob.sel=[x]
 if(button_is(x))modal_enter('button_props')
 if(field_is (x))modal_enter('field_props' )
 if(slider_is(x))modal_enter('slider_props')
 if(canvas_is(x))modal_enter('canvas_props')
 if(grid_is  (x))modal_enter('grid_props'  )
}
object_editor=_=>{
 const card=ifield(deck,'card'), wids=ifield(card,'widgets'), pal=deck.patterns.pal.pix
 if(ob.sel.length==0&&in_layer()){
  if(ev.dir=='left' )n_go([lms('Prev')],deck)
  if(ev.dir=='right')n_go([lms('Next')],deck)
 }
 wids.v.map(wid=>{
  const w=unpack_widget(wid), sel=ob.sel.some(x=>x==wid)
  if(has_parent(card))draw_rect(w.size,23)
  if(sel){draw_box(inset(w.size,-1),0,ANTS)}else if(ob.show_bounds){draw_boxinv(pal,inset(w.size,-1))}
  if(sel&&ob.sel.length==1){draw_handles(w.size)}
  if(w.locked&&ob.show_bounds){draw_rect(rect(w.size.x+w.size.w-10,w.size.y,10,10),1),draw_icon(rect(w.size.x+w.size.w-8,w.size.y+1),LOCK,32)}
 })
 if(has_parent(card)||!in_layer())return
 if(ob.sel.length>0){
  let nudge=0
  if(ev.dir=='left' )ob_move(rect(-1*(ev.shift?dr.grid_size.x:1), 0),1),nudge=1
  if(ev.dir=='right')ob_move(rect( 1*(ev.shift?dr.grid_size.x:1), 0),1),nudge=1
  if(ev.dir=='up'   )ob_move(rect( 0,-1*(ev.shift?dr.grid_size.y:1)),1),nudge=1
  if(ev.dir=='down' )ob_move(rect( 0, 1*(ev.shift?dr.grid_size.y:1)),1),nudge=1
  if(nudge&&ev.shift&&ob.sel.length==1)ob_move(snap_delta(getpair(ifield(ob.sel[0],'pos'))),1)
 }
 const ish=ob.sel.length==1?in_handle(unpack_widget(ob.sel[0]).size):-1
 const isw=wids.v.some(w=>over(unpack_widget(w).size)&&ob.sel.some(x=>x==w))
 const a=ev.pos,b=ob.prev,dragged=a.x!=b.x||a.y!=b.y
 const sr=rnorm(rect(ev.dpos.x,ev.dpos.y,ev.pos.x-ev.dpos.x,ev.pos.y-ev.dpos.y)), box=sr.w>1||sr.h>1;
 if(isw&&ev.dclick){
  for(let z=count(wids)-1;z>=0;z--){
   const wid=wids.v[z], w=unpack_widget(wid)
   if(over(w.size)){object_properties(wid);break}
  }
 }
 else if(isw&&ev.mu&&!box){
  for(let z=ob.sel.length-1;z>=0;z--){
   const wid=ob.sel[z], w=unpack_widget(wid)
   if(over(w.size)){if(ev.shift){ob.sel=ob.sel.filter(x=>x!=wid)}else{ob.sel=[wid]};break}
  }
 }
 else if(ish!=-1&&ev.md){ob.resize=1,ob.resize_first=1,ob.handle=ish,ob.prev=rcopy(ev.pos),ob.orig=unpack_widget(ob.sel[0]).size}
 else if(isw    &&ev.md){ob.move  =1,ob.move_first  =1,ob.prev=rcopy(ev.pos)}
 else if(ob.resize&&(!ev.drag||!ob.sel.length)){ob.resize=0,ob.resize_first=0}
 else if(ob.move&&(!ev.drag||!ob.sel.length)){
  if(ob.sel.length==1)ob_move(snap_delta(getpair(ifield(ob.sel[0],'pos'))),!ob.move_first)
  ob.move=0,ob.move_first=0
 }
 else if(ob.resize){
  let delta=rsub(ev.pos,ev.dpos), r=rcopy(ob.orig)
  if(ob.handle==0){r.w+=delta.x, r.h+=delta.y}
  if(ob.handle==2){r.w+=delta.x, r.h-=delta.y, r.y+=delta.y}
  if(ob.handle==6){r.w-=delta.x, r.h+=delta.y, r.x+=delta.x}
  if(ob.handle==4){r.w-=delta.x, r.h-=delta.y, r.x+=delta.x, r.y+=delta.y}
  if(ob.handle==1){r.w+=delta.x}
  if(ob.handle==3){r.h-=delta.y, r.y+=delta.y}
  if(ob.handle==5){r.w-=delta.x, r.x+=delta.x}
  if(ob.handle==7){r.h+=delta.y}
  r=snapr(r), r.w=max(8,r.w), r.h=max(8,r.h)
  if(dragged)ob_resize(r,!ob.resize_first),ob.resize_first=0,ob.prev=rcopy(ev.pos)
 }
 else if(ob.move){
  const delta=rsub(a,b)
  if(dragged)ob_move(delta,!ob.move_first),ob.move_first=0,ob.prev=rcopy(ev.pos)
 }else{
  if(box&&ev.drag)draw_box(sr,0,ANTS); if(box&&(ev.drag||ev.mu))ob.sel=[]
  let f=0;for(let z=count(wids)-1;z>=0;z--){ // backward pass for selection priority(!)
   const wid=wids.v[z], w=unpack_widget(wid), sel=ob.sel.some(x=>x==wid), overlap=rclip(w.size,sr)
   const insel=box?overlap.w>=1&&overlap.h>=1: over(w.size)&&dover(w.size), c=ev.mu&insel
   if(c)f=1; if(!c)continue; if(box){ob.sel.push(wid);continue}
   if(!sel){if(!ev.shift)ob.sel=[];ob.sel.push(wid);break}
  }if(ev.mu&&!f)ob.sel=[]
 }
}

// Toolbars

let toolbars_enable=0, tzoom=1 // should be off by default?
const tcellw=22, tcellh=19, tgap=1, toolsize=rect(tcellw*2+1,tcellh*18+tgap+1), tfb=image_make(toolsize), tid=new ImageData(toolsize.x,toolsize.y)
const tooltypes=['select','pencil','lasso','line','fill','poly','rect','fillrect','ellipse','fillellipse']
const patorder=[0,1,4,5,8,9,16,17,12,13,18,19,20,21,22,23,24,25,26,27,2,6,3,7,10,11,14,15,28,29,30,31] // pleasing visual ramps for 2 columns

toolbars=_=>{
 if(!toolbars_enable)return
 const toolbar=(element,render,behavior)=>{
  const c=element.getBoundingClientRect()
  const pos =rint(rect((ev.rawpos .x-c.x)/tzoom,(ev.rawpos .y-c.y)/tzoom))
  const dpos=rint(rect((ev.rawdpos.x-c.x)/tzoom,(ev.rawdpos.y-c.y)/tzoom))
  tfb.pix.fill(0),frame=draw_frame(tfb),draw_box(rpair(rect(),toolsize),0,1),behavior(pos,dpos)
  const anim=deck.patterns.anim, pal=deck.patterns.pal.pix
  const animated=rin(rect(c.x,c.y,c.width,c.height),ev.rawpos)&&dr.show_anim?(0|(frame_count/4)):0
  const anim_pattern=(pix,x,y)=>pix<28||pix>31?pix: anim[pix-28][animated%max(1,anim[pix-28].length)]
  const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1
  const draw_color  =(pix,x,y)=>pix>47?0: pix>31?pix-32: draw_pattern(pix,x,y)?15:0
  const data=tid.data;for(let z=0,d=0,y=0;y<tid.height;y++)for(let x=0;x<tid.width;x++,z++){
   const pix=tfb.pix[z], a=anim_pattern(pix,x,y), c=draw_color(a,x,y), cv=COLORS[c]
   data[d++]=0xFF&(cv>>16),data[d++]=0xFF&(cv>>8),data[d++]=0xFF&(cv),data[d++]=0xFF
  }
  render.getContext('2d').putImageData(tid,0,0)
  const g=element.getContext('2d');g.imageSmoothingEnabled=false,g.save(),g.scale(tzoom,tzoom),g.drawImage(render,0,0),g.restore()
 }
 const toolbtn=(pos,dn,b,icon,active)=>{
  const i=rcenter(b,rect(16,16))
  draw_box(b,0,1);if(active)draw_rect(b,1);draw_icon(i,TOOLS[icon],active?0:1)
  if(rin(b,pos))uicursor=cursor.point;return rin(b,pos)&&rin(b,dn)&&ev.mu
 }
 const modebtn=(pos,dn,b,text,active)=>{
  draw_box(b,0,1);if(active)draw_rect(inset(b,2),1);draw_textc(b,text,FONT_BODY,active?0:1)
  if(rin(b,pos))uicursor=cursor.point;return rin(b,pos)&&rin(b,dn)&&ev.mu
 }
 const brushbtn=(pos,dn,b,brush)=>{
  const i=rint(rect(b.x+(b.w/2),b.y+(b.h/2)))
  draw_box(b,0,1),draw_line(rect(i.x,i.y,i.x,i.y),brush,1)
  if(dr.brush==brush)draw_box(inset(b,2),0,1); if(!rin(b,pos))return
  uicursor=cursor.point;if(!ev.mu||!rin(b,dn))return
  setmode('draw');if(dr.tool=='select'||dr.tool=='lasso'||dr.tool=='fill')settool('pencil');dr.brush=brush
 }
 const palbtn=(pos,dn,b,pattern)=>{
  if((dr.pickfill?dr.fill:dr.pattern)==pattern){draw_rect(inset(b,3),pattern),draw_box(inset(b,3),0,1)}else{draw_rect(b,pattern)}
  if(rin(b,pos)){uicursor=cursor.point;if(ev.mu&&rin(b,dn)){if(dr.pickfill){dr.fill=pattern}else{dr.pattern=pattern}}}draw_box(b,0,1)
 }
 toolbar(q('#ltools'),q('#lrender'),(pos,dn)=>{
  draw_rect(rect(0,6*tcellh,toolsize.x,tgap),1)
  if(toolbtn(pos,dn,rect(0     ,0,tcellw+1,tcellh+1),0,uimode=='interact'))setmode('interact'),ev.mu=ev.md=0
  if(toolbtn(pos,dn,rect(tcellw,0,tcellw+1,tcellh+1),1,uimode=='object'  ))setmode('object'  ),ev.mu=ev.md=0
  for(let z=0;z<2*12;z++)brushbtn(pos,dn,rect((z%2)*tcellw,(6+(0|(z/2)))*tcellh+tgap,tcellw+1,tcellh+1),((z*12)+(0|(z/2)))%24)
  for(let z=0;z<10;z++){
   if(toolbtn(pos,dn,rect((z%2)*tcellw,(1+(0|(z/2)))*tcellh,tcellw+1,tcellh+1),z+2,uimode=='draw'&&dr.tool==tooltypes[z])){
    settool(tooltypes[z]),ev.mu=ev.md=0
   }
  }
 })
 toolbar(q('#rtools'),q('#rrender'),(pos,dn)=>{
  draw_rect(rect(0,16*tcellh,toolsize.x,tgap),1)
  if(modebtn(pos,dn,rect(0,0     ,tcellw*2+1,tcellh+1),'Stroke',dr.pickfill==0))dr.pickfill=0
  if(modebtn(pos,dn,rect(0,tcellh,tcellw*2+1,tcellh+1),'Fill'  ,dr.pickfill==1))dr.pickfill=1
  for(let z=0;z<4*8;z++)palbtn(pos,dn,rect((z%2)*tcellw,(2+(0|(z/2)))*tcellh+(z>=28?tgap:0),tcellw+1,tcellh+1),patorder[z])
 })
}

// Script Editor

setscript=x=>{
 if(uimode!='script')sc.prev_mode=uimode;setmode('script')
 sc.status='', sc.target=lii(x)?x:x[0], sc.others=lii(x)?[]: x.slice(1)
 let v=ifield(sc.target,'script'), p=0
 if(!count(v))v=card_is  (sc.target)?(p=1,lms('on view do\n \nend')):
                button_is(sc.target)?(p=1,lms('on click do\n \nend')):
                grid_is  (sc.target)?(p=1,lms('on click row do\n \nend')):
                field_is (sc.target)?(p=1,lms('on change val do\n \nend')):
                slider_is(sc.target)?(p=1,lms('on change val do\n \nend')):
                canvas_is(sc.target)?(p=1,lms('on click pos do\n \nend\n\non drag pos do\n \nend\n\non release pos do\n \nend')):v
 sc.status=p?'No existing script; populated a template.':'',sc.f=fieldstr(v)
}
finish_script=_=>{if(sc.next){setscript(sc.next),sc.next=null}else{setmode(sc.prev_mode)}}
close_script=next=>{
 sc.next=next,field_exit()
 try{const text=ls(rtext_string(sc.f.table));parse(text),script_save(lms(text)),finish_script()}
 catch(e){
  modal_enter('confirm_script')
  ms.message=lms(`The current script contains errors:\n\n${e.x}\n\nDo you wish to discard your changes?`),ms.verb=lms('Discard')
 }
}
script_editor=_=>{
 const field_position=(x,cursor)=>{
  if(x.layout.length<1)return rect(1,1);cursor=max(0,min(cursor,x.layout.length+1))
  const e=cursor>=x.layout.length?1:0, i=cursor-e, l=x.layout[i].line, c=i-x.lines[l].range.x
  return rect(l+1,c+1+e)
 }
 const mh=3+font_h(FONT_MENU), bb=rect(0,mh,frame.size.x+1,frame.size.y-2*mh)
 let overw=null;if(sc.xray){
  const card=ifield(deck,'card'),wids=ifield(card,'widgets');
  for(let z=0;z<wids.v.length;z++){
   const wid=wids.v[z],size=unpack_widget(wid).size, o=ev.alt&&over(size), col=o?(overw=wid,13):44
   draw_textc(size,ls(ifield(wid,'name')),FONT_BODY,o?-1:col),draw_box(size,0,col)
   if(count(ifield(wid,'script')))draw_icon(rect(size.x-1,size.y),ICONS[ICON.lil],o?1:col)
   if(ev.alt&&ev.mu&&over(size)&&dover(size)){close_script(wid),ev.md=ev.mu=0;break}
  }if(ev.alt&&ev.mu)close_script(card),ev.md=ev.mu=0
 }
 ui_codeedit(bb,0,sc.f),draw_hline(0,frame.size.x,frame.size.y-mh-1,1)
 if(overw){uicursor=cursor.point;draw_textc(unpack_widget(overw).size,ls(ifield(overw,'name')),FONT_BODY,-1)}
 if(sc.status.length){draw_text_fit(rect(3,frame.size.y-mh+3,frame.size.x,mh-6),sc.status,FONT_BODY,1)}
 else{
  let stat='';if(in_layer()&&wid.infield){
   const x=layout_richtext(deck,sc.f.table,FONT_MONO,ALIGN.left,bb.w)
   let a=min(wid.cursor.x,wid.cursor.y), b=max(wid.cursor.x,wid.cursor.y)
   if(a!=b&&a<x.layout.length){
    a=max(0,min(a,x.layout.length)), b=max(0,min(b,x.layout.length))
    const ap=field_position(x,a),bp=field_position(x,b), l=(bp.x-ap.x)+1, c=b-a
    stat=`${l} line${l==1?'':'s'}, ${c} character${c==1?'':'s'} selected`
   }else{const p=field_position(x,min(a,b));stat=`Line ${p.x}, Column ${p.y}`}
  }
  const l=font_textsize(FONT_BODY,stat);draw_text(rect(3,frame.size.y-mh+3,l.x,l.y),stat,FONT_BODY,1)
  stat=`script of ${sc.target.n}  '${ls(ifield(sc.target,'name'))}'${sc.others.length?` and ${sc.others.length} more`:''}`
  const t=layout_plaintext(stat,FONT_BODY,ALIGN.right,rect(frame.size.x-6-20-l.x,font_h(FONT_BODY)))
  draw_text_wrap(rect(3+l.x+20,frame.size.y-mh+3,t.size.x,t.size.y),t,1)
 }if(in_layer()&&ev.exit)close_script(),ev.exit=0
}

// Runtime

interpret=_=>{
 if(msg.overshoot&&!running()&&!msg.pending_view&&!msg.next_view)msg.overshoot=0
 if(msg.pending_halt){if(running())halt();sleep_frames=0,sleep_play=0,msg.pending_view=0,msg.next_view=0}
 if(sleep_play&&sfx_any())return 0;sleep_play=0
 if(sleep_frames){sleep_frames--;return 0}
 const nomodal=_=>(ms.type==null||ms.type=='query'||ms.type=='listen')
 let quota=FRAME_QUOTA;while(1){
  while(nomodal()&&running()&&sleep_frames==0&&sleep_play==0&&quota>0){runop(),quota--,mark_dirty()}frame=context
  if(quota<=0&&running())msg.overshoot=1
  if(!nomodal()||quota<=0||sleep_frames||sleep_play){if(sleep_frames)sleep_frames--;break}
  if(!running()&&pending_popstate)popstate(),pending_popstate=0
  if(msg.pending_halt||pending_popstate){/*suppress other new events until this one finishes*/}
  else if(msg.pending_view){fire_event_async(ifield(deck,'card'),'view',NONE),msg.pending_view=0}
  else if(msg.target_click){
   const arg=grid_is(msg.target_click)?lmn(msg.arg_click.y): canvas_is(msg.target_click)?lmpair(msg.arg_click): NONE
   fire_event_async(msg.target_click,'click',arg),msg.target_click=null
  }
  else if(msg.target_drag    ){fire_event_async(msg.target_drag    ,'drag'    ,lmpair(msg.arg_drag   )),msg.target_drag    =null}
  else if(msg.target_release ){fire_event_async(msg.target_release ,'release' ,lmpair(msg.arg_release)),msg.target_release =null}
  else if(msg.target_run     ){fire_event_async(msg.target_run     ,'run'     ,msg.arg_run            ),msg.target_run     =null}
  else if(msg.target_link    ){fire_event_async(msg.target_link    ,'link'    ,msg.arg_link           ),msg.target_link    =null}
  else if(msg.target_order   ){fire_event_async(msg.target_order   ,'order'   ,msg.arg_order          ),msg.target_order   =null}
  else if(msg.target_change  ){fire_event_async(msg.target_change  ,'change'  ,msg.arg_change         ),msg.target_change  =null}
  else if(msg.target_navigate){fire_event_async(msg.target_navigate,'navigate',msg.arg_navigate       ),msg.target_navigate=null}
  if(!running())break // not running, and no remaining events to process, so we're done for this frame
 }if(msg.next_view&&ms.type!='listen')msg.pending_view=1,msg.next_view=0 // no more than one view[] event per frame!
 return FRAME_QUOTA-quota
}

text_edit_menu=_=>{
 const selection=wid.fv!=null&&wid.cursor.x!=wid.cursor.y
 if(menu_item('Undo',wid.hist_cursor>0              ,'z'))field_undo()
 if(menu_item('Redo',wid.hist_cursor<wid.hist.length,'Z'))field_redo()
 menu_separator()
 if(menu_item('Cut',selection,'x',menucut)){}
 if(menu_item('Copy',selection,'c',menucopy)){}
 if(menu_item('Paste',wid.fv!=null,'v',menupaste)){}
 if(menu_item('Clear',wid.fv!=null))wid.cursor=rect(0,RTEXT_END),field_keys('Delete',0)
 menu_separator()
 if(menu_item('Select All',wid.fv!=null,'a'))wid.cursor=rect(0,RTEXT_END)
}
all_menus=_=>{
 const blocked=running()||msg.overshoot
 const canlisten=!blocked&&(ms.type=='listen'||ms.type==null)
 menu_bar('Decker',canlisten)
 if(menu_item('About...',1))modal_enter('about')
 if(menu_check('Listener',canlisten,ms.type=='listen','l')){if(ms.type!='listen'){modal_enter('listen')}else{modal_exit(0)}}
 menu_separator()
 if(menu_check('Nav Gestures',1,enable_gestures))enable_gestures^=1
 if(menu_check('Script Profiler',1,profiler))profiler^=1
 if(menu_check('Toolbars',tzoom>0,toolbars_enable))toolbars_enable^=1,resize()
 if(blocked){
  menu_bar('Script',1)
  if(menu_item('Stop',1)){msg.pending_halt=1;if(ms.type!=null&&ms.type!='query'&&ms.type!='listen')modal_exit(0)}
  menu_bar('Edit',(ms.type=='input'||ms.type=='save')&&wid.fv)
  text_edit_menu()
  return
 }
 menu_bar('File',ms.type==null||ms.type=='recording')
 if(uimode=='script'){
  if(menu_item('Close Script',1))close_script()
  if(menu_item('Save Script',1,'s')){
   try{const text=ls(rtext_string(sc.f.table));parse(text),script_save(lms(text));sc.status='Saved.'}
   catch(e){sc.status=`Error: ${e.x}`,wid.cursor=rect(e.i,e.i)}
  }
  menu_separator()
  menu_item('Import Script...',1,0,_=>open_text('.lil,.txt',text=>{field_exit(),sc.f=fieldstr(lms(text))}))
  if(menu_item('Export Script...',1))modal_enter('export_script')
  menu_separator()
  if(menu_item('Go to Deck',!deck_is(sc.target)           ))close_script(deck)
  if(menu_item('Go to Card',sc.target!=ifield(deck,'card')))close_script(ifield(deck,'card'))
  if(menu_check('X-Ray Specs',1,sc.xray))sc.xray^=1
 }
 else if(ms.type=='recording'){
  menu_item('Import Sound...',1,0,_=>open_file('audio/*',load_sound))
  menu_separator();
  if(menu_item('Close Sound',1))modal_exit(0)
 }
 else{
  if(menu_item('New Deck...',1)){
   if(dirty){
    modal_enter('confirm_new')
    ms.message=lms('The current deck has unsaved changes.\nAre you sure you want to discard it?')
    ms.verb=lms('Discard')
   }else{load_deck(deck_read(''))}
  }
  if(menu_item('New Card',1)){const c=deck_add(deck,lms('card')), n=ln(ifield(ifield(deck,'card'),'index'));iwrite(c,lms('index'),lmn(n+1)),n_go([c],deck)}
  menu_separator()
  menu_item('Open...',1,0,_=>open_text('.html,.deck',text=>{load_deck(deck_read(text))}))
  if(menu_item('Save As...',1))modal_enter('save_deck')
  menu_separator()
  if(wid.gv){
   menu_item('Import Table...',!wid.g.locked,0,_=>open_text('.csv',text=>{
    const a=[lms(text)];if(wid.g.format.length)a.push(lms(wid.g.format))
    grid_edit(n_readcsv(a))
   }))
   if(menu_item('Export Table...',1))modal_enter('export_table')
  }
  else{
   menu_item("Import Image...",1,0,_=>{open_file('image/*',load_image)})
   if(menu_item("Export Image...",1))modal_enter('export_image')
  }
  menu_separator()
  if(menu_item('Cards...'     ,1,'C'))modal_enter('cards')
  if(menu_item('Sounds...'    ,1,'S'))modal_enter('sounds')
  if(menu_item('Resources...' ,1,   ))modal_enter('resources')
  if(menu_item('Properties...',1,   ))modal_enter('deck_props')
 }
 if(ms.type==null||wid.gv||wid.fv){
  menu_bar("Edit",wid.gv||wid.fv||(ms.type==null&&uimode=='interact')||uimode=='draw'||uimode=='object')
  if(wid.gv){
   const mutable=!wid.g.locked&&ms.type==null
   if(menu_item('Undo',wid.hist_cursor>0              ,'z'))grid_undo()
   if(menu_item('Redo',wid.hist_cursor<wid.hist.length,'Z'))grid_redo()
   menu_separator()
   if(menu_item('Copy Table',1,'c',menucopy)){}
   if(menu_item('Paste Table',mutable,'v',menupaste)){}
   menu_separator()
   if(menu_item('Delete Row',mutable&&wid.gv.row!=-1))grid_deleterow()
   if(menu_item('Add Row',mutable))grid_insertrow()
   if(menu_item('Query...',ms.type==null,'u'))modal_enter('query')
  }
  if(wid.fv)text_edit_menu()
  if(ms.type==null&&uimode=='interact'){
   if(menu_item('Undo',has_undo(),'z'))undo()
   if(menu_item('Redo',has_redo(),'Z'))redo()
   menu_separator()
   if(menu_item('Paste',1,'v',menupaste)){}
  }
  if(ms.type==null&&uimode=='draw'){
   const sel=bg_has_sel()||bg_has_lasso()
   if(menu_item('Undo',(!sel)&&has_undo(),'z'))undo()
   if(menu_item('Redo',(!sel)&&has_redo(),'Z'))redo()
   menu_separator()
   if(menu_item('Cut Image',sel,0,menucut)){}
   if(menu_item('Copy Image',sel,0,menucopy)){}
   if(menu_item('Paste',1,'v',menupaste)){}
   if(menu_item('Clear',1)){const t=dr.tool;if(!sel){settool('select'),dr.sel_here=rcopy(frame.clip)}bg_delete_selection(),settool(t)}
   menu_separator()
   if(menu_item('Select All',1,'a')){settool('select'),dr.sel_here=rcopy(frame.clip)}
   if(menu_item('Tight Selection',sel,'g'))bg_tighten()
   if(menu_item("Resize to Original",sel&&dr.tool=='select',0)){bg_scoop_selection();const s=dr.limbo.size;dr.sel_here.w=s.x,dr.sel_here.h=s.y}
   if(menu_item("Resize to Card"    ,sel&&dr.tool=='select',0)){bg_scoop_selection(),dr.sel_here=rect(0,0,frame.size.x,frame.size.y)}
   menu_separator()
   if(menu_item('Invert',sel&&!dr.limbo_dither,'i')){
    if(bg_has_sel())bg_scoop_selection()
    const s=dr.limbo.size, pal=deck.patterns.pal.pix
    for(let z=0;z<dr.limbo.pix.length;z++)dr.limbo.pix[z]=1^draw_pattern(dr.limbo.pix[z],(z%s.x),0|(z/s.x))
   }
   if(menu_item('Flip Horizontal',sel)){
    if(bg_has_sel())bg_scoop_selection()
    image_flip_h(dr.limbo);if(dr.mask)image_flip_h(dr.mask);if(dr.omask&&dr.limbo_dither)image_flip_h(dr.omask);
   }
   if(menu_item('Flip Vertical'  ,sel)){
    if(bg_has_sel())bg_scoop_selection()
    image_flip_v(dr.limbo);if(dr.mask)image_flip_v(dr.mask);if(dr.omask&&dr.limbo_dither)image_flip_v(dr.omask);
   }
   if(menu_item('Rotate Left',sel,',')){
    const s=rect(dr.sel_here.w,dr.sel_here.h)
    if(bg_has_sel())bg_scoop_selection()
    image_flip_h(dr.limbo),image_flip(dr.limbo)
    if(dr.mask)image_flip_h(dr.mask),image_flip(dr.mask)
    if(dr.omask&&dr.limbo_dither)image_flip_h(dr.omask),image_flip(dr.omask)
    dr.sel_here.w=s.y,dr.sel_here.h=s.x
   }
   if(menu_item('Rotate Right',sel,'.')){
    const s=rect(dr.sel_here.w,dr.sel_here.h)
    if(bg_has_sel())bg_scoop_selection()
    image_flip(dr.limbo),image_flip_h(dr.limbo)
    if(dr.mask)image_flip(dr.mask),image_flip_h(dr.mask)
    if(dr.omask&&dr.limbo_dither)image_flip(dr.omask),image_flip_h(dr.omask)
    dr.sel_here.w=s.y,dr.sel_here.h=s.x
   }
   if(dr.limbo_dither&&sel){
    menu_separator()
    if(menu_item('Lighten Image',dr.dither_threshold>-2.0))dr.dither_threshold-=.1
    if(menu_item('Darken  Image',dr.dither_threshold< 2.0))dr.dither_threshold+=.1
   }
  }
  const card=ifield(deck,'card')
  if(ms.type==null&&uimode=='object'){
   if(menu_item('Undo',has_undo(),'z'))undo()
   if(menu_item('Redo',has_redo(),'Z'))redo()
   menu_separator()
   if(menu_item('Cut Widgets',ob.sel.length,'x',menucut)){}
   if(menu_item('Copy Widgets',ob.sel.length,'c',menucopy)){}
   if(menu_item('Copy Image',ob.sel.length==1&&canvas_is(ob.sel[0]),0,copycanvasimg)){}
   if(menu_item('Paste',1,'v',menupaste)){}
   menu_separator()
   if(menu_item('Paste as new Canvas',!has_parent(card),0,pasteascanvas)){}
   if(menu_item('Paste into Canvas',ob.sel.length==1&&canvas_is(ob.sel[0]),0,pasteintocanvas)){}
   menu_separator()
   if(menu_item('Select All',!has_parent(card),'a'))ob.sel=card.widgets.v.slice(0)
   if(menu_item('Move to Front',ob.sel.length))ob_order(),ob.sel                   .map(w=>iwrite(w,lms('index'),lmn(RTEXT_END))),mark_dirty()
   if(menu_item('Move to Back' ,ob.sel.length))ob_order(),ob.sel.slice(0).reverse().map(w=>iwrite(w,lms('index'),NONE          )),mark_dirty()
  }
  if(wid.fv&&wid.f){
   const selection=wid.fv!=null&&wid.cursor.x!=wid.cursor.y
   menu_bar('Text',ms.type!='field_props')
   if(wid.f.style=='rich'){
    if(menu_item('Heading'    ,selection))field_stylespan(lms('menu'),lms(''))
    if(menu_item('Body'       ,selection))field_stylespan(lms(''    ),lms(''))
    if(menu_item('Fixed Width',selection))field_stylespan(lms('mono'),lms(''))
    if(menu_item('Link...'    ,selection))modal_enter('link')
   }
   else if(wid.f.style=='code'){
    if(menu_item('Indent'        ,1    ))field_indent(1)
    if(menu_item('Unindent'      ,1    ))field_indent(0)
    if(menu_item('Toggle Comment',1,'/'))field_comment()
   }
   if(wid.f&&wid.f.style!='code'){
    if(menu_item('Font...',!has_parent(card)&&wid.f.style!='plain'))modal_enter('fonts')
   }
  }
 }
 if(ms.type=='recording'&&!wid.fv){
  menu_bar('Edit',au.mode=='stopped')
  if(menu_item('Undo',au.hist_cursor>0             ,'z'))sound_undo()
  if(menu_item('Redo',au.hist_cursor<au.hist.length,'Z'))sound_redo()
  menu_separator()
  if(menu_item('Cut Sound'  ,1,'x',menucut)){}
  if(menu_item('Copy Sound' ,1,'c',menucopy)){}
  if(menu_item('Paste Sound',1,'v',menupaste)){}
  if(menu_item('Clear',1,0))sound_delete()
  menu_separator()
  if(menu_item('Select All',1,'a'))au.head=0,au.sel=rect(0,au.target.data.length-1)
 }
 if(uimode=='interact'||uimode=='draw'||uimode=='object'){
  menu_bar('Card',ms.type==null)
  const card=ifield(deck,'card'), parent=ifield(card,'parent')
  if(menu_item('Go to First'   ,1))n_go([lms('First')],deck)
  if(menu_item('Go to Previous',1))n_go([lms('Prev' )],deck)
  if(menu_item('Go to Next'    ,1))n_go([lms('Next' )],deck)
  if(menu_item('Go to Last'    ,1))n_go([lms('Last' )],deck)
  if(menu_item('Go to Parent',has_parent(card)))n_go([parent],deck)
  menu_separator()
  if(menu_item('Cut Card',1,0,cutcard)){}
  if(menu_item('Copy Card',1,0,copycard)){}
  menu_separator()
  if(menu_item('Script...'    ,!has_parent(card)))setscript(card)
  if(menu_item('Properties...',1))modal_enter('card_props')
  menu_bar('Tool',ms.type==null)
  if(menu_check('Interact',1,uimode=='interact',0))setmode('interact')
  if(menu_check('Widgets' ,1,uimode=='object'  ,0))setmode('object')
  menu_separator()
  if(menu_check('Select'     ,1,uimode=='draw'&&dr.tool=='select'     ))settool('select'     )
  if(menu_check('Lasso'      ,1,uimode=='draw'&&dr.tool=='lasso'      ))settool('lasso'      )
  if(menu_check('Pencil'     ,1,uimode=='draw'&&dr.tool=='pencil'     ))settool('pencil'     )
  if(menu_check('Line'       ,1,uimode=='draw'&&dr.tool=='line'       ))settool('line'       )
  if(menu_check('Flood'      ,1,uimode=='draw'&&dr.tool=='fill'       ))settool('fill'       )
  if(menu_check('Box'        ,1,uimode=='draw'&&dr.tool=='rect'       ))settool('rect'       )
  if(menu_check('Filled Box' ,1,uimode=='draw'&&dr.tool=='fillrect'   ))settool('fillrect'   )
  if(menu_check('Oval'       ,1,uimode=='draw'&&dr.tool=='ellipse'    ))settool('ellipse'    )
  if(menu_check('Filled Oval',1,uimode=='draw'&&dr.tool=='fillellipse'))settool('fillellipse')
  if(menu_check('Polygon'    ,1,uimode=='draw'&&dr.tool=='poly'       ))settool('poly'       )
 }
 if(uimode=='draw'||uimode=='object'){
  menu_bar('View',ms.type==null)
  if(menu_check('Show Widgets'      ,1,dr.show_widgets))dr.show_widgets^=1
  if(menu_check('Show Widget Bounds',1,ob.show_bounds ))ob.show_bounds ^=1
  if(menu_check('Show Widget Names' ,1,ob.show_names  ))ob.show_names  ^=1
  if(menu_check('Show Cursor Info'  ,1,ob.show_cursor ))ob.show_cursor ^=1
  menu_separator()
  if(menu_check('Show Grid Overlay',1,dr.show_grid))dr.show_grid^=1
  if(menu_check('Snap to Grid'     ,1,dr.snap     ))dr.snap     ^=1
  if(menu_item('Grid Size...',1))modal_enter('grid')
  menu_separator()
  if(menu_check('Show Animation'   ,1,dr.show_anim ))dr.show_anim ^=1
  if(menu_check('Transparency Mask',1,dr.trans_mask))dr.trans_mask^=1
  if(menu_check('Fat Bits'         ,1,dr.fatbits   )){
   if(uimode!='draw')setmode('draw')
   dr.fatbits^=1;if(dr.fatbits)fat_offset(rcenter(bg_has_sel()||bg_has_lasso()?dr.sel_here:frame.clip,rect()))
  }
 }
 if(uimode=='draw'){
  menu_bar('Style',ms.type==null)
  if(menu_item('Stroke...',1))modal_enter('pattern')
  if(menu_item('Fill...'  ,1))modal_enter('fill'   )
  if(menu_item('Brush...' ,1))modal_enter('brush'  )
  menu_separator()
  if(menu_check('Transparency',1,dr.trans))dr.trans^=1
 }
 if(uimode=='object'){
  menu_bar('Widgets',ms.type==null&&!has_parent(ifield(deck,'card')))
  if(menu_item('New Button...',ob.sel.length==0))ob_create([lmd([lms('type')],[lms('button')])])
  if(menu_item('New Field...' ,ob.sel.length==0))ob_create([lmd([lms('type')],[lms('field' )])])
  if(menu_item('New Slider...',ob.sel.length==0))ob_create([lmd([lms('type')],[lms('slider')])])
  if(menu_item('New Canvas...',ob.sel.length==0))ob_create([lmd([lms('type')],[lms('canvas')])])
  if(menu_item('New Grid...'  ,ob.sel.length==0))ob_create([lmd([lms('type')],[lms('grid'  )])])
  menu_separator()
  let al=1,as=1,at=1,ai=1,an=1
  ob.sel.map(unpack_widget).map(w=>{al&=w.locked, as&=w.show=='solid', at&=w.show=='transparent', ai&=w.show=='invert', an&=w.show=='none'})
  if(menu_check('Locked'          ,ob.sel.length,ob.sel.length&&al))ob_edit_prop('locked',lmn(!al))
  if(menu_check('Show Solid'      ,ob.sel.length,ob.sel.length&&as))ob_edit_prop('show',lms('solid'      ))
  if(menu_check('Show Transparent',ob.sel.length,ob.sel.length&&at))ob_edit_prop('show',lms('transparent'))
  if(menu_check('Show Inverted'   ,ob.sel.length,ob.sel.length&&ai))ob_edit_prop('show',lms('invert'     ))
  if(menu_check('Show None'       ,ob.sel.length,ob.sel.length&&an))ob_edit_prop('show',lms('none'       ))
  menu_separator()
  if(menu_item('Font...'  ,ob.sel.length))modal_enter('fonts')
  if(menu_item('Script...',ob.sel.length)){
   if(ob.sel.reduce((m,v)=>m&&ob.sel[0].script==v.script,1)){setscript(ob.sel)}else{
    modal_enter('multiscript')
     ms.message=lms('Not all of the selected widgets\nhave the same script.\nEdit them all together anyway?')
     ms.verb=lms('Edit')
   }
  }
  if(menu_item('Properties...',ob.sel.length==1)||(ob.sel.length==1&&ev.action&&ms.type==null))object_properties(ob.sel[0])
 }
 if(ms.type=='listen'){
  menu_bar('Listener',1)
  if(menu_item('Clear History',1))li.hist=[],li.scroll=0
  if(menu_item('Clear Locals' ,1))li.vars={}
  menu_separator()
  if(menu_item('Show Locals',1))listen_show(ALIGN.right,0,lmd(Object.keys(li.vars).map(lms),Object.values(li.vars)))
 }
}

main_view=_=>{
 const card=ifield(deck,'card'), back=ifield(card,'image'), wids=ifield(card,'widgets'), pal=deck.patterns.pal.pix
 if(back.size.x>0&&back.size.y>0&&ms.type!='trans'){
  if(dr.fatbits){draw_fat(back,pal,frame_count,0,FAT,dr.offset)}
  else{image_paste(rpair(rect(),back.size),frame.clip,back,frame.image,1)}
 }
 if(uimode=='draw'&&in_layer())bg_tools()
 if(dr.tool=='select'&&(dr.sel_start.w>0||dr.sel_start.h>0))draw_rect(card_to_fat(dr.sel_start),dr.fill)
 bg_lasso_preview()
 const livesel=bg_select()
 if(((uimode=='object'||uimode=='draw')&&dr.show_grid)||ms.type=='grid'){
  for(let x=0;x<frame.size.x;x++){const r=card_to_fat(rect(x*dr.grid_size.x,0,1,frame.size.y));draw_vline(r.x,r.y,r.y+r.h,44)}
  for(let y=0;y<frame.size.y;y++){const r=card_to_fat(rect(0,y*dr.grid_size.y,frame.size.x,1));draw_hline(r.x,r.x+r.w,r.y,44)}
 }
 const eb=ev;if(uimode!='interact')ev=event_state()
 if(uimode=='interact'||(dr.show_widgets&&!dr.fatbits))wids.v.map(w=>{
  if(button_is(w)){
   const v=lb(ifield(w,'value')), p=unpack_button(w)
   if(widget_button(w,p,v)&&p.style=='check')iwrite(w,lms('value'),lmn(!v)),mark_dirty()
  }
  if(slider_is(w)){widget_slider(w,unpack_slider(w))}
  if(canvas_is(w)){const v=canvas_image(w,0);widget_canvas(w,unpack_canvas(w),v)}
  if(grid_is(w)){
   const p=unpack_grid(w),v=unpack_grid_value(w);widget_grid(w,p,v)
   if(wid.gt==w)wid.gv=v
  }
  if(field_is(w)){
   if(wid.ft==w){widget_field(w,wid.f,wid.fv)}
   else{const p=unpack_field(w),v=unpack_field_value(w);widget_field(w,p,v);if(wid.ft==w)wid.fv=v}
  }
 })
 else if(dr.show_widgets&&dr.fatbits)wids.v.map(w=>{draw_boxinv(pal,card_to_fat(unpack_widget(w).size))})
 ev=eb
 if(uimode=='draw'){if(bg_has_sel())draw_handles(livesel);draw_box(livesel,0,ANTS)}
 if(wid.pending_grid_edit){wid.pending_grid_edit=0;modal_enter('gridcell')}
 if(uimode=='object')object_editor()
 if((uimode=='object'&&ob.show_names)||(uimode=='draw'&&dr.show_widgets&&dr.fatbits)||ms.type=='listen'){
  wids.v.map(wid=>{
   const size=card_to_fat(unpack_widget(wid).size)
   const n=ls(ifield(wid,'name')),s=font_textsize(FONT_BODY,n)
   draw_text_outlined(rect(size.x,size.y-s.y,s.x,s.y),n,FONT_BODY)
  })
 }
 if(ob.show_cursor&&ms.type==null&&uimode in {draw:1,object:1}){
  if(uimode=='draw'&&bg_has_sel()){
   let r=livesel,l=r;if(dr.fatbits){const p=fat_to_card(r);l=rint(rect(p.x,p.y,r.w/FAT,r.h/FAT))}
   const t=ls(dyad.format(lms('(%3i,%3i,%3i,%3i)'),lml([l.x,l.y,l.w,l.h].map(lmn)))),s=font_textsize(FONT_BODY,t)
   draw_text_outlined(rect(r.x,r.y-s.y,s.x,s.y),t,FONT_BODY)
  }
  else if(ev.drag){
   const a=dr.fatbits?fat_to_card(ev.dpos):ev.dpos, b=dr.fatbits?fat_to_card(ev.pos):ev.pos, r=rect(b.x,b.y,b.x-a.x,b.y-a.y)
   const t=ls(dyad.format(lms('(%3i,%3i,%3i,%3i)'),lml([r.x,r.y,r.w,r.h].map(lmn)))),s=font_textsize(FONT_BODY,t)
   draw_text_outlined(rect(ev.pos.x,ev.pos.y-s.y,s.x,s.y),t,FONT_BODY)
  }
  else{
   const c=dr.fatbits?fat_to_card(ev.pos):ev.pos
   const t=ls(dyad.format(lms('(%3i,%3i)'),lml([c.x,c.y].map(lmn)))),s=font_textsize(FONT_BODY,t)
   draw_text_outlined(rect(ev.pos.x,ev.pos.y-s.y,s.x,s.y),t,FONT_BODY)
  }
 }
}
gestures=_=>{
 if(!enable_gestures)return
 const card=ifield(deck,'card')
 if(!in_layer()||uimode!='interact'||(!ev.drag&&!ev.mu))return          // must be in the right state of mind
 if(ev.drag&&ob.sel.length&&lb(ifield(ob.sel[0],'draggable')))return    // must not be dragging a canvas
 if(card.widgets.v.some(x=>dover(unpack_widget(x).size)))return         // must touch grass
 const d=rsub(ev.pos,ev.dpos);if(Math.sqrt(d.x*d.x+d.y*d.y)<50)return   // must be emphatic
 if(Math.abs(d.x)<2*Math.abs(d.y)&&Math.abs(d.y)<2*Math.abs(d.x))return // must be highly directional
 const dir=Math.abs(d.x)>2*Math.abs(d.y)?(d.x<0?'left':'right'):(d.y<0?'up':'down')
 image_paste(rect(ev.pos.x-8,ev.pos.y-8,16,16),frame.clip,GESTURES[dir],frame.image,0)
 if(ev.mu)msg.target_navigate=card,msg.arg_navigate=lms(dir)
}

validate_modules=_=>{
 for(let z=0;z<count(deck.modules);z++){
  const err=ifield(deck.modules.v[z],'error');if(!count(err))continue
  modal_enter('alert'),ms.message=rtext_cast(lms(`The module "${ls(deck.modules.k[z])}" failed to initialize: ${ls(err)}`));break
 }
}
load_deck=d=>{
 deck=d, dirty=0, wid.active=-1, wid.hist=[], au.hist=[], doc_hist=[], doc_hist_cursor=0, dr=draw_state()
 FONT_BODY=dget(deck.fonts,lms('body')),FONT_MENU=dget(deck.fonts,lms('menu')),FONT_MONO=dget(deck.fonts,lms('mono'))
 fb=image_make(getpair(ifield(ifield(deck,'card'),'size'))),context=frame=draw_frame(fb),validate_modules(),setmode('interact'),msg.next_view=1
 seed=0|(new Date().getTime()/1000),n_play([NONE,lms('loop')])
}
tick=_=>{
 msg.pending_drag=0,msg.pending_halt=0,frame=context,uicursor=0,fb.pix.fill(0)
 menu_setup(),all_menus(),widget_setup()
 if(uimode=='script'){script_editor()}else{main_view()}
 modals(),gestures(),menu_finish()
 if(uimode=='draw'&&dr.fatbits)draw_icon(rect(frame.size.x-14,2),ZOOM,1)
 if(uimode=='interact'&&ev.drag&&ob.sel.length&&lb(ifield(ob.sel[0],'draggable')))iwrite(ob.sel[0],lms('pos'),lmpair(rsub(ev.pos,ob.prev))),mark_dirty()
 q('#display').style.cursor=uicursor||'default'
 for(let x=0;x<=1;x++)for(let y=0;y<=1;y++)draw_icon(rect(x*(context.size.x-5),y*(context.size.y-5)),CORNERS[x+y*2],1)
 const used=interpret()
 if(uimode=='interact'&&profiler){
  const r=rect(frame.size.x-60,2,50,12)
  draw_text(inset(r,2),ls(dyad.format(lms('%0.2f%%'),lmn(100*used/FRAME_QUOTA))),FONT_BODY,1)
  draw_invert(deck.patterns.pal.pix,rect(r.x+1,r.y,ceil((r.w-2)*(used/FRAME_QUOTA)),r.h)),draw_box(r,0,1)
 }
 if(msg.pending_loop)sfx_doloop()
}

let id=null
sync=_=>{
 toolbars()
 ev.shortcuts={}
 ev.mu=ev.md=ev.click=ev.dclick=ev.tab=ev.action=ev.dir=ev.exit=ev.eval=ev.scroll=ev.rdown=ev.rup=0
 if(ev.clicktime)ev.clicktime--;if(ev.clicklast)ev.clicklast--
 if(ev.pos.x!=ev.dpos.x||ev.pos.y!=ev.dpos.y)ev.clicklast=0
 wid.cursor_timer=(wid.cursor_timer+1)%(2*FIELD_CURSOR_DUTY)
 if(wid.change_timer){wid.change_timer--;if(wid.change_timer==0)field_change()}
 const anim=deck.patterns.anim, pal=deck.patterns.pal.pix, mask=dr.trans_mask&&uimode=='draw'
 const anim_ants   =(x,y)=>(0|((x+y+(0|(frame_count/2)))/3))%2?15:0
 const anim_pattern=(pix,x,y)=>pix<28||pix>31?pix: anim[pix-28][(0|(frame_count/4))%max(1,anim[pix-28].length)]
 const draw_pattern=(pix,x,y)=>pix<2?(pix?1:0): pix>31?(pix==32?0:1): pal_pat(pal,pix,x,y)&1
 const draw_color  =(pix,x,y)=>pix==ANTS?anim_ants(x,y): pix>47?0: pix>31?pix-32: draw_pattern(pix,x,y)?15:0
 if(!id||id.width!=fb.size.x||id.height!=fb.size.y)id=new ImageData(fb.size.x,fb.size.y)
 for(let z=0,d=0,y=0;y<id.height;y++)for(let x=0;x<id.width;x++,z++,d+=4){
  const pix=fb.pix[z], a=anim_pattern(pix,x,y), c=(a==0&&mask)?13:draw_color(a,x,y), cv=COLORS[c]
  id.data[d  ]=0xFF&(cv>>16)
  id.data[d+1]=0xFF&(cv>> 8)
  id.data[d+2]=0xFF&(cv    )
  id.data[d+3]=0xFF
 }
 const r=q('#render');r.getContext('2d').putImageData(id,0,0)
 const g=q('#display').getContext('2d');g.imageSmoothingEnabled=false,g.save(),g.scale(zoom,zoom),g.drawImage(r,0,0),g.restore()
 pending_tick=1,frame_count++
}

move=(x,y)=>{if(!msg.pending_drag)pointer.prev=pointer.pos;pointer.pos=ev.pos=rect(x,y);if(pointer.held)msg.pending_drag=1}
down=(x,y,alt)=>{
 ev.rawdpos=ev.rawpos
 move(x,y),pointer.held=ev.drag=1;pointer.start=ev.dpos=pointer.pos,ev.md=1,ev.clicktime=10;if(alt)ev.rdown=1;initaudio()}
up=(x,y,alt)=>{
 move(x,y),pointer.held=ev.drag=0,pointer.end=pointer.pos,ev.mu=1;if(alt)ev.rup=1
 if(ev.clicktime)ev.click=1;ev.clicktime=0;if(ev.clicklast)ev.dclick=1;ev.clicklast=DOUBLE_CLICK_DELAY
 if(ev.callback&&ev.callback_rect&&over(ev.callback_rect)&&(ev.callback_drag||dover(ev.callback_rect)))ev.callback()
 ev.callback=null,ev.callback_rect=null,ev.callback_drag=0;if(audio)audio.resume()
}
mouse=(e,f)=>{
 ev.rawpos=rect(e.pageX,e.pageY);const c=q('#display').getBoundingClientRect()
 f(0|((e.pageX-c.x)/zoom),0|((e.pageY-c.y)/zoom),e.button!=0); e.preventDefault()
}
touch=(e,f)=>{const t=e.targetTouches[0]||{}; mouse({pageX:t.clientX, pageY:t.clientY, preventDefault:_=>e.preventDefault,button:0},f)}
loop=_=>{tick(),sync(),pump=setTimeout(loop,1000/60)} // 60fps
resize=_=>{
 const b=q('body'), screen=rect(b.clientWidth,b.clientHeight)
 zoom=max(1,0|min(screen.x/fb.size.x,screen.y/fb.size.y))
 tzoom=0|min((screen.x-(zoom*fb.size.x))/(2*toolsize.x),screen.y/toolsize.y)
 const tz=tzoom*toolbars_enable
 const c =q('#display');c .width=fb.size .x*zoom,c.height =fb.size .y*zoom
 const tl=q('#ltools' );tl.width=toolsize.x*tz  ,tl.height=toolsize.y*tz
 const tr=q('#rtools' );tr.width=toolsize.x*tz  ,tr.height=toolsize.y*tz
 const r =q('#render' );r .width=fb.size .x     ,r .height=fb.size .y
 const rl=q('#lrender');rl.width=toolsize.x     ,rl.height=toolsize.y
 const rr=q('#rrender');rr.width=toolsize.x     ,rr.height=toolsize.y
}
window.onresize=_=>{resize(),sync()}
q('body').addEventListener('mousedown'  ,e=>mouse(e,down))
q('body').addEventListener('mouseup'    ,e=>mouse(e,up  ))
q('body').addEventListener('mousemove'  ,e=>mouse(e,move))
q('body').addEventListener('contextmenu',e=>e.preventDefault())
q('body').addEventListener('touchstart' ,e=>{e.preventDefault(),touch(e,down)},{passive:false})
q('body').addEventListener('touchend'   ,e=>touch({targetTouches:e.changedTouches,preventDefault:e.preventDefault},up))
q('body').addEventListener('touchmove'  ,e=>touch(e,move))
q('body').onwheel=e=>ev.scroll=e.deltaY<0?-1:e.deltaY>0?1:0
q('body').onkeydown=e=>{
 initaudio()
 if(e.shiftKey)ev.shift=1
 if(e.key=='ArrowUp'   )ev.dir='up'
 if(e.key=='ArrowDown' )ev.dir='down'
 if(e.key=='ArrowLeft' )ev.dir='left'
 if(e.key=='ArrowRight')ev.dir='right'
 if(e.metaKey||e.ctrlKey)ev.alt=1,ev.shortcuts[e.shiftKey?(e.key.toUpperCase()):e.key]=1
 else if(e.key.length==1&&wid.infield)field_input(e.key)
 else if(uimode=='draw'&&ms.type==null){if(e.key=='Backspace'||e.key=='Delete')bg_delete_selection()}
 else if(uimode=='object'&&ms.type==null){if(e.key=='Backspace'||e.key=='Delete')ob_destroy()}
 else if(ms.type=='recording'&&!wid.infield&&au.mode=='stopped'){if(e.key=='Backspace'||e.key=='Delete')sound_delete()}
 if     (wid.ingrid )grid_keys (e.key,e.shiftKey)
 else if(wid.infield)field_keys(e.key,e.shiftKey)
 if(uimode=='script')sc.status=''
 if(e.key==' '&&!wid.infield)ev.action=1
 if(e.key=='Enter')ev.action=1
 if(e.key=='Tab')ev.tab=1
 if(e.key=='l'&&ms.type==null&&!wid.ingrid&&!wid.infield)ev.shortcuts['l']=1
 if(e.key=='j'&&ms.type==null&&dr.limbo_dither&&dr.dither_threshold>-2.0)dr.dither_threshold-=.1
 if(e.key=='k'&&ms.type==null&&dr.limbo_dither&&dr.dither_threshold< 2.0)dr.dither_threshold+=.1
 if((e.metaKey||e.ctrlKey)&&e.key in {c:1,x:1,v:1}){}
 else{e.preventDefault()}
}
q('body').onkeyup=e=>{
 if(e.key=='Meta'||e.key=='Control'||e.metaKey||e.ctrlKey)ev.alt=0
 if(e.key=='Enter'&&e.shiftKey)ev.eval=1
 if(e.key=='Shift'||e.shiftKey)ev.shift=0
 if(e.key=='m'&&uimode=='draw'&&in_layer())ev.hidemenu^=1
 if(e.key=='t'&&uimode=='draw'&&in_layer())dr.trans^=1
 if(e.key=='Escape')ev.exit=1
 if(!wid.infield&&uimode=='interact'){
  if(e.key=='ArrowUp'   )msg.target_navigate=ifield(deck,'card'),msg.arg_navigate=lms('up'   )
  if(e.key=='ArrowDown' )msg.target_navigate=ifield(deck,'card'),msg.arg_navigate=lms('down' )
  if(e.key=='ArrowLeft' )msg.target_navigate=ifield(deck,'card'),msg.arg_navigate=lms('left' )
  if(e.key=='ArrowRight')msg.target_navigate=ifield(deck,'card'),msg.arg_navigate=lms('right')
 }
 e.preventDefault()
}
let local_clipboard=''
getclipboard=after=>{
 const t=document.createElement('textarea');t.style.top='0',t.style.left='0',t.position='fixed',t.onpaste=e=>{e.stopPropagation()}
 document.body.appendChild(t),t.focus(),t.select();const success=document.execCommand('paste');const x=t.value;document.body.removeChild(t)
 if(success){after(x);return}                                       // this approach seems to work fine for Safari
 try{navigator.clipboard.readText().then(text=>{after(text)},_=>_)} // this works in Chrome, but *explicitly* does not work in Firefox
 catch(e){after(local_clipboard)}                                   // so Firefox gets to use a baby-jail local clipboard
}
setclipboard=x=>{
 local_clipboard=x
 const t=document.createElement('textarea');t.value=x,t.style.top='0',t.style.left='0',t.position='fixed',t.oncopy=e=>{e.stopPropagation()}
 document.body.appendChild(t),t.focus(),t.select();const success=document.execCommand('copy');document.body.removeChild(t);if(success)return;
 try{navigator.clipboard.writeText(x).then(_=>_,_=>_)}catch(e){console.log(e)} // generally not necessary, but worth having as a last resort
}
docut=_=>{
 if(wid.fv){
  const s=rtext_span(wid.fv.table,wid.cursor),i=rtext_is_image(s);field_keys('Delete',0)
  return i?image_write(i):ls(rtext_string(s))
 }
 else if(ms.type=='recording'&&au.mode=='stopped'){const r=sound_write(sound_selected());sound_delete();return r}
 else if(ms.type==null&&uimode=='draw'&&(bg_has_sel()||bg_has_lasso())){
  const i=bg_has_lasso()?image_mask(dr.limbo,dr.mask): dr.limbo?bg_scaled_limbo():bg_copy_selection(dr.sel_here)
  bg_scoop_selection(),bg_delete_selection();return image_write(i)
 }
 else if(ms.type==null&&uimode=='object'&&ob.sel.length){ob_order();const r=ls(card_copy(ifield(deck,'card'),lml(ob.sel)));ob_destroy();return r}
 return null
}
docopy=_=>{
 if(wid.gv){return ls(n_writecsv([wid.gv.table,grid_format()]))}
 else if(wid.fv){
  const s=rtext_span(wid.fv.table,wid.cursor),i=rtext_is_image(s)
  return i?image_write(i):ls(rtext_string(s))
 }
 else if(ms.type=='recording'&&au.mode=='stopped'){return sound_write(sound_selected())}
 else if(ms.type==null&&uimode=='draw'&&(bg_has_sel()||bg_has_lasso())){
  const i=bg_has_lasso()?image_mask(dr.limbo,dr.mask): dr.limbo?bg_scaled_limbo():bg_copy_selection(dr.sel_here);return image_write(i)
 }
 else if(ms.type==null&&uimode=='object'&&ob.sel.length){ob_order();return ls(card_copy(ifield(deck,'card'),lml(ob.sel)))}
 return null
}
dopaste=x=>{
 if(ms.type==null&&/^%%IMG[012]/.test(x)){
  const i=image_read(x);if(i.size.x==0||i.size.y==0)return
  if(wid.fv){
   if(wid.f.style!='rich'){field_input(x)}
   else{field_edit(lms(''),i,'i',wid.cursor)}
  }else{setmode('draw'),bg_paste(i)}
 }
 else if(ms.type=='recording'&&au.mode=='stopped'&&/^%%SND0/.test(x)){sound_edit(sound_replace(sound_read(x)))}
 else if(ms.type==null&&/^%%WGT0/.test(x)){setmode('object'),card_paste(ifield(deck,'card'),lms(x))}
 else if(ms.type==null&&/^%%CRD0/.test(x)){
  const c=deck_paste(deck,lms(x))
  const card=ifield(deck,'card'), n=ln(ifield(card,'index'));iwrite(c,lms('index'),lmn(n+1)),n_go([c],deck)
 }
 else if(wid.gv&&!wid.g.locked&&ms.type==null){grid_edit(n_readcsv([lms(x),lms(wid.g.format)]))}
 else if(wid.fv){field_input(x)}
}
cutcard=_=>{const c=ifield(deck,'card');setclipboard(ls(deck_copy(deck,c))),deck_remove(deck,c),mark_dirty()}
copycard=_=>{const c=ifield(deck,'card');setclipboard(ls(deck_copy(deck,c)))}
copycanvasimg=_=>{setclipboard(image_write(canvas_image(ob.sel[0],1))),frame=context}
pasteascanvas=_=>getclipboard(t=>{ob_create([lmd([lms('type'),lms('image')],[lms('canvas'),lms(t)])]),frame=context})
pasteintocanvas=_=>getclipboard(t=>{const i=image_read(t),c=ob.sel[0];iwrite(c,lms('size'),ifield(i,'size')),c.image=i})
menucut=_=>{const r=docut();if(r)setclipboard(r)}
menucopy=_=>{const r=docopy();if(r)setclipboard(r)}
menupaste=_=>getclipboard(t=>{if(t.length)dopaste(t)})
document.oncut=e=>{const r=docut();if(r)e.clipboardData.setData('text/plain',r),local_clipboard=r;e.preventDefault()}
document.oncopy=e=>{const r=docopy();if(r)e.clipboardData.setData('text/plain',r),local_clipboard=r;e.preventDefault()}
document.onpaste=e=>{
 const src=e.clipboardData||e.originalEvent.clipboardData
 if(src.items[0]){
  const file=src.items[0].getAsFile()
  if(file&&/^image\//.test(file.type)){load_image(file);return}
  if(file&&/^audio\//.test(file.type)){load_sound(file);return}
 }
 dopaste(src.getData('text/plain')),e.preventDefault()
}
q('body').ondragover=e=>e.preventDefault()
q('body').ondrop=e=>{
 e.preventDefault();const file=e.dataTransfer.files.item(0);if(!file)return
 if(/\.(psv|csv)$/i.test(file.name)&&!has_parent(ifield(deck,'card'))){
  file.text().then(t=>{
   const data=n_readcsv([lms(t),NONE,lms(file.type=='text/csv'?',':'|')])
   setmode('object'),ob_create([lmd([lms('type'),lms('value')],[lms('grid'),monad.cols(data)])])
  })
 }
 if(/\.(html|deck)$/i.test(file.name)){
  file.text().then(t=>{
   modal_enter('resources'),ms.message=deck_read(t),ms.grid=gridtab(res_enumerate(ms.message))
  })
 }
 if(/^image\//.test(file.type)){load_image(file)}
 if(/^audio\//.test(file.type)){load_sound(file)}
}

pushstate(lmenv()),load_deck(deck_read(q('script[language="decker"]').innerText))
const tag=decodeURI(document.URL.split('#')[1]||'');if(tag.length)iwrite(deck,lms('card'),lms(tag))
resize(),(pump&&clearTimeout(pump)),loop()
</script></body>