From 27d238cbe6b7181f61db7edfc19c747ef4c75f6f Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Fri, 30 Oct 2026 19:56:16 +0300 Subject: [PATCH] Ice slab with layered plasma, two-pass refraction Pass 1 renders eight plasma sheets into an atlas with linear depth in alpha; pass 2 marches the refracted ray to each sheet and absorbs it by real path length, so near and far layers separate under pointer parallax. Plasma cycle repeats exactly every 30 s; - / = change speed. --- .gitignore | 2 + 1_plasma.frag | 18 +++++++ 2_tunnel.frag | 17 +++++++ 3_orb.frag | 38 ++++++++++++++ 4_ice_scene.frag | 56 +++++++++++++++++++++ 5_ice_refract.frag | 107 +++++++++++++++++++++++++++++++++++++++ 6_ice_plasma.frag | 75 +++++++++++++++++++++++++++ 7_ice_over.frag | 94 ++++++++++++++++++++++++++++++++++ build.py | 49 ++++++++++++++++++ build_ice.py | 116 ++++++++++++++++++++++++++++++++++++++++++ build_plasma.py | 123 +++++++++++++++++++++++++++++++++++++++++++++ ice.html | 92 +++++++++++++++++++++++++++++++++ ice_plasma.gif | Bin 0 -> 1390733 bytes ice_plasma.html | 112 +++++++++++++++++++++++++++++++++++++++++ playground.html | 36 +++++++++++++ preview.png | Bin 0 -> 310811 bytes preview_ice.png | Bin 0 -> 426019 bytes render.py | 47 +++++++++++++++++ shot.py | 11 ++++ test_ice.py | 46 +++++++++++++++++ 20 files changed, 1039 insertions(+) create mode 100644 .gitignore create mode 100644 1_plasma.frag create mode 100644 2_tunnel.frag create mode 100644 3_orb.frag create mode 100644 4_ice_scene.frag create mode 100644 5_ice_refract.frag create mode 100644 6_ice_plasma.frag create mode 100644 7_ice_over.frag create mode 100644 build.py create mode 100644 build_ice.py create mode 100644 build_plasma.py create mode 100644 ice.html create mode 100644 ice_plasma.gif create mode 100644 ice_plasma.html create mode 100644 playground.html create mode 100644 preview.png create mode 100644 preview_ice.png create mode 100755 render.py create mode 100755 shot.py create mode 100644 test_ice.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/1_plasma.frag b/1_plasma.frag new file mode 100644 index 0000000..75f18f7 --- /dev/null +++ b/1_plasma.frag @@ -0,0 +1,18 @@ +precision highp float; +uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; +float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);} +float noise(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.0-2.0*f); + return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);} +float fbm(vec2 p){float a=0.5,s=0.0;for(int i=0;i<5;i++){s+=a*noise(p);p*=2.03;a*=0.5;}return s;} +void main(){ + vec2 p=(gl_FragCoord.xy-0.5*u_res)/u_res.y; + float t=u_time*0.15; + vec2 q=vec2(fbm(p*2.0+vec2(0.0,t)),fbm(p*2.0+vec2(5.2,-t))); + vec2 r=vec2(fbm(p*2.0+q*1.8+vec2(1.7,9.2)+t*1.3),fbm(p*2.0+q*1.8+vec2(8.3,2.8)-t*1.1)); + float v=fbm(p*2.0+r*2.2); + vec3 col=mix(vec3(0.02,0.03,0.10),vec3(0.85,0.25,0.35),smoothstep(0.2,0.9,v)); + col=mix(col,vec3(0.15,0.65,0.95),smoothstep(0.5,1.0,length(r-0.5))); + col+=0.35*pow(max(0.0,1.0-length(p*0.9)),3.0)*vec3(1.0,0.85,0.6); + col*=1.0-0.5*length(p)*0.4; + gl_FragColor=vec4(col,1.0); +} diff --git a/2_tunnel.frag b/2_tunnel.frag new file mode 100644 index 0000000..2dda318 --- /dev/null +++ b/2_tunnel.frag @@ -0,0 +1,17 @@ +precision highp float; +uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; +void main(){ + vec2 p=(gl_FragCoord.xy-0.5*u_res)/u_res.y; + float t=u_time*0.6; + float a=atan(p.y,p.x); + float r=length(p)+0.001; + float z=1.0/r+t; + float rings=fract(z*2.0+a*0.6+sin(a*3.0+t)*0.3); + vec3 base=mix(vec3(1.0,0.15,0.55),vec3(0.15,0.9,1.0),smoothstep(0.0,1.0,rings)); + float glow=pow(1.0-r,2.0); + float band=smoothstep(0.45,0.5,rings)*smoothstep(0.6,0.5,rings); + vec3 col=base*band*glow*2.2; + col+=vec3(0.9,0.5,1.0)*pow(glow,8.0)*1.5; + col+=0.02*vec3(0.3,0.1,0.5)/(r*r); + gl_FragColor=vec4(col,1.0); +} diff --git a/3_orb.frag b/3_orb.frag new file mode 100644 index 0000000..5215d2a --- /dev/null +++ b/3_orb.frag @@ -0,0 +1,38 @@ +precision highp float; +uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; +float sdSphere(vec3 p,float r){return length(p)-r;} +float map(vec3 p){ + float d=sdSphere(p,1.0); + for(int i=0;i<3;i++){ + float fi=float(i); + vec3 q=p; float a=u_time*0.5+fi*2.1; + q.xz=mat2(cos(a),-sin(a),sin(a),cos(a))*q.xz; + d=min(d,sdSphere(q-vec3(0.75,0.0,0.0),0.34)); + } + return d; +} +void main(){ + vec2 uv=(gl_FragCoord.xy-0.5*u_res)/u_res.y; + vec3 ro=vec3(0.0,0.0,3.6), rd=normalize(vec3(uv,-1.6)); + float tt=0.0; int hit=0; + for(int i=0;i<80;i++){ + float d=map(ro+rd*tt); + if(d<0.001){hit=1;break;} + tt+=d; + if(tt>8.0)break; + } + vec3 col=vec3(0.03,0.04,0.07)+0.15*pow(1.0-abs(uv.y),3.0); + if(hit==1){ + vec3 pos=ro+rd*tt; + vec3 n=normalize(vec3(map(pos+vec3(0.001,0,0))-map(pos-vec3(0.001,0,0)), + map(pos+vec3(0,0.001,0))-map(pos-vec3(0,0.001,0)), + map(pos+vec3(0,0,0.001))-map(pos-vec3(0,0,0.001)))); + vec3 l=normalize(vec3(0.6,0.8,0.5)); + float dif=max(dot(n,l),0.0); + float fre=pow(1.0-max(dot(n,-rd),0.0),3.0); + vec3 alb=0.5+0.5*cos(6.2831*(pos*0.6+vec3(0.0,0.33,0.67))); + col=alb*(0.15+0.85*dif)+vec3(0.6,0.8,1.0)*fre*1.2; + col*=0.9+0.1*sin(pos.y*40.0+u_time*4.0); + } + gl_FragColor=vec4(pow(col,vec3(0.4545)),1.0); +} diff --git a/4_ice_scene.frag b/4_ice_scene.frag new file mode 100644 index 0000000..27efc81 --- /dev/null +++ b/4_ice_scene.frag @@ -0,0 +1,56 @@ +// Pass A: icy scene. RGB = colour, A = linear depth / 8.0 (our z-buffer) +precision highp float; +uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; + +float sdSphere(vec3 p, float r){ return length(p) - r; } + +float map(vec3 p){ + float d = sdSphere(p, 1.0); + for(int i=0;i<3;i++){ + float fi = float(i); + vec3 q = p; + float a = u_time*0.5 + fi*2.1; + q.xz = mat2(cos(a),-sin(a),sin(a),cos(a)) * q.xz; + d = min(d, sdSphere(q - vec3(0.75,0.0,0.0), 0.34)); + } + return d; +} + +void main(){ + vec2 uv = (gl_FragCoord.xy - 0.5*u_res)/u_res.y; + vec3 ro = vec3(0.0,0.0,8.5); + vec3 rd = normalize(vec3(uv, -1.6)); + + float t = 0.0; int hit = 0; + for(int i=0;i<90;i++){ + float d = map(ro + rd*t); + if(d < 0.001){ hit = 1; break; } + t += d; + if(t > 12.0) break; + } + + vec3 col = vec3(0.008,0.02,0.045); + col += 0.07*pow(1.0 - abs(uv.y)*0.9, 4.0)*vec3(0.15,0.35,0.6); // cold haze + float depth = 12.0; + + if(hit == 1){ + depth = t; + vec3 pos = ro + rd*t; + vec3 n = normalize(vec3(map(pos+vec3(0.001,0,0))-map(pos-vec3(0.001,0,0)), + map(pos+vec3(0,0.001,0))-map(pos-vec3(0,0.001,0)), + map(pos+vec3(0,0,0.001))-map(pos-vec3(0,0,0.001)))); + vec3 l = normalize(vec3(0.45,0.85,0.55)); + float dif = max(dot(n,l),0.0); + float fre = pow(1.0 - max(dot(n,-rd),0.0), 3.0); + + // frozen layers instead of rainbow + float f = 0.5 + 0.5*sin(pos.y*20.0 + u_time*2.0 + pos.x*5.0 + n.z*3.0); + vec3 alb = mix(vec3(0.90,0.97,1.00), vec3(0.34,0.66,0.92), f); + col = alb*(0.06 + 1.05*dif) + vec3(0.60,0.90,1.0)*fre*1.6; + col *= 0.86 + 0.14*sin(pos.y*38.0 + u_time*4.0); + } + + // depth fog: far things fade into the cold void + col = mix(col, vec3(0.03,0.10,0.19), smoothstep(3.0, 12.0, depth)*0.85); + gl_FragColor = vec4(col, clamp(depth/12.0, 0.0, 1.0)); +} diff --git a/5_ice_refract.frag b/5_ice_refract.frag new file mode 100644 index 0000000..4bac8d6 --- /dev/null +++ b/5_ice_refract.frag @@ -0,0 +1,107 @@ +// Pass B: the ice wall. Refracts pass A through a displaced surface using its depth buffer. +precision highp float; +uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; +uniform sampler2D u_scene; + +const float ICE_Z = 1.20; // front face of the ice +const vec3 SIG_A = vec3(0.13,0.042,0.020); // absorption in ice (red dies first -> blue) +const vec3 SIG_B = vec3(0.16,0.055,0.026); // absorption in the "depth fog" behind + +float hash(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453); } +float noise(vec2 p){ + vec2 i=floor(p), f=fract(p); f=f*f*(3.0-2.0*f); + return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x), f.y); +} +float fbm(vec2 p){ + float a=0.5, s=0.0; + for(int i=0;i<5;i++){ s+=a*noise(p); p*=2.02; a*=0.5; } + return s; +} + +// height of the ice surface at (x,y) — almost frozen, drifts very slowly +float surf(vec2 p){ + float t = u_time*0.04; + return 0.055*fbm(p*1.7 + vec2(t,-t)) + 0.022*fbm(p*4.3 - vec2(t*1.7,t)); +} + +vec3 surfaceNormal(vec2 p){ + float e = 0.004; + float hx = surf(p+vec2(e,0.0)) - surf(p-vec2(e,0.0)); + float hy = surf(p+vec2(0.0,e)) - surf(p-vec2(0.0,e)); + return normalize(vec3(-hx/(2.0*e), -hy/(2.0*e), 1.0)); +} + +// project a world point with the scene camera (ro=(0,0,3.6), fov via 1.6) +vec2 project(vec3 p){ + vec3 d = p - vec3(0.0,0.0,8.5); + return d.xy/(-d.z)*1.6; +} + +void main(){ + vec2 uv = (gl_FragCoord.xy - 0.5*u_res)/u_res.y; + vec3 ro = vec3(0.0,0.0,3.6); + vec3 rd = normalize(vec3(uv, -1.6)); + + // --- front surface: plane z=ICE_Z displaced by surf(), two fixed-point steps + float t = (ICE_Z - ro.z)/rd.z; + for(int i=0;i<2;i++){ + vec3 p0 = ro + rd*t; + t = (ICE_Z + surf(p0.xy) - ro.z)/rd.z; + } + vec3 ps = ro + rd*t; + vec3 n = surfaceNormal(ps.xy); + if(dot(n,-rd) < 0.0) n = -n; + + float cosi = clamp(dot(n,-rd), 0.0, 1.0); + float fres = 0.025 + 0.975*pow(1.0 - cosi, 5.0); + vec3 sky = mix(vec3(0.015,0.05,0.10), vec3(0.42,0.68,0.98), smoothstep(-0.35,0.95,n.y)); + sky += vec3(0.9,0.97,1.0)*pow(cosi, 60.0); // specular glint + + // --- refraction with chromatic dispersion down to the back face + float backZ = ICE_Z - (0.55 + 0.18*fbm(ps.xy*0.9)); + vec3 sceneCol = vec3(0.0); + float pathIce = 0.0; + for(int c=0;c<3;c++){ + float ior = 1.31 + float(c)*0.022; // 1.31 .. 1.354 + vec3 d2 = refract(rd, n, 1.0/ior); + if(length(d2) < 0.5) d2 = rd; // total internal reflection guard + float t2 = (backZ - ps.z)/d2.z; + vec3 pe = ps + d2*t2; + pathIce = t2; + vec2 suv = project(pe); + vec2 texUV = (suv*u_res.y + 0.5*u_res)/u_res; + vec4 s = texture2D(u_scene, clamp(texUV, vec2(0.001), vec2(0.999))); + sceneCol[c] = s[c]; + } + + // --- z-buffer: how much ice/fog the view ray had to cross to the object + vec2 baseUV = gl_FragCoord.xy/u_res; + float dScene = texture2D(u_scene, baseUV).a * 12.0; // linear depth from pass A + float cell = max(0.0, dScene - (t + pathIce)); // ice-to-object gap + vec3 absorbA = exp(-SIG_A*pathIce); // inside the slab + vec3 absorbB = exp(-SIG_B*cell); // depth haze behind it + + vec3 iceTint = vec3(0.80,0.94,1.0); // pure, clean blue ice + vec3 col = sceneCol * 1.10 * absorbA * absorbB * iceTint; + + // --- body detail: small trapped bubbles, hair cracks, frozen sheets (static, they live in the slab) + vec2 q = ps.xy*2.2; + float zone = smoothstep(0.42, 0.62, fbm(q*0.7 + 21.0)); // clear ice vs bubbly ice + float bub = noise(q*19.0 + 3.1) * noise(q*17.0 - 1.7); + float dots = smoothstep(0.045, 0.075, bub) * mix(0.10, 1.0, zone) * 0.34; + float rid = abs(fbm(q*1.9 + 11.0)*2.0 - 1.0); + float crack = smoothstep(0.022, 0.0, rid) * 0.45; + float sheet = smoothstep(0.62, 0.78, fbm(vec2(q.y*1.3, q.x*0.35)) ); + + float streak = smoothstep(0.55,0.95, fbm(vec2(q.x*7.0, q.y*0.35) + 5.0)); + float sheen = pow(max(0.0, dot(n, normalize(vec3(0.35,0.75,0.55)))), 40.0); + col += iceTint * (dots + crack*0.22 + streak*0.05 + sheen*0.5) * (0.25 + 0.75*cosi); + col += iceTint * sheet * 0.06 * smoothstep(0.0, 1.2, cell); + + // --- reflection on top, thicker rim where the slab turns away + col = mix(col, sky, fres*0.85); + col += iceTint * pow(1.0 - cosi, 2.0) * 0.10; // frosted grazing edge + + col = pow(clamp(col, 0.0, 1.0), vec3(0.4545)); + gl_FragColor = vec4(col, 1.0); +} diff --git a/6_ice_plasma.frag b/6_ice_plasma.frag new file mode 100644 index 0000000..49f7730 --- /dev/null +++ b/6_ice_plasma.frag @@ -0,0 +1,75 @@ +// Pass 1: eight plasma sheets, one atlas tile each. Alpha stores linear depth. +// Density field lit as a height surface: billowing volumes with a real terminator. +precision highp float; +uniform vec2 u_res; // size of one tile +uniform float u_time; + +float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } +float noise(vec2 p) { + vec2 i = floor(p), f = fract(p); + f = f*f*(3.0-2.0*f); + return mix(mix(hash(i), hash(i+vec2(1.0,0.0)), f.x), + mix(hash(i+vec2(0.0,1.0)), hash(i+vec2(1.0,1.0)), f.x), f.y); +} +float fbm(vec2 p) { + float sum = 0.0, amp = 0.5; + for (int i = 0; i < 4; i++) { + sum += amp * noise(p); + p = mat2(1.62, 1.17, -1.17, 1.62) * p; + amp *= 0.5; + } + return sum; +} +float fbm2(vec2 p) { + return 0.5 * noise(p) + 0.25 * noise(mat2(1.62, 1.17, -1.17, 1.62) * p); +} +const float EXTENT = 3.8; // half-width of a sheet in world units; must match the ice pass + +void main() { + vec2 tile = floor(gl_FragCoord.xy / u_res); + float layer = tile.x + tile.y * 4.0; + vec2 uv = fract(gl_FragCoord.xy / u_res); + vec2 p = (uv - 0.5) * 2.0 * vec2(EXTENT * u_res.x / u_res.y, EXTENT); + p *= 0.85 + 0.10 * layer; // far sheets carry finer detail, which reads as distance + float angle = hash(vec2(layer, 3.1)) * 6.2831853; + p = mat2(cos(angle), sin(angle), -sin(angle), cos(angle)) * p; + + // Periodic time: every field returns exactly after 30 seconds. + float phase = u_time * 6.2831853 / 30.0; + vec2 o = vec2(cos(phase), sin(phase)); + vec2 seed = vec2(layer * 17.3, layer * 9.1); + + // Two nested warp stages, the marbled-plasma motion. + vec2 q = vec2(fbm(p * 0.50 + seed + o * 0.55), + fbm(p * 0.50 + seed + 23.1 - o * 0.55)); + vec2 r = vec2(fbm(p * 0.85 + 2.0 * q + seed + vec2(o.y, -o.x) * 0.40), + fbm(p * 0.85 + 2.0 * q + seed + 31.7 - vec2(o.y, -o.x) * 0.40)); + vec2 w = p * 1.10 + 2.6 * r + seed; // warped coords of the density field + + // The silhouette is shared by all sheets (slid a little per depth), so the gaps + // stay open right through the stack instead of filling in with overlap. + float mass = smoothstep(0.520, 0.548, fbm((p + o * 0.30 + vec2(0.10, -0.07) * layer) * 0.62 + r * 1.1 + 13.7)); + + // Light the density as a height surface: $cheap 2-octave gradient, strong terminator. + float h = 0.05; + vec2 slope = vec2(fbm2(w + vec2(h, 0.0)) - fbm2(w - vec2(h, 0.0)), + fbm2(w + vec2(0.0, h)) - fbm2(w - vec2(0.0, h))) * (10.0 / (2.0 * h)); + vec3 nrm = normalize(vec3(-slope, 1.0)); + float lit = clamp(dot(nrm, normalize(vec3(-0.38, 0.52, 0.76))), 0.0, 1.0); + + // Folded planes across the volume, feathered so they never read as a grid. + vec2 g = (p + r * 2.0) * 1.4; + vec2 cell = floor(g), f = fract(g); + float feather = smoothstep(0.0, 0.10, min(min(f.x, 1.0 - f.x), min(f.y, 1.0 - f.y))); + float facet = mix(1.0, mix(0.70, 1.25, hash(cell + seed + step(1.0, f.x + f.y) * 37.0)), feather); + + float shade = 0.60 + 0.55 * lit; // soft light across the folded planes + float e = mass * facet * shade * 0.075 + + mass * (1.0 - mass) * 4.0 * 0.015 // thin torn silhouette + + mass * pow(lit, 14.0) * 0.045; // glint where a fold faces the light + vec3 col = e * mix(vec3(0.02, 0.42, 0.60), vec3(0.08, 0.64, 0.88), facet * 0.5); + // Faint dye in the open gaps so darkness reads as depth, not as a hole. + col += smoothstep(0.40, 0.85, fbm(p * 0.30 + seed * 0.4 + o * 0.3)) * vec3(0.0001, 0.0004, 0.0006); + + gl_FragColor = vec4(col, (0.42 + layer * 0.26) / 2.6); +} diff --git a/7_ice_over.frag b/7_ice_over.frag new file mode 100644 index 0000000..615fc44 --- /dev/null +++ b/7_ice_over.frag @@ -0,0 +1,94 @@ +// Pass 2: clean ice. Refraction, chromatic shear, absorption by true path length. +// Nothing is added to empty space, so the gaps between sheets stay black. +precision highp float; +uniform vec2 u_res; +uniform float u_time; +uniform vec2 u_mouse; // normalized, bottom-left origin +uniform sampler2D u_scene; +uniform vec2 u_tile_res; + +const float EXTENT = 3.8; // must match the plasma pass +const float FRONT = -2.0; // ice surface, camera at the origin looking down -z +const vec3 SIGMA = vec3(0.66, 0.26, 0.11); // absorption per unit: red dies first, so depth turns blue +const float IOR = 1.31; + +float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } +float noise(vec2 p) { + vec2 i = floor(p), f = fract(p); + f = f*f*(3.0-2.0*f); + return mix(mix(hash(i), hash(i+vec2(1.0,0.0)), f.x), + mix(hash(i+vec2(0.0,1.0)), hash(i+vec2(1.0,1.0)), f.x), f.y); +} +float fbm(vec2 p) { + float sum = 0.0, amp = 0.5; + for (int i = 0; i < 3; i++) { sum += amp * noise(p); p = mat2(1.62, 1.17, -1.17, 1.62) * p; amp *= 0.5; } + return sum; +} +vec2 atlasUV(vec2 world, float layer) { + vec2 uv = world / (2.0 * EXTENT) + 0.5; + uv = clamp(uv, 0.5 / u_tile_res, 1.0 - 0.5 / u_tile_res); // keep bilinear taps inside the tile + return (vec2(mod(layer, 4.0), floor(layer / 4.0)) + uv) / vec2(4.0, 2.0); +} +// Smooth frozen surface: two octaves, no bubbles, no grain. +float height(vec2 x) { + return 0.22 * fbm(x * 0.85 + 2.0) + 0.06 * fbm(x * 2.6 - 5.0); +} +void main() { + vec2 screen = gl_FragCoord.xy / u_res; + vec2 p = (gl_FragCoord.xy - 0.5 * u_res) / u_res.y; + vec2 look = clamp(u_mouse - 0.5, -0.35, 0.35); // orbiting the slab centre + vec3 ray = normalize(vec3(p - look * 2.2, -1.6)); + + // Front surface along the view ray; two fixed-point steps settle on the swell. + float t = FRONT / ray.z; + for (int i = 0; i < 2; i++) t = (FRONT + height(ray.xy * t)) / ray.z; + vec3 hit = ray * t; + + float e = 0.02; + vec2 slope = vec2(height(hit.xy + vec2(e, 0.0)) - height(hit.xy - vec2(e, 0.0)), + height(hit.xy + vec2(0.0, e)) - height(hit.xy - vec2(0.0, e))) / (2.0 * e); + float bevel = smoothstep(0.14, 0.0, min(min(screen.x, 1.0 - screen.x), min(screen.y, 1.0 - screen.y))); + slope += bevel * (screen - 0.5) * 0.55; // the slab turns away at the frame edge + vec3 normal = normalize(vec3(-slope, 1.0)); + float facing = max(dot(-ray, normal), 0.0); + + // Refracted ray; red and blue bend a little differently, which is what makes ice ring. + vec3 inside = refract(ray, normal, 1.0 / IOR); + vec2 shearR = (refract(ray, normal, 1.0 / (IOR - 0.012)) - inside).xy; + vec2 shearB = (refract(ray, normal, 1.0 / (IOR + 0.016)) - inside).xy; + inside = normalize(inside); + if (inside.z > -0.05) inside = vec3(0.0, 0.0, -1.0); + + // Recentre the parallax on the middle of the sheet stack: near and far sheets + // then slide past each other at different rates as the pointer moves. + vec2 par = look * 4.54; + + vec3 acc = vec3(0.0); + float trans = 1.0; + for (int i = 0; i < 8; i++) { + float layer = float(i); + float depth = texture2D(u_scene, atlasUV(hit.xy + par, layer)).a * 2.6; // depth buffer from pass 1 + float path = (hit.z - (FRONT - depth)) / -inside.z; // ice crossed to reach this sheet + vec2 at = (hit + inside * path).xy + par; + vec3 plasma; + plasma.r = texture2D(u_scene, atlasUV(at + shearR * path, layer)).r; + plasma.g = texture2D(u_scene, atlasUV(at, layer)).g; + plasma.b = texture2D(u_scene, atlasUV(at + shearB * path, layer)).b; + vec3 c = plasma * exp(-SIGMA * path); + // Near plumes are nearly opaque, so the sheets behind them show through the gaps + // instead of averaging into a wash. That is what makes the stack read as depth. + float alpha = clamp(max(max(c.r, c.g), c.b) * 1.8, 0.0, 0.80); + acc += trans * c; + trans *= 1.0 - alpha; + } + + // Gentle shoulder: overlapping glow stays coloured instead of clipping to white. + vec3 col = vec3(1.0) - exp(-acc * 1.5); + + // Glass: reflection and a rim only where the surface turns away. Empty space stays dark. + float fresnel = pow(1.0 - facing, 5.0); + vec3 sky = mix(vec3(0.004, 0.016, 0.030), vec3(0.075, 0.220, 0.320), normal.y * 0.5 + 0.5); + col += sky * (fresnel * 1.2 + bevel * 0.03); + + gl_FragColor = vec4(pow(clamp(col, 0.0, 1.0), vec3(0.4545)), 1.0); +} diff --git a/build.py b/build.py new file mode 100644 index 0000000..9a9e5be --- /dev/null +++ b/build.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Inline every *.frag next to this file into playground.html (keys 1..N switch, mouse = u_mouse).""" +import glob, json, os + +here = os.path.dirname(os.path.abspath(__file__)) +frags = sorted(glob.glob(os.path.join(here, "*.frag"))) +names = [os.path.basename(f)[:-5] for f in frags] +srcs = [open(f).read() for f in frags] + +html = """shaders + +
+""" +out = html.replace("__SHADERS__", json.dumps(srcs)).replace("__NAMES__", json.dumps(names)) +open(os.path.join(here, "playground.html"), "w").write(out) +print("playground.html:", ", ".join(names)) diff --git a/build_ice.py b/build_ice.py new file mode 100644 index 0000000..371aab8 --- /dev/null +++ b/build_ice.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Two-pass ice pipeline: scene -> texture(+depth in alpha) -> refraction -> screen. +Writes ice.html. Open ice.html#shot&t=2 for a single still frame.""" +import json, os + +here = os.path.dirname(os.path.abspath(__file__)) +import sys +a = sys.argv[1:] +scene_f, ice_f, out_f = (a + ["4_ice_scene.frag", "5_ice_refract.frag", "ice.html"])[:3] +scene = open(os.path.join(here, scene_f)).read() +ice = open(os.path.join(here, ice_f)).read() + +HTML = """ice + +
+""" + +out = (HTML.replace("__SCENE_B64__", __import__("base64").b64encode(scene.encode()).decode()) + .replace("__ICE_B64__", __import__("base64").b64encode(ice.encode()).decode())) +open(os.path.join(here, out_f), "w").write(out) +print(out_f, "written") diff --git a/build_plasma.py b/build_plasma.py new file mode 100644 index 0000000..5adbea0 --- /dev/null +++ b/build_plasma.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Embed the two GLSL passes into the standalone ice_plasma.html page.""" +import json +from pathlib import Path + +here = Path(__file__).resolve().parent +scene = (here / '6_ice_plasma.frag').read_text() +ice = (here / '7_ice_over.frag').read_text() +html = r''' + + + + +Сквозь лёд + + + + +
Сквозь лёд
Двигайте мышь · скорость − / =
+ + + +''' +(here / 'ice_plasma.html').write_text(html.replace('__SCENE__', json.dumps(scene)).replace('__ICE__', json.dumps(ice))) +print('ice_plasma.html written') diff --git a/ice.html b/ice.html new file mode 100644 index 0000000..698a656 --- /dev/null +++ b/ice.html @@ -0,0 +1,92 @@ +ice + +
diff --git a/ice_plasma.gif b/ice_plasma.gif new file mode 100644 index 0000000000000000000000000000000000000000..f0afb70d318782045f93e627e2e8bb0aa62335c9 GIT binary patch literal 1390733 zcmeF1)mIdb7wxBjp@;5nL8PUfp_`!_hHe-_O3<01Q(8c}Ly?wthLG+q1q7s2()P>! z-M`|#+_lcbc|7Z^ea^%FY;9dFc?D-U5Dxrt2M|y-5kgpqs9TC^*^25oi0L_r8@fEu zv6Vo&OX}H6nR!VYI7wUh${M@KS_R12pdXsLKeP*$xA2sA2vu+jSG4p|wDDK63siE6 zRCWkbc738^?W5ups^b1s)f4l`&hL?P*dv!nxHk@AAAoQQLbyjEeB;$TW7JV0>i&ru z-p@1wlQmr)Yx-ifJfpRO(zM;ewf*C?Lo;*&6Li9|bp0^8k-2(7N&21<`XQbADkh$m+jK((N-m5nP+)=k;ndU>PM4XC<(fXhg#%>(Df zgv-jB6dGPl41h-XZGZzWJn496>TRG?j}m4Xun$Tk<5r?&Y9Mq=X4FZ0lNBo~v>i?r z)R+n&Tt=X=Y;ro80HE{mQjI{|(O&|$cLI&EXmmXc5t~A$Xj@#L^>%cdFP1!fBU>Qo zg}lB;M5EcEQ7JV>3#NyyH=z{Dz32W{rqR5~yxsP0N3Yb^`i-M^_UlUb`>QF<1hfxvf91NkUJg zZj)#pCZ3DLk!#6#+eDZpTXB*`7BX47xiajSea*acxs`Z|LN& z{GOPvJ?gX?=M5gH*d3+<`-H`Vz<9uedmEKzS)XluD-3%p5d4C8n0JNADYCv?-0fvb z9dTlowoKRQLX#ZhizQu&EWfjmfyJockak7n5K+JC8ePk>g%v&VsA9u)j!A20HTn(pQ@$b3-(8Da9PAb$`NxfOfYul*Ji|G<2Gl(xnq%Oh-ZD{W)nrBKQ7BmcGSN4XXBj5cUQaUN z4-u9xsFurx2`RP$jr#W!gjaZmX0B3b$1Bk={AgB^SlVAc*CXMF(;PG>X}PRK+>n#%&@UHI-1fk=VV+$P;eWwv(#-{&X*3_+P zRPBk)1@8wB4blX6`0vI;ybXQk=njQbz4;J{pL31g1E;)Q;5|MM?Q)FXJd~dFQcISrB=+7cIji z(pqgd@4P8@$S0DQs)jg~aiIR+)k6iTs}Yc|n5&LRBGxUcIb=%NTiWClOBDgI*%TR? zt5kbUR{FUWRgs25p9+@bUr>0hQH#4m)Fkk)_&26}}8hvH&5N;Zq z6?P-EvH&1b+%=Gwl29_xQ_De-fyd$B`&DzqdUg-ughL6%1uDZV7$3&_>uZDOt${Hc z!A=r8Kic&*Kd78^4!1K@_&235^|I^6tzC_ZJJwKG6Y3`8I5f~_P3|DMdF01^(+WR< zuL-KfQ}(7Bh}?^>;EtN(W5L6p zC+@j`NDtYZW*Uj=EiZ5km9JjkD?6O@bJq9Fn%w0|X@aH%;a*zyp|tg0muj5(#63@H zAyat%uj6Vx%;8arc32BJ6*I&l}Oq@O$8LmNW^Qht=UwWTeZZQ= zdo`Y7Q&B5YcOe~7SRX%kwms7Ja@n4kYZ5g6t>O!Y{GjIC_}H`6{r#)Cb*dY506m^$ zmti5ftJxv)N)*OV*SjET8Wwc!FdW5ADul$E83FQYl;f9HDtFvoj|pwLi&9+O2eTDK zCrDNA?UFoDkEU(tvuOXl2m7X$>z%F=R_i(_XV>EeAD<36158^}*gMS{Dt z`wo@gF3PK7m_^3zSFb9%&<@JtLzrS2EO)CGjp(gX3pTez*AB+;TSvc zBKnbOneqKKrew9^I+hX_>tJ*&-`_L8pNhQCdeMpC+X+w4+uW*_@vT-mKLxnicHrl6 zCpv0_iQxxUsdWP`(tXnY0f5OR*3VY^s9~eH$hxcI3QGTS5SIrA!vEmc#LIQQs;vG8 zc8{w#f%99^R09&v4v)2+nEG)3Z=J0&Luq7Gh-EQR%g*7(J`UH&U>=x@@RQE1bToyd zaV(p}%Y>Fbo^!DAW74vt3z6!76R%6#7%E{zf)W*E22e-Z2#okwHY^GIN87ufk%>+p zf^Jncl3L(+lAm&k2^X(Jzk{f#KU1~`USg%kNq+YO$#)m|Y0;#Si^b*4+gYK0yp5%=fgFwQ9m` zA3u%De(KmMTudWUCJB-UJe&3KByV}BuMts_EiwKbXvNQ41&jpfL_*dRh>jB+>7|tS zF(1B%(EPOL&cP?E^l|BxWUSxK%Cs51g|6`EAV|J&fdjPjU`l z*c(7HG)62lPVAg$^P)4FYR#wlyPM|)+El{b86LCzB1UZ&Vu^=nLzOqz;*7_g{4P;( zi{Vc3U@ItO{QFb%_jsp*2&(qrA0(bVDqvk3^!F8ygq<-C=CnE5 zR)Pqx*<1hcpLFVL_A{M{rJr1m!}#ZJuyVV=xQL|0<4}GtM>8Rw>`~l5x#V+aotgx+ zA0}BQA{sj8cGN-SL-0`=kFA2n`FQFyG81XTh~BKH=oZ9j#G7m7IoalWv{}ehx%mzL zOl47vV3SKbzmB)-20D$aPSiguF$F&VoCai0FZ84pbxvQsOQMwzPTxGH-HI@{14o4) z)A6m?M_N1V6LUaw`M-LA9-u(TlMF$0k|6hUey9??+w{+3p>sYqYe+diVuU@=$J{kC-BFq*T*q5#B8o9JuoNwt7*(*C2ZgM^lS{IflMe5 zO1ssL80rSvb_YLkRTVExW4Qt{B7whi3I(x+iZ62HymQ(Xa}?JiL*;YfCb^M60Z)E< z!3y9BM0v)fc_tz+k%b_m&-CHBiqn;Onatej%SL}9bp9pT0F#po^K6v!bX2>GA0>r( z2N6+)6D!HSFu8y}uhqvHi$NKmbbz|a*b_$Ut;391otG50FL{54_M9+e zE+kP8F+2S7C0qx3>lS-TM5865Yr~9uSW@Ol(6c@HN>H?`#EUwwlI1$0RVyM!8(%9v zW5-=J6XEzCc|BX};H>?p`0nhK@HElH0%&Z(Q4Y^_VVQhkSydsQQgT^{u=I>OaGD6K zN&{qiU zVTEX5a{hH*{_%3YDl`tA9A-67WQO;tyAzICPK*&YH^&PakA_rrL!E2sElUV~yc4A* zm8GsftF#mw$31}0AC_g(r8g8c8by_r{c2P;wJQG%5IwSgs>2LytikPp;OGpVT@X7O zt5sOz`5RGv6WNG5(lNKY6QnliznQ!X3m$5IO+d%5z!Kz3a^{VYxu1Mx9Y>5755mhiraO8AtEegpe5Z`YdY?#?2_Bp@w2_ z!fVy3c6e%Yerk$GTl16T`xl!p3`esJ{}hB*>6N6Eo{4ml?q-p=SMSF9fqZMO3tRqP zS3BDEWiBWQj`cRlW=V}~=t=WG1Py%(`XNnz#BphCcd2 zJTQ>)qAM=zxqVKY`Z%QVPCm|m!O3Q|J6Xf0p)0>ixS6D9Fu$)pIi*>Nfy)6M^Tb0#uX8qrQfZ|Mn+>3d4SUDTVhAmBgG8mjF_oxWa{K7C43T_ZpRx<8`- zod5_^O-n&QI*EQ}w(`BCsErk%%tg`Tvs@#V0Scd_M9l#uy1A#}5flEz_JtG>)JPrVVXqLtA-^YMfoE2vZm#jS*f`;rQGYHm%PX%u820nJ5O)9bDc!r7;2H2Yy)@e{MdZ<`~=$()v0=2KOMC#qIBDs4Q;N# z@bDb^H~MV7Mt|P95Ea@|c#=sDe)KMNR_+}S19D-eJnY|7_0uKPzVF;>G5ei9_6H!2 z0pVT7B9!}|MwFL*YTXn7>x92*B^@w|eCz!?gR3A^IX~&X1h_;)_Bs2+B2$pZLqX$M z9f&S^ykVj!t=S;DdG_I4nqthlLh2QLd|eusff7q8jb)W@Y51ptH`^klyK&K0Jk?R7 zm7A>nf9LzoL+A{e+C|~N)D1wV{sXKWj1n9yEz{F}{Ikj&(B1$+xc4+$o7mreF~gTz z#i1Lm>7w&@wmc%D#vs%ja<YUe_L#~-J9*Q_`t}^wYAP#$MR;fBe?$1MA$aIF9CqXf1L(OyxPL|Q_do0#eh6dB zd;K{hgK|Rw56=1WVI^|o;%wsjH6yW&pcN8LVwKOuT4@;8Uf4fT%|V77CcZaS8+@0x0)D+x)i*l+o~3zM367fLnw{?v+h;^>^9QWHFIn}y z^IN#jCoP`|%q0FKk?~2Aiav*GQGU=sj&q2<1@w`lE!F^x5FcOEKQP~@l?Ke}eg&%m z2KCV!$5BMH;Y3B0C1#Fj$HYU~`okfXddjt8O~Xxu-DZjX5z*|jHXs!hB19GzWO_XJ zUsEvJv2@6PZ~5yaIofVqODW3v&9nblSaz#Qd5)iBG{#%r|FUTG`R&ZS-8qAB=4XX>Uz|HxiWjc;2JvvGuSbsi zf3K%hA9iUNi>d?x8I^$C+c&>BZ`bbMI+q(&^hR8&85X;WWR6XoSxNkG7rH!cT7GhOiu&(jm}+j+F-GuiE=Jhc$r)oXiz*PBH3P6gbI~FeuFUWm;=cB;&Vj?{1cN%G~Tcl=h2t)px1Y zu`sL<*Of}&X*3~j@lx(JQq;IoLxGo{l$uX7fIiujKQU`CCKZ54Q@`9FYhb{^P&Lm+E7`E=2Fxl9&U4NP;Gbl~FBsHi|^f=+cyAI~G7Ty%@O5za@2(EQh6=~2md2jgD*fgVj1%%cE z`~?h4s;?yD&9|4*rq_vR(_TkcWy_J=+r2L@_U7P9Z;u$1Bafr~?XN)2i$d_8G`qGN zp0{ny_GJl6Oh7pNS}GDb9dWuODo#3tIJl=CLV(q89(y+^)Wv+7_AmSD!D4L}HyYm9 z63o{`#Bz`;N<1b1>2<4M?%9yI@IM^E+)SlvnEEsAWwJm9N)5`Dw#zfh7Cb+cL~wM= ze$V3HVKO!m-I1m}V~)l`HdNPlK~!jN-emq98A=jb#V6Sik)~Is`q-qU;I}wt7~Ql) z)}Jqx$&5AJt^o zlEL+AvV9P;%i}U;jj95fOPL6w)K_o!P08L#TOQ`R>*%LHPPXyBC6EwIM+#d8!=RSy z&dKq9W3H0qzuSm?>MI~y9#a@1_OyC6@`g^Y;Jdhhfzfj?I;hXoK6LD#q92wXh4E_> z0MJ#$<)&g{KNqzJ@IE=o69$vXx4i9p($rV~QPK0>b7KzZS7&AT)E@%zP%Jafq%v*u z`Tlax}n*Po_{4hdA^`(tqj{noX z^5yCwBW6BBA^NhbJ|?Nst}M3i&G3tFgYT}HF7PA)6a7P!wFK($)#*2%kAW;+MsIvB4Az#ZB~hCBCq5zBT?<`W&1 z^%eKHXy+G2#QP!V3$1w!<0=NM-v=OtBf-Aj1oC;MbQe`jp>R6f*K%{wuk8oh14)4Y z2#wQ5zYwt9#>By?kCtMd2`laFW*8DnwOyT~oUuL=5apZmPZ-bf?w_x-7>l)m^%X6L zurt)O8%Ljc0pW)pKH|&Ow=HOkPfZrTN7hTvzFkh7)j64sq+vOe*D0rT$u=#sx2c(I zX{6}+N-C6VLl)CE#7HGb7Ke4FiI7ugU2O@kkEC!H=j)i1;U;;ngt+o*SN2ih@zGH> zycJs-p_wMux0x)ZkGYop7MYbwcE`-M+1ove)!EgPM`454^HqBcHPTs7RliR)A1Eva z8SS-x;=E4Ee$fFjK_G|dnMst&br6sE*~x5~h0E)!$y~GIhH;i_+=g<(xfECC!bVZvc&e~R)-}`-nN60Kz=ZBC;5Q6XU+~M<{C86(7ja>)DE~^4$O5nu z_t992q*mKCQ`7XQgZqKr{$T6hoB0u+YQ9EFMjMZMn3ptaV$oSZ`5_=hZc%Yu=7J(k zgI#Duk#cuQL-7)gu6B1-mO`zC(JtxSlM8v{s7i>*Q!+Kj*p@gt#^5-9{>g?eRPVCN zRj@Z2%udVNM%74>bDwSaPF=S__piCRdz|bJQ}Zs5NW^lLKGJv@He-V>-_PX z+YKB8K#$(QCCr^k^A$f+2O>o=*MeFzPvClqM~f;y3n{sSx@_N|dCC~35Ab%h?h$2V z0e0%5K|a*ON^AK)^V1~rsrcHlSzmof?vH!>iO<8>FTUDp%&)p9`LcYHlXY0l4G7`c z-99P(;NIAP+>1ULvuNiCf@6_BD#9KxTDzp-Du}P^r=}iNqF7uu`VW( z=nL)p`8gPkdrLw5_2D)*3ByvFWt>_{Yqpvc*a!e%|)lKiJAIsuvBtYl$@{3DGcN9!x5S4 zNSSj};my0OBRq-j1r}C-hX@rU?MW_&CM$3(v1MRJ`AZNKE=U|K4)ciz1EfGBV=en^ z_2z$1n-CZs?EkPfk}(HQW4~-4xzGW*)@32h3WK}3q#I@#!CD0+TFKk@H?_4DJ5J2~ zmB_@VT^x;<`(D>2S`^Iah{zOM#Rur-I}_+u=LLUk6I35|KT2_{7F;W_5%a^6nPZ0N zb1`u}Q}-TpRdP^Qwx9(X!YTW;Ds2EccV)DeLmR;nTNum<9v}_<`?k z=LbhQhNM4DLPEU+6@EYDZM><|;CJbHY`#R}bb5e73(rs<>WDv#@8+6jH17Z5Kd;r|6aPz#5!fXgev`sLdysRYk-Xa}g!BwR|eSjE+CFUMj zwNq(g%?abFIxJ8*s+Y>lWUHD|*&zqQ))C%jy;Vzq?nY94p=d61yMJo$K@RBmW)s~| z@65e-CX-@md(84JgCuq>B)+e{NWZ02kkN=$)0ir!zzq^BJ?f6d31;^7{Hed|YbyNF zrxZ^fD~x|P;41&`x`plf(MK}4eP#C~rUTxOW$6ZvJy9kx$8$)4gispnX6f_m{c`{+ zNoNJo-?HL3z%n1g&>pcoHL~Cfn1KTr=n+fmFK{|k(mZc2Y%^Hxsd@-(za%?3mQ^U2 zlPj+W!taqj*y7@&m;R+Ubc+N9Zi({4$t0qsVIZES7B%%hs$!wUaN9bXpNkhTgk| zNJfL?MxGt1A(+HDjv3)C?4(3BV!LG@^vW{Qs~E+N)3IxU%XLi55qAvJ8fN^^E`=PH zTtdZ5j5WP3Y@D!7NBWB}YQH?XcQz`)CVgP{oaeafw_-&FKppxrMFs$FT-R;lj+K#+ zc`%~(pkMkVwMCsySom+dI^S@TGk;*5659NcraCpxJdj61J2;nPqd`5)ZR{ROBl3Ti z3*QtgD|~!Cj~(9KVUAUI1MWPTqC<}OEimfDY1q6R$#d0la>W$<88LxqCPSuE{QzKV zK#CtGm0fdG1K`K{Lh3~4BuV89^BW4-g!S@(LKfFsQ{DSZfZ9INNZbQu>;(5kmuCBO z*M87a7MbKa^V)X7G!Xp8RZlFs1x`2lu@BM4AnYdq`_|kgW=`5;f}cxh{3WFo6yVjvg>>4Ez?uWDrQQGo}XUK@{>Lg zrT-C}tgJ?VpbnDWsyiC)B~0REVBj@+upHDx77zKKatS~R^kT$85=hm3Yl!{Zk zdD16i{^-_q_<-NANqOw`Na}G1=sy0@{UlIB#q6m8?fJA3FfNTT3F~la#IuVX0|T!# zgq))=up}%;CJplig_q>L{%AoTH6~Ioop;k3zhBMW$qtPFv&TS$F_3lRKOJ*;zv=cK z+92EjXa;)HZ9pZ_X>HKU^_TFGZLsH*i_C#qqi6nRy^fw07tK2RU>76{150(A%W;F{ zgcp2nX6R?sqNl`m{o0dS-%BaW2w!NijV`d?FIL)E;2bBnxlQilrU~o7aZ{S|Ni8-= zF1Bh^{`XRTR30ld1LQ>E5Hh&x2@%n}bjk<-g*>pjA?tyP&gQOikfho1kOqz4(vb2r zTGjx~um;h=P+9|mR1Ds;4~U;q2Bi=-KQ%lWnEu~iPKnA*z z$KA%_2>T!jBv=Y%Z72{cg~xral7P3*+`VZxq*LM7h|1OWmJmi-JioR61ktvNS>ZTb z2+0N6<;~hNEPlSUXi%u+`@O;@+Rq}g3O&Fg%z>1`SY9;aRT||k8gp$9tX^Q$?WfcY zHU4=cYhbW66uJ~DJB>D6L(8raW6T;D))WOsdDvcT>5Nv`Re}GRb*L%Yb!dLnpYU9v;qZ~*MCdRa9V?U%`C*mK@w=ggdKCqaok(Bcf_K@lVmHzN_u2V>*K8x@TP_5 z?iSz8I{HuLtVeU2`t@F)gMQ;JL@#jm|Li|!+3?ZsEZ*Yy2WIbG6tb=Zg_uWJkU$Ek zIlKIP`D0Ai34mdJv3Eq@Z)WwbvA44}zDK4;r}6cs{LE&ftd)Eo=1_JGG;cd11E4K# zR_>$?ifU`^H4PoQ-#-D!K+SvcO(5bk@|4XZPuEmMnEAbMfp#+eH6Sq^YlDACNkFU^ z9`|$RmB{wTD)#OAUEBjNrF+Qj51|W5SbLc?dwytlKWn+BfZ=rfuz0sc-?+tmCyp%0 zrr?x}hLYsNUX21b|E)%g8;#3dh=1(Xu&?i)Le1p4bk@ zeF%N*cp$s>@IFR9W{DF00TQwF`pewPAH3&&_J{G(b$A`k`iC$7pz*tH(Rp%_k{@fQ zl2sb(n37P^W}seo)E)o$)}&WtH?1GjglgEc18h+0^Xzwd|75q8`leE=8Rn|vAF}|D_>)~ z{#HO?g6!P}8%Y3)3k+if*8MiEdem}v*Rsyg03E-?FSiE1I6PFr92#2Dqdz>f*jMn! zFy3_9g&5yoNVmyp^~&&&Qyab7nYP~^xEvVmtif<^kMi^<7ehbg2|u4I>6QKh)?aarCo+L`y8a!&b-QjyOBrRxY-`pk5j%EZIjnWW zi>>@;`3i7%!u8Mp^q~x!GsPoHU1=RiBpvwkw0XJRnNU~?X5`0XdCH4!*v|^m%vI)0 zEPp+ghbP-NPgaaDvNv-$!oTk*4saqqMNyriEcYM&12}W;qYVu>12+BLk3^$tw=nr1 zUQ-@QBw39N_%eR%_%h9Xa}?NB^#ht)ET5bBRW2sQTmSN?=Z&< ze(q&l=>NJ!O?jZ&Y$7W*i&It4l%0(WKn+A4G@Nzu{REOd#Wjx{kdelULu1ngcZ|N{ zjEN5$vb32nKq@#^XkisL4^XGb-Vp`TV7v>(y?L`bDeQGPXAc{{oPd9xRB)aGf4V!0 zpWao^x^r3{xxYWDeJUuX(72ybNg|Q4#bDmgGvPNo`XRN8Y98?6`qUw=t_XVeT`iYW z;xm`m!Q)jsM5(7jz*)r8v+&i467yIYuWuzCu8s4b^&f-Z`MWA|qSy=%uBV(b<8fMy z+CgsYB$j?E3ZT9cukw0w#c7KPaq@Jzos0~$6VW}!pFHHq^sj_k*fECj0(~Kdr9)!r zky9+fz>?n8x$e#tyJ=$N2Z!(OpbPh%_x|Na{*2Zx0v}G?hyzNbm-aq5Lt94n#pQ}s zH6?=p-YgR~=Y*DT3yb)Ua?b_x>=j8y&hR`6Ph(d|x2 zD6vh z%f~t3L!~38>x`?6GZl)TK8u52-w>a*sM0qq z<#5A6H`0NUOd&VoAva$?sg@2Y%)U}M@=Uz7GwgKwtD>$l7V>vAL=3v28Gw_~@=I84 zN+f*?Hcd^lydge8={!fjNDpl$@DiZ`%#L`&J2ZnAmX&*i87g$}{-n;r@$Mk~k|pqQ zMRU*&r;C8msmj*w z{`6nd+uu#pAlCCQh?9y)In2qE^(e*bf!>8LP zx1xC!T6y;n`(3YdMT5uuHq^qW3;QF-E;v33q`ko7NJv&q57B#fxD+n=7CassH%3FB zfTXF?B4-j(o4s&8h>M%2<<1n`K@m=f$Z1=Q(7Uqd>usLY@|p14FI$Z!50Ag}$)J}8 zn3ymADMf%GHADgHWRhHV|3d-f}ka^uP z0)!3?8+!aWAUrFKDUu!+v74S-tt(Vhy-5CTV;)Rvh{o<2s(g{LeERz`oSs%4A_<{I z+M76GQ5eLErBxPBLM*Jv=cAE>1+yiR*~e>%QQv=Inb9fQM2ofOsBw)YrgNMZh5hC3 znjRmCts+vCdygTK(0~DnSc~U}C>~D3z+IBtcy$^vVF}jY;h;zXKg(=iyL^j`J^)33 z7V+00N;vn|0FTn1;v$9?-AtkYaY$V;r7k^)Xp3c=hM3KU-%(c}BJ+Th@?~7M-g(bw z)7sQ9ou#}MCEb4#xr{tj32Is<>Mw`q^_Z&m%!(vU7cDct| zEIuug9c=1#^-PY@q>QgxW4$Yg+9tmysT0q;NceXuY`5G}DjeX4Df#7D0(`n_{;>Lb zxNd9$!OID90Zlur0P^cW(e1#SeD7}qm88|*h`|=S05wvPTx>D9?g284n?0>%i2S(~ zZTX(zIfCp#3&aVg2d@aCPoA~lRQ#FK<1A=yGpI5FBanrZ@-J(uKk>sqln;;^lrAsj zYCi-!6?WyewSJM?^tPK|qcCRG_87QxDHUP$_9f=Dc}Ht1AOAO%hsMS}jI*vT&5Ikt zn`}J-a;K%KMiGfdV5xAKBKIKARb{bAq>)_N3Qhcdzl{PClTo7ebh7YAk{siMF8MDS ze8hT}I2Z$T@;5hx$OUOPFL${$!<_c-E89(znb&qJ-i zUBa1|MVwL%hACr7_*a1!_0>2TI6YZnC6{h%hN=Mj2283!m*0RVp7=NYxz0-7nF7r70 zZ49yMd?l>^#{dV?AM70MnmZ{k@I8<8;nydItW%u=AIgbIe)M?^eD`2i7@X35)`Tgd z0C5cSe08F%m<4it?jB3$=jXvPm#bxG-&A8%GS896DNGf@`miZo~e?$5ebG*qzn0K298$&$-E3e!DKxHUJ1`oER7oq zqNHa@ER4jl%VSARl7?tAU^CymUlWUSV~Mn`-|sV*r=7JmKJ=zD63c2gkpHfdk zw(RN$<}xYq0L1^>xyy1Q-f$%rmna1Nm!$}$I0%#3og-nT%}S!rg(BNPu^xKr8m(^6 zGRxzt4Cu}DNQX3p^cJ|>Xxr3}kwz#My^WJu3w7+guD(w``vgH)}W z#)pL~+62^wd9rQ+jRdt6&Y3j?#0n#j6-WLrD#}Cl0lY0N2#1dJ_w6W|Q8_7-Cn6kRN*7Ex|qIGPGX2fdjeqC6u z!x;*AzjfXTdJMB{w=mU8UP=(6TP3aws<@rSNoJ=j8HOIlveu(|Oav%FU#*G*^d1JR ztkBATtknvcp)}YJmhztVet46`B>ypm;vH3`H6_cY-O4|bEk0KL)S%f>0dvzC9j-VPRQEL z%=`|`dAkfRlo9r74jFTMCnO06G2($RG#LmkO{5(F|~{JaJ7Yum|ZP{ zX)-N^H}Yg&*x&o01V~j-df(ZPb^4w>&}N>s^QioUg-f0Q71+fS-75zyL0Evq_4k4* zm)S_S16Tb*${z1&iJx}amjv)rhA(WMNhFAlTdD>k!k)mm4H5>v<%NB(Pp?a}fAZ!o zu7Jco(}RwpOCa}ku9mi?$lGqBHkElS<>ses{hs)^-2GlHUz{H9`As~7+>Pjzsp))F z1m%5Yk>=%0V%<7Ir(i&Lij}e*=c;IOOkYTKZrCb6G$RaE`tcbPu!Vt z?l`1o>33!*^xCV}{QV{PGDyQWIS^!gb)Xl-NXMUpH*$_c5?mr zHyBvNzuOEKh*dEcYz?q6a85a+{r6j6c9tZFt;36L^tluY+Wk|oAljzfsN9PY+vs>!Ab z2sA@fY?(lVz3h*&#ls5rySI8y04*lJB06(>PNF<$F6$>!1nmKy>V}zQWF?~o!-2p6 zJW#+v8j7Sm^W-VwG&s0;W=p2r4_$X10+Q^&<{V;Qxr%~QV?pjvkQPwKg&?yp5HP0PY06%I;q=Jmox-HV7cZ&UY1QF)P7i>Q` z;?-{FzZ5U)sz9qS`3aXVJ!GLX7*kvUqQ=5Gxu;B;)r3ir`X;(a%?pmuG?QT=u!>u$ zNoWyC;EB@MA>06p&hz)g^cqva>?AZ7a~}BxlXAenHc^_XEzDFD}4@;vK{F%;Md=* z(r!Tbg>G3AFC=S!IMeN^A%$Mo27@^nX1b7C?QWIU-u(K0e*B&b^q&>=VMf*85#xG6 z+hdCH0?m_)tva|ZAtr_86$LZkd2%kW_HdMu)#MG_K8@aywmX?P0#8=V^EQ;Gm_N1c z1`6KAdu6%?%e$xv@*;@GymT0R(=f*zcop6a-|-r5+dKy!tIdO%7jOVM9za$Oe?s;$ zPXz9)hzHYWsF0>D?vWCA;JG!A90|L_^)9?bDI~~DYIX#*0ijQXQQvf##=t9ffOm6- ze;m2^zML%nCbQvS)|YJ5a35(H;Y8J%HdVcfOhH^E=`3EPFhM`FjChUoFxTy@1~so` z=JI^{1*clZlXmP8DPuA;Jc*E>mqS5e!I z)GFsWo%!pPW3ypTbj6Mq9*fc92kIu`bw(EBG2}8xH$9J{8V!nR6t9B^t_VN}MNs#< zT#fG2-e6#HB^6SR)a*d9!BLlZdSnD+V#X}FLqXN{Oa4wH-9joxXwmnXw68y>8OFKN zMvl{F>sv0e)6hk9{8$=h0>S_T&a0p*-=n;-jzUzQ+hCq2u`AdLf$)I4b;?|-CRxK1 z`!+3=l)APdt^D8<@T4)1%aRY;`w=5=r~~)MdIo`0orT*4JohyB<%DKbqA=^?@_jQh zC|MMRB|Q{-bFvF#2lg)9VJNooWZQUhzf=pUdTNiw+r8=NYRzvtr!ym&WRdZng~yfi zvp*a3s-w9W{ID1H1o}7xQ}kXvaY94osq_D=EE+gV+U- z@{1^(rWVNS=C1mY%%=Y4$9vhIJSGt5B?)z8pV%}IcdDT*6kPab>q10G(Dk~m^=q8B zG#V&~_P94GKQ>&VUf|^3$UW(c)^{DZcoD=)ay+3qE>czu7>XttK7IiW^(TWcKCKxeJHXTQ zZ&8=CJxO}LI*lQPtny+>o|a(l&Em~liubGm0r5OQ z2C@8oJuJNemcg2Et!$jG_VvK1nI?3zDIfoR9L~sqAa4Oc53-0K;U7NbBaemyesAVb zoSn9b=Z;hE$N=V~eEqihfV%--7t^g9avoJ~fBUc5DM7bOT$||FBHppPk`utyF$rb;E_o$hCs7sGEY=TSV&9usc_NX2HA#gG~K zP31`BUnUxl@huBx4MQMMp8&K&(El_AsXwPR{K{E(nw}u`>Yn4oJ^-4DA+~do)UA7- z1_!J@m+zuGYYu2xB;Sb!WcSQ$!7$`!3C(+W;{H!Be)1gwABcQI=fzCb5%P+aA?jX< z*OMYTBi?RB8oIR0v_ir}H3$02Sc-l;`80~poqW9uz!$kM25m|d)q5b zKRWwQZ>h9fW4vtp9V#h0#cDH`yEA+)Z9GZgMM$cEhSD_# zCXb*ITT}i_W3}48d@~N$(DI^Z%Z)0B|1{(b;0(yb`19K!`ep>pd>^JWaM_*-e?*H| zl@}gJ*p{pTIx9lGNVLIRStA%-2?M=z@7qdk@wbUs!&V}PKosFMY4_cY1YL-TAZ-x~ zE!yOQR@hQ~pw&dE^S}Q>XV&9PxMkP8Gl>~w{rDc6bl90aBG4+!(%p2KH??#9S*#bN zQMy~DKzG7mK#EMv=OdG>SEj?}^Jz9JXZVIeo1Vc!Wzikn^=h znLW_{%qHCm4eQzy&M3$OXz_nP1Wi)WV|x-t0#~HI2M67{zV zL+ddHA(vf|aB{C%8bA_ho}Hi2opeko3(#(*MCVL0owt_XK+iM%`2CCEr|7eWw_ML0^$HzJToJ9NSk#va~ zOmHA5bj;5GZksx-qll|pLNo~Xu7gZW zKGs!IuLF-FC3w*?o2CjTfUV@XUPxwe?=?Oc6$~>@mOhM8rkQ|lD;F&5#_|-nw zL&on#G5tSOR$uzg4kzHWA(FdYG3)yWl{nW`kr?6s?(Tuszr zET)hysfuvk{#FI{T?kApHDG9z(+rn(Z&*z&RI&6>LgA<2|3lGv_(S3Uas0MB^Ei7R zXPkZZ*4Z&Vwd_JGY=ktEN z$Lsa{)Og0&e@yRxhQFJjT{EWKr+TRV8$j<{tC{$Zt9I_kby-t+(#-=Vz31NfhZpNU zGH+HLMoW-Ths2GfOSigi2e>62=&Bs>7RG&)7}5Ck5o9wO*_mdcGp3U+_WS9BGV#xS zr~M-Ld)|K0dsXA_@H;_>;Txa+!FljmF$}z?5B#m4T0;yCv#d1BXXPEKI{Wo^d^@}K zu?cbuz!CcMYDW9Duj$VeOP@Xb{ZvDj1G|m122yejZk#zQ{bKi+e<KPVj6;$Wvk>s3qGiO^R#5}Bc!xr85;2^j%uZFhK_XxWy+Mx+GZWW z$rRbAXq~tZ2f977Pi2jYH`({!Srv&}6~ejSj#+bKY)S-+_3zs9DVullv<#IWNAk6s z=Lb(SOfslKhUwBRBq=ir8`rw!bgW6#E4B;DUIVn1(JYQjf1{hxq)y5R4;mjvA24|b zW8;^m)et90z)BY6bUGbRo~{+p^)U2F3|uU9773%L-36|&u`{w>36jF$e6gN~cAiQs zZ_uU?Rp+S5DfakP8zOl85@Sh#cG?_6c|TwUiq zSOG@tM!FlQ5i!C1;{D8DvKmsM3W>L?S!o{?0zIikC&QhW+@NchTWp)Mkruv znpqX^Xg{|#yQ8aA-`_pEZvvOH9zcWGJQDNzhdvD1Sw1kHR>X~P9xLW|jysugbU!j2 zQ_=vzY3$t?$sKO3wJ6sTt*O8~ju{xzOQ~x#esQPc^<_2{%7*P08X@2iR!7~OJ0LvZ z)u#d#Bn#=JECH9Tj(ZeFOWNcC0cNLcyqn_ZsjR%soosKdxO-_hL<&LVi!`8)z zBQTM$IZ1{%=kpePE%7!&uAa(T&myMsgTx}9yBff9t5LypS&Feh9&5uN>?ul>Fe1kJ zJ@Ain#Jo}-g9d_Y7%D>56RmfK<3&E(jyde8^*)ou5GY_xOtG&CuMdLl9}pnA>Mi${t5(mB8&&jy-sAAF!OppTZ-hvN*kuXtsXc0rI%rtt*_iXdOIEu z)@|W*wx*48Jlvy{%1p56rHpWOWf)Xe+sdk;ax*}U@~2+~LrWU0*^_aSo@$N?&V#lK zg?)-`hB4tmBX}13J@W1nK_w3Hree~{_`A>t+-cCYRoSuXU)%ub*B$%6qcR;wgQuha zomm0hHW#@knN`3?T=63?%cO9fx(e81QHc8-+1Nc6R=-0W3kIB!0kWmwse$bKyxiY( zH!1Ys=`q6<8*XGUTHG2mYORxDDzpt3U)ZD)}_Fx>|BW!NTEvNZHE)4(o+{rRY zK_lD4x#TxB!@Yl_pvd*D7x8o9eX0ti;+H*6XI|Bz3vHEo|X{xhcyLKg)?#WXdDJisX zu=+PLe+f!G7i^3JVMtB2R>GBQmVgZI!y+4dQvyU;f^=jg5BFqMHh=-bHfcVn^P$YigjKCOqKAiw=Xi+R(%>#>W z@_|^X75GnYk41(Nw~C83qu}!ms$aw82o7FRG4gDq`B`$g*Ve-1F(s0+co`O?4f$Mx zlM9BXT>|Ii;#CW#Ck^hZflb+^fTn<_Ez za1%HCA0m$Y^QE#+stDT~BDg8tVgUhf^3wFAt0OH#;>%j82&vQ;tY34=#6qYj;h(oe zQ}`8397gfD{p*OLTX0&(NUo<+t;jU~rY$$Qz)_hZzAES%?;?aN5QxmbNr2fiU$p1Z zGB%GdAC9%&MY`OrhX!Cy=iz3ErAc%shKZjMI^1FDhZ!wXVxW1!xH5Es3xZird{p8AQkxt+b`K(1!1E)z^!{dcBV;qjGG+Di3kR1yUw^nWe2lk^d7tk%hms3BqU58SqS1)7Po;aCuo@;zfdz9i zRf2;swmaX@Z(%hbC0`f5Gu?fs^3|wzV7Sc+n>Sf9Ro>=E-Lm{Y;` z8CyM`t)9`d+QC{9rD!-FMg>5f01#`!uUaM6d_2jEu~qZ*MXsu9VbS9u_LKSYr%BeQ z9j8H)`S+)z)Zi_Vy~s=5s%+d1po7R)mUvwBSnH(Y>)xHCy?p4utiSrvQh)5B!C{CF zG=;PbB+H_t(E5Q!Rhk zZyF_T1*f*jz#zMg6OC^Ew_7sH21iXq2{egouFcczY749=UVx7!7TUe6rJhEr7 z>=(k1ZI$xpW`3F?`q|Z%S+z{rnDw~cv#m!qhiZw>cCq8;kP*o;JeF$}AYs0hM z2f!XMei-LVhZieSnYp3G;P~R0bj7a`0G3sdV9}mHPo7{>Fz#jeZ$?}D`1vPI&x9n* z)M&x?rz61x#`@5t?aX;Fkv=R<0OxkHf0Gpz!oLRCTc!p1!=G?0e*4DUM7VwAL^(-0 z$@f@(8U;8?Tt~C!5_+UZ;VyKjtTOP}IRhCld2AcT!t0W1I3-Eo+^nKY8FXQI64c(9 zVdNI1iin8qB81@ryb8~jYp<{n2)p&G1IK3i`Fb(G z)pN~vc>-ql%iiJ|&}t$HY6fvA$51H_UOZ+oQCa~JO4|^9KCR!w7TTp4pDFzS&qyD8 zl!Axn6IddMP=KQO6>iH;b{|+n!SUr1?eebch7CfGLTuWgu*(UMX5_1^MQz42RU*Oa zLho#EsG=BMu{w(Jg=1uR45GmGfr3BPNI;}B`7S>Fd#GmkEx{0_B5_ih1HMWcpW{jZ z{0xqsrrU^}vN62P3?L{1JSW0eHBsIXh<~M0M)Ti6ZrRaWS+)3N?JiUUKwesd^*%vb zUS(xYb0ut6Bc_u-GDcIa3}jAsf_K6QXmeoXjPIg%?6_Zk@p+<)#R@EH`iaI%OSM_$ zE7MSDEa7|y#b$0wGZxdnZKp7WjENlw3Zkt)aSZ7aJNGBL`2%78gwvPk;D>b1YWzy* zmmdrbLxUOd?;1$9Z}!KI({}^utOQkLKoNo%NqPPa*^17kX>D$~VGL;aB&yWZ(sz9) z)+VCgi@QmCWvC)F>z^{jXTG02s(>>xx$Iig>W$L!Zk`o8rXovVf4qA>-eqF+w&3#W zxM{&`wpvGK8(Cea?wOEh((LdDHJ7FK_55}GGTRHPUK{|vH+d+E4%vWO$})8BH*#Dm zPqaJ_UP)ptWHbnuH1L-+n4jO^kDhMgJ7*FOle)wDWk17Znv#q6f~b7o*K~a6p4`Y_ zo#j)1k2sqqtvQBbpMPYBnuSds}Y zS2_a3e%9FYS{z?53|wQ`VOX-ZQ`mNHtLb~J`)a9e=ZW0yoONOZCyjZ#^nt{gz&>+t zEb}w_%s0G^WLUv-^)6o-3;1+U01UMOC~8QtJS)U|O2e*9Pzvf&v=_*Lq=e#PPOqpG zA^xEvf<8Ks`XfH_su^?!e}0!D6B5}{f&aRP*y3~gjn5)?dlMvxEbZBWE)ggU$N?biuaBL_hI+Hug9V;8s+DGoNaoZFG5m?hOAx7oW*5_ ziwLP6@NqlQ?;bI7?T15apPJsDZ<4i3;f1UsolMe&h#SnrJtWt4v~n-;$L(oYCWk`W zm)t&22$4heZojkQwGVS6dpkg0gol$6ZjX_{LX|WK*lN-# zoj|a2Vk(ORAXFk4p29e40{ara67v#W=tj7rp#30t3POrE(0D9}{Nvj(yH-mH9^ksd z^$0v5n3Wtvn3i?&S=3a9I57Y8-u!%!OO`qm1+v9dI~ zuh~cQyfdFtq2J#&=V-{g@H{|{36e#(=n>&qfD*^HKNd896+RG3ucn~kwRA)?fwcn9 zl14Q=a-eDD8D7#}fyAVRL@%_tzkn1uj=CBuZQpqBeTwC(yXGX~&v(?Z4tF-R*SAMI z6Z6uzkI>0-k)5WKdW(_O(#Q7M9e$r~y6)(4dlY_ql&xa0F*Wg`0ilaYLFCdQcy#); z)k+Ee+&68X!i1mDalVbg#4r6ex8JuPDqQJ_kDM&J0?z9#muX2gfJ87}>(e2I#J?at zs0UHf!?KT{1aBm=mfF9dVu_R zJWB;(j6F|Ftgm7>u(g-{5=dVANPE3aaA!2eDnBkLq$j$2)#`X`zLl4FbRY%xLie8T zIUz5A?#shW@!(Nug&K4TNGOQtK6nY=MsPZy}}uN>+_30rbmGW+RcYCWgC$dssb+OJP)S7 z3ABZ~NWs}H;}eahv)m- z)Hmm!LqY$O_P<7ya!wY`>%-o&Q^0uM7B_8kl{&dbpvyArkp#$>)?3axqehkBkVO(lJck+>zMYl48*%tR_ECf zr8o3GZ$ZQMq4XvF(8pEvu0!m*fgy-eto@Mw@2k=|C}oEdUdhp7I8rpKRT?-V8NH5_ zF}%OkC7tUbX&KQ;6RCv2kyu?C^_t{ZCL6zSOfwn?f$nnF0OgXmi1F^2LT^Ez8d-gR?5eLfE6a)vjA&&kg^z=GE|X=2_0%TKOCMGX0?BPzoCs?Rl- zd-y~)=)HIWYQ8l2@?6Iw?X@U&JcAgWzq@=U){-}%CT{vVWBsCG@;stfA0G39I}>TQ05zvK&(W{15QoOz`__v(Vp z%|6}*k`8*nj9v3RGsA)NAEb_#$0ck-z+S%G&CQb%XOZo}F7npFpH%LAQH zX*iQH!nZ;YZX}Eq8hj`}rm$r}wWW*S#K&$1=6M6WOG}IpH_!QXf4Ca9b9Gs$l25ng zWo6d{EHo-i>V*i4I|HtOlk-bjeuN%lTY5woyI_Kzr|_7u2aK7( zPFLP)*tu>m-vrTjhT69*A2i$jRcqcYY^?b6i75FL@`ltg3y5?uG;(fraurM}a~-LMw#(8W28*BY!j#RGO34+!=T91tx@^=18}_F(El2UAQkIC@E*fsLgI7wAP7o$UA1#y4FjT%loRDd!UM`N2PbAdvC#6 zq((WPY}IQVFEhRUy_|V&qopGrWr75^@T}w$3Y0VnE`!dG`Ru6^|7zEFOCJr?=#hB< zbW;Bnu@HWnGH^i%M=BO7jS`*``N1Iz(b9L|QK}Nf1kX8$cg}v=Ggr$!|IyJ5>4&22 zWt^JF!xXJ08@#G+8Fod(WX^Eilpdv=U3SRc9=U!cWvb!LrzkwmSB&l4o;c){G(tb; zgkI@Y9wolZ>T2_Hp^U%0C9zIUa$hQ^iUv7}U6QRYO*m%Zjsop*0nyIfoO5zhdg4=% zFOQ4)s_+!lL7dN_xk&&-Z(4(Ip*TIG5h#$VI>tTc*KR{NMh^-@o3r8y!kI7dPLSSk z^l=&89+Y56Z|1CW(MLB5($g5|LU~{XpB->g-LjT%l2&<7>4VkV@8UULGnkIj7ixF; z%q1<&Q=Uq{){y8P6B{rOvl|F)r>|z#hw<;@kyvvkS{~SSUrV zHx^l;Gt?XvdW|2R8)Q|Z>0D%RLc60-jMGcdS1v^6L&d?>W#Gn^^~ho47hJiR;X zj1UrMzw}8wx2?qpU1x1)ucZNdW#3}=N+{;WAXuHJ$W_mpJXa&gd(zWmeR*u|qpJs5 z{+wh15A2QBFU8*zdXA3zs`QR?xgI1cD5+#_4dNsK!1uNVdZoNbTt9PX#Wn>XN+(?G zOv>C*^^Uz=F8iC_@l_Rz=UpYmp226nujXkpx=Xs+vNaw%C!A)Gt|gklO$}_h=j0GN zy6yGaRJqTME(Y32H)IyR*>juTcVTEYIhbLW{rR9@9fB~gnX8q~gSgkaJZet2GQY0& zVsKXJ_3grG`Fy8xIkWGG`6!Ftdi%_4BcN8jr)ed5gmI~MpF2lLbBfjwpku~|jg5eI z+Sn2}KNe16ryxBNm+?@QDmt4j?F|E&JW`m-{-B)?V+V_bxx=)})=FX&v4&W6|P?zUCZ>OWJ*1Pa9t1Y#Ni9bK(c#`+z*Hi)I)s(c?^wux(EzQw+h2AG%c1m6Ct9E`AKtvn&dM8 z6cS2Fm&@0a|7DVQh8h2ayKzdeA;iRk*o9eN4Kk(!xq2I}H@`s}81MalwlcU@mqD?x zhxwcP<9|ogmMpP24<5lcRypa7~Zf{k< zn&JFa!o}Lom*2^B1Jkm!(gbjUO>(%KO`wKEmxGN!Wg0fmRP6mmyn!6#)Sj9(5n_kN zTFrrR@5Po_u)6yaFK?xf@LNpqfJX>au%md^COyQEA5DLZ8@ zz~t3~{2Z8kn>QU)-?`2Q$9}!#@#R422cI@>lwS#ujRu(cWXCA%VGW3xe-9vjPDbNo zBY!6;bK2SbU&PM&Q^-EAGN>*pTQ&ct$}OPk(L$V&6A zD4vj67_Z0Lt?&t`=*-AoHB%Y~QDfdIv!7Fu0FXG7g)b#Im#xTv%w@8_>l8zU5fIs; zjkqdla*i5s*cnro1k#A*)}cX^e+AyD04YzI^fZA_&4Kkh#WV%QHI}qHSH;-hCfuse zmwm@$$prhyUe=4#=pEO)|6JnK>O~u@+6hl{tOuk<%XAORRAYcEgFwk}{*zB5erLtl zzc_=8lNXZ=?g%_z;}XlVuXPD{T*%VxfyWiXvs1uO(3WoEuSt0hQnoS;Y=u+u4?gWd z~Q6%1&WofqxEDh}AMaC8RIi zGWl9GZc9@6NWdqlEM$ zRmf~zW?Q%TeRq_{r|37UjjvxcZhQh!t!m@LYR4P>m1D!6)q!M*C<2w3C|@~7c6dDv zDhj%BsYQp_lKSu64c?>xuM*)b4H+6-=u^EeTsZJUyfl}PKw@HGL$7FCR)mT`qjI*3 zJTXC@B6n@EI3*y%76(0zgW583I|d*xP3tFrfwsA!>OrSpsyn;{xf;b94TIBPZHVbW zF5Tx19+^BgRhrg!$7dSNka@2bb7C4fdn&YO+2) z&1!QYjgG9MR~_hHW^Lx0Nt1Qb6U-^jxoVmR$`CaNiwr?ZSfQQ(sMxzYM_P;3UgY5S z$n84$P9BB&c!*b>#e>C!LHTR?0Boy*ywhQ+$FCZKb?fVo4UJxPoC(cl3jEM>5JmN~ z&Q4Iqhy3Wc>oeO5M=Oo?FUxe%P7^>di{<%nsK(-5aHOfBl>mM&WK;DuSC}3U4o{?A7^>C2AMvkXtHDgdm;Ni0aBu zfiQ!Nt|dtQSZApMv|5wWcXHHeDN<7u0aSRk7O#wwd!6C!u#gj6`I{xcY>-vVy6Tpv+{D z3LW%yypW{?Oi9Kmtb@MDR=6|NgCr|0=%9?S8xjP1Qx^b1102cUSrDNEQgumYG>Vyx zN6D?c)H~5Tv;+z;@o}0M4g4>{r?MLR>7t8hYAfm;So7B~1_C{Uhn~el!+d$2X{c2U zmGx?ewQWKvtcXd?$vuF?^J9b*#e-8RO<+%4^i*zn;pXrku zS>fYC&BB=o5sXuz1VJ?|gO-~CMhH#hsevxpk`r2iZ?B1}(ZCm5hqopIO9zLQs7N?T zPA|5z%L3~PfO_pgnfcK0If&yN*iR1Pitd_gikh0p1K!Vo*n>y=k#4_0*t$!Lf=WUgG-YYlbtZaXQO=gVWR8P<+EgH&*1r!F>TB!@jq3zIff zhCx9=8_)oz14743BG#*0blgmdzF^Lg?a9>+G(FQfa#z1;gJPM+-nmoED741$88_G?9t!&M!>8M)q10<=6TJ~bdVci(*RV$y?wL1#1tKt z6mt4y*>C>&0Yy?@Ga=Ynet%Iv)hpsSiS z1So~}{Uz63t)|0b_8gLB-SisgYG^s!dt`6uv(+s{n@;X7ymDIOgGcmQDNp7t1!Py= zUs7^aw*vT!DA9OK<9^Jaa;cyoV+EuMsr*XfcV%%x@oTTM<|kqm+5!(C)nt7`Tt$Y@ z!Uo&1CLjH-^C_r!o?w3@^9!mh=YI^8yL{)>+S*9hlec*;+`zfqdSr7F>gKARNz1x7 zR_q!o$d~}N!Y4RWG`$#0GY-)G|Dfw3@6V&U{&m?8E+$C*_x|cdFy$OW|5}orzF%lZ zLznH)tsftnt|48uKBhTGCpq}^c ziY|j8o?t6GkYMe?#hwM%z^auUC;R-jcSv-N_97A7cB_%i1^p*{PM-+m8+U5o0bR*G z!=lwF@Yy!3we}{Y&gXi#8Y}Sga?Mj`q^aIMq{J4wBsRNoAIfG24M83_CAN5puiBOK zqk_J`Uk$+hWgBTs_bbqxZ8OgKsB$7jpEN@JNGbqv5uyX5HHAqCuVfUqyaia>MbO%%i+!AF?lKw_~53+l4rNln3$f-2U&<=hNriQd+CMn{$pt z@!v06$w=v|`v;x<$&XO<%TC@+i~)ulP^q|jjE z(J#_*Y`~5b9)L90KGr1Sk)|dT7H(lftjQ>h%A=r5@roN`$>T#?ujQLZvND{_)!R_D4qhZGcjYS5i_JRQ^&>}xMfrRu}1_>`r#EBY zQfzp|)r05e;)179GN+pfzsVC(-rXT|r$JE2%$Wg~TBSO>Uyo=qDMhP$BbccI0iF26 z>Eev3jj=01N}*3uCJTj%&W%%_3`~Te6wHsL(@Y^B1a*S<_Ws|jZEdc`ahGgP7K&Iv zK2lxA_W~r0ecueI{qAPvQ;v9oeH)ZihD3*^1$0d0@nclTH?V4@P&hBTH)_R{49Wg1 z8)mLXKF)yVaASu(WgR(6vj#%K|6RdjEbF_1eyO8BJCEPE34VcLI?_gS zH$z8BgVIh!(hyGswr*&r!;EC0OhlUuB0csbbERYPK%*b4i*Pc>Tg(Ybb-neO;V}&! z!Su-ZS=u?8^QRS$&sX@vcN46$?+8LWF-DB}z2FECRQfi9RIZZGl;OkzfZ(f9AZK%~ z6r`F0Z))yL7PgSKF(G&^Q72P?^|1*|B25>B=%TEfS}Q$gkXfbs>49%ZzqD2f3a?8* zcN88RMQX}!?(RRHkOjM{PosNMy7)Cw8x*Y^o{=Dwk5|AmlT;izTe<`%_3l*oyLNS> z_wSWqOiG>xLYBY0e!iLzraicAN+szdHBYB4?%qG-h4J|lO!HhG*nK7EN)IHFpu#D9 z!(<+>~8n)YTq%eKF@I)&v>{ATJGAl*Bf&DCHAx9<$5B4cDq8_O-|3 zo*ET5H&d9vsiZeX-nW}ws2k0EEIYP25>QSa_MUG1P97PdJAuj}Az!D}W@rlVu)`)9_FzJI6*!hTUuY^;)^ z?1UH-y z_lMVf7@@m3b;YYB_9!$(+%SOE30d{=@JFT+H9E0HvW6vkwJ9k|$m}eI->Y2R&v&#S8CU@!#VNj?8 zU?0$S%l`raA_;M0O;H-kchP2KK{7S4M#>c6BI>w*B&Ci$cWT497)>_#U>NYe(bqi z6@MP{ZW6R2rJK|O68R*sAu5RoQ@_NmM#ZQl>cSIKAqMgE{O*pjy>$K!(BiFA7GG=b zMZS*WT?DNHtlLLeg8i286wdUd-^5-D&hULn zgNQZuvkYCcPS0C>*abN=q4h<%_%TWjTRtd7BRK^_#)Q-Y>g@-BY5|(yIQryxMxRC9++gsko*yZ&-08WQGdT=5*?l-o?#a}J0b!DuVyce+e z%IiS>Z`*TbkHP3qh$1G1Qk+BXJaxd5#lsh-s%y}i?B|yuMxroP3h3j-3Ifj^U)+2rDL*!4uf?|Xq|1@ba>)%t@sL#_G-J-(mk0`3h%%evqumT4 zxz+Jv=RR2{?DPe)mx1y*2E*=KjwPztztl$TD$BuM+)gA@781-=l*>TfJ&*$6~WwZQ$zY z;+A$-dXV}-GSm%f3&z33=19- z5YBVm+!3)LGm8(uq&xB9B+)*>hU6`6@%Pa9BlI~nKQG5;%E-@PaAp_<&wU`>t2I4R z>9#hY13dO-NhzUxjQB^HyHvM%SVQ3(nbvK!8Ks9}Zapv)-@kl?ZTRA9g?t!j*_kYN z2$Y}!<<;{I&3Lt47%2^T)2%#0v8;Tg8c}>MAC7bngwYU2X}rCqF(an_X+SZsA+@_! zDy(|k_Q6EGP8OzI7>7m1-gsO^;twl(Svt68MiQPgdcD}4k#K)~_5WU{94de1_Fca< zY!VuiE>9^IOj$f-?AG(}p3a^NPahE^v&xojFrL!NBd@?A{%uSSPR1N> zkv=c5-eMHr+yf5NNuplBqkYv$z@QFK=gCftUX&z^R;ioT=CEQFy^Lb&*&Hw+rUuG6 zkHo_i|MFbcHCGX@&Kyu*@7MqlC<4aW=Lj_xaGD3LE^C>x6O3gAT2GI(O9b(9UF;L(=eCM^+Ab>brq-te6NL*hmx=8IZgfuB+f(#|&q8 zVxQ2N5i?3HD9fmQ?tCH?sLD(|2{zuEoL zv4Lt~L11`X(W31zYv5#PSw)qPKFmkIsEW#ko;j;lbUT47w6rB_PQNCN*l`aK-w4=u zHA`w_k9Sreu*#;Sb2s0f;#)15#&w!SWm`4FPWXPcIWsRNlPX z6FynE3Xrcc4a&1E7#a=f8oC|Sr+8nv{Xf3)t!sC9Peq9caUaKmf)?y2T0}?4Ru?2? zN9rX-K6Ptdl^m+6745WBd8F}R&CV)HL(aLRZ&g9IMJMgMj=U4O--%2;0!q@S`MM-C z*~xeCphUhENzl@RK*;`b`Lk#K@qQ2v8g{`h>-Iq+IOUUvsif!a)9>@#93{Kmy32! zcy6ApnHWZDklugo*8cK83rsY1Xf>TC6az(Tfif94gd*72e2J3 zu)jbef9HrHZ&3rLP+i^hFvlI9rVE*#ff2PrGo*`c|MfT;oNs+Wx4YKmsBS>k z!Z?k(39dLVxw-dz)+2urCWB7^9DcIEvgh}j*``sJ90j)o(IE9dPA`}R3OEplmg6d% zw>IF>^BvHYiy-puV|jRn3d2((tZiz`Uia7=OV2^nzW3$;aPr}=vNHhn^=*V7d{_5S?mD|L0p(Pa;w zF?OD=7vE=C0FT+3fP~LP6jLbWIFCGYtvgw(=||K;XN0I7X~jBLmq5~VC+m>)KAPKPlx}tq2L@kF6Z6N=3Z$Buv+eBa z>g0RDLeE^vo@=<96_68iKyshRgUko;dmOSU&f0_w!H^}KlNRsRiz@2Pk96W|qkOSu zGBc_Akp7b5L)iwAyZ{~F;A4Xayn$QMOzMmh`he)vtTt{E=BnWUl&m+b7G_OxR; zJ_cUpvOyjc0ZJi%!CM0LL2Xy8?QX^-1!P~1zf|qC1VYWx*5uyXBKeg zbg<9cjOHPDTByf}4~rf`LFH!g0Kh{rpKlIu2PMI256bps71Hgc*+_z~<^PB}(|=nf zXh`&@pMm&X)ll1F^}HOVRQ2J+#|fu zYVV}N1sWZ@aGn`PvTgxTostsGB&l{PjA7drJlox(CYz`Xi+e3&DI?d1c2TI{@Ipem zKd=qF9cfl)XTEsd#=3&+@&5-D)78{c<7Pyhdz^~;o3u7TFaAwIJ-K%lF}t=R_`qoR z9EFWzd~R&PsUdcByp0OFmBO7wsW;}p%LsyG4#@@yHKZ?L@>hdf(P{>RrOTbC3T8=( zW2k4jH?Z&wjaUssTlWS3va-v0ORf*(=0Fn3)d~Y+aIl}+(5IScL>DhffSD(T1@asM zDowS$g@(H2^<@w49#4COW`#!ZA=*Qbg1AvoaRr;81Xdx4YYu=sxp8f$?7y=KP1m81 z>C-$f5XIr1xux8_vuBN?U4JgUNu6EmbBWXy|868D*o%pJ7D- zada*KP=F{ULXhO%B32_$m=R><6f8B)(PlB->|aZNH}X757EECQW|5U(O-d47xb` zF&Clv=XH?rq!fx-RbrzANz83x6#Iwjc3Kl;ib2-W0g42WVs3%*nSxfFj=^yk3jJg5 z)ZmnXdE&$s74vD?x5tgoQmX9M2vuUKnTM0pMKC3u^y)S7FJMS| zQ`qkeAjqF8F82jQkLwY*l!tIktMJ} zb?BI!jE-cL?@5+tcNB0(wY5@n-6^lD9(8iuYpP#UGb#E{QL{x?ZISdj7j#osV+N83 zDD#a;GjaK5k9#L{_Go1OIS2A-RncFR-M?R-O#UkN|2Fy-P8!kW(6Xz< z5MBw#0y*I|6?+REYcx)4;E4tAtt-HH?JDSGSdS$Z3V+FD-00Agp&p|{!~DLS51HY@ znI3mu?Hi0Z{7ph{0YwDYN8q5&Lj!B~IdQ7!4>MQIu9}457#;thmRtF!x(GuZpmc4} zkb6TxbwXnQ*N1-jdNCIshXWn^$QY;Gf5Ua+-diJXpfoeGOzgnJW{}dKrwd*a=KZPKvp`}3O%2Og$Gh9J7(Vy3sT}<~`4*@)}{O3i12SR;pMBt(GXG!Y#VO%og ze-xc%SW|xkhS$Y_4Mv9>-HdJ=o#N=m5z;Y2L_{1gI-~^z9Y_lZDhTH25ET#*6m>Kf z0%GfbFYovB;e0sfT<3az&vV~RN5{a-3y$gdnKWd|@4SfZKYkz2`5tZj|LfT1c1vAS*;N`Q~;c77~>Q- zt+YOO5Hx8iY2ZVhAo`oq5!|VMtY1tZR7g8Kh(u!y&cPf6q>i$+bUH*1ujIi>h*)gp zXtt$w1Jj+`gB3|6gFaIzGU{b?aku{n0wZmi0vJjk&f$<$XR>;NCV8bYF8jRaiJ#(E zaBog=Y}l|Z!|R7WARgUcE`Oc#9qYkv|6!5FkPcEE5gN+d5IBNI>|2(KfO&=4v86%q zPg(Yqj8x9^Nip8|7d(Ydhvkta}l9LD+Z!eT1l#Cd{B-lQf~?M0_ScxwrO$cg5wgeS;{~rkBQQtS@l)u-IiGS z?&=v7^H*52=`C!KIghKkV`d!(^aDL27W5+|W)%3rvq2_djNFi)xM<3yYLr%&SSg`a z*1TF6Vv)QlWKzP*H>l( zyFz+Hw=*YRqw8y)AyWuBnQP z|0>36@ju)s|3jg~;i!jMS^6FbbQepuR!ck&< zl9DbsLy3aYUqjScQ034;phN%w{w)|Hfn(Q$P_1rxu*;pieW5@;JP*2dtzs3vhIA0-3-vjZj#FcDNA zVM4#pfE5{olmb^ePY}sWV8)c^V9^97$3U3lhY)zu5Kfs1$ck|63!Vpv7H3k&=y`>~ z{T1VNO<6AIo}g5jz+=(qlWYbDkRzJKZQ><vQT0@6RaXt5fGMKc3M<)zCVOAVbN9+MW1Qh_nu>i~A0MyuKCsi%pLX$_)lVPLx zq~|nPfK`v$N&pvzn{${dnV{(lnTkxQT$iPxM*MAvNVzOrMemxu&krd3XNU}Lv;nO- zHL6-8b3*C`+b`B-6!XUAOB(Rubt(cio)KpWt@8{$M0_Ky%Z1nD%($OrQ6VA|OubfL zuGADtX5XkP8i4Zr`!-PFZ_{DwXO8F>DO7r&P(2K_AH*H)3PuCQI9iV2dp>*4t|+Gzz$=+cQUV_dlz|O>DcDjni2Du;s)sn>y87|WHC-m? zo(mnjBfd-!==nMv~8ZR|1g)$n3SF}Hrx#2i3J0`lxs zSqSw7Zz~?ExpZ*OTqv!M#*!f(nIt|rrAttQVd1IKZ$53&P%b7H9{6x@>`k*9jTGY_ zx<|#h+Ax$marZ29D*M&G{E_^xU8@o`eJgUhaInmx15HyaIgW ziyk0nnj&1CjZ* zgQ#eV3+LNXAn+zgvo7FW)1K$5TT~3lvY5|Yqi8K#rMcm0#UiH%YZir0Lt4K`=cWBe z_R?Imd$tFa!&%mSqz_>ti6>R>1tCAJD1O$644)8P>iS_sxEM>Vj`157F>;mly9z6E$ zMd=lSa{#GT+;QW?{nurvn$k4k9j2)>_z7oXF$iPArwQ(r?HPwG4#b3xGO(F4g{>-!hg=dFb$psc2~x`oMYts&0<89!9Ic-5 z)68A+H0l8jJp=ghjftI^e}$RjO)F0d1zK0+VbKB??Y-Dn8s9B1J0Bt1m2y4d9dpGk$_H?G8E7Kk*yVw9nb35@`W2wF9)dt)?c$w_x`*u%hB{(3UNZYuLw zqC50L075G5TPpzJ!6aC&A@Bhfr5LaWjSbQrg{P2wG)b;NF9wzb9>K`SkYrL_8;p28 zf>_rFPaRe3chV?`_EQ*C8kf@GXZbXPOn--GYn(m>5lWZ4sn^@IOs3v9Om&v|Lx8-+ z_%8%2?hmun2Ngf4foCaA{z_?j72`pJXsvsET^9&eGvye-qSgmeyxtY8n@cs~^t%hI zSjZs8zMeCQ{>cS-T$;HmFu2$i1hpqbC&zDL!x5tSZ;8Z7nP+doEa>xLU>?DI~`U^cDBtrPlLp0je9B z!G@TKgjIn6*gNR5-G;Q~^a!4F`FzS7OPte@uF?PP^O|`HYB_C3s`$TZ5-)ua>Y9f2 zAe~Np2~H5e9FA>gl3;(m0d7+0Jx(t`5m1f}6IhE24H4w^vCIv$%9LzEIo>3ZfgqbiLvVGTl;sZ)7PFTfzwfRO@pK&mm`s~WP@Z{2>cBG~N?}#ws@OArt5OZT1 zd6Sl%W&O5$G*5w4_jyhN#s#L(5ESOkIA|f6Z+(z?Kcxbq?J$acW>fSe)s#FyZmf>ftfYjMq$DcxL4by5>V3V^xQcK1-uwI-_JO03#I?lKYuHXS91Bdb89P8X zS4r!J&k8h@mA~x_IxKjcT*K2Zd5(O=>JVT}Gv}6y0>zgopBhzxMZWRL%Kd->T$n28{ zKJFR+JAx+9{W^Ad6jc^baF~5_1An%@WAaRv>iP}CpZbsa+U&+ zz_Q+5ltTdEa(fPOV~gRvmjO*%*sbikLr}R8erHKO9v_dy%NoX147Fc zejlOxYiUkxa(f_Ev^fdhO>~-AW;H>4(pVj>u{@A{b_Z3%eKC^#tq7Agvkdb?a6Am6oE1Fhyvh&T-hk9?z=bmues z`wlst6Mmoy8E$5*KF& zZ2~nPQq4pmy3r$*s%D!v{|OKCTtTmUEQBe&W}f^zV@oTz*88n{m}K8Prm_`BAP*4; zApkD=6kM+-iAu+|lEiM)_`o+g>}hbaHz$<_ZGD~{O;hf*;L?g=>5||G6Ko_+`0nVM zQm5Nfgh=2M&;vX73X8$boWV)=^+Afzc< z9sFAhidX26{&AC&c732I+qXH}3MZ5~lP8eeeY;+r=Me3Xe<^5Bldt;V48>~dboT0^ zgFpR{TS{pc;C%=XG5dB)z>82lo!i3~f1tA&N|QfSB&f|wr@w)Zr8VV~E672Xla9lS zP??#ciau&#qvCeoUFlB*W1wr*%?tmD+zE~%2QmNU3c(w$m_n)_9vpqYiq1fQjG7z& zshAA;9Z1L1Wlp)+xr=aznjZx_i=B#OXCk|4FX8dM0DUjnlOj!t9k*5`zR?bDh2-#u zP+*5=!7tn3IR~_ zY&)tir@dNd+zj~BJB42)j$#@<-<=P0blk(FyuPj4lmjvZKz#snTn^q8X#Szl{9?|J zqJ^R)K4&GyvD+5e$4Ma}%7j1lE243xjUp`)=627UuG#Ls_A+72CEC6Vy5qkt(0<2? z>1>%f6_X%dybm~2e1Q6{=+@2}^su77Y;gz~cQj2?^s*#MUb|S?O?gCPINTMuC;g?{ z5?4fQcH61N*PC`%V*0)24x6me5H@#0o^@4TXmMon4ITcP-kei3=R&~tXjW{l6?PuL zLCfL$co%7%&5a*@j5XmA)vsF(&*re{YDR>u@#H}q7NXZ&zAX=nIk-+EiXKcf7#-%5 z?m(%DA@R3QLPq~&0uYHL_!yIen1@8zm2%&$YF*}G97>;uM85cbDIZR$?l5O|T*>&A zdcdr7-NuO9J1E}AvF-e2>|vS1d&5HdE#ED3Y;^?l(`KA2SPPLRX%G?MbR-ffp|V z1i^##+^mLea;m>JNP)%*NXA~80onQbIcF1d05@CLPk}o1UsRLexK4C;lpqY~ za5E8C74hu;XSUsnTOr4rLXO4O`LX2~|2-2&h_|mo#-;B~o9hmHrS2D2x`=>``xWDh z6T&S5sSlm9|DlzxmB)l#x%zNqI``aO_ISBY4B%L}+{mYA4pvmtVpmiMUFjXW@>D zr``L32p%cB8UM8r^L)XKz{V=Ib<3iA ziATCGTwKs3kCg3I$oqP;ac=1o3{sdnbP_`dcJHw6S+nr!4Lwzx>VGf6RJ`p}qr?3n zIs&T`oV54Zn~vm__d=-p)$XlsXe553&eiI1%i7ad1XGf-R{1o@m%sb{whY@<%fraC zS3x;Eyrd*D7QCM?YPj`M7bszlz#3LcJ(vYS;DS_ZW{$!K`Rg$1Q3F_zwnKt)DUg_r z*15dRT2mhvU5~tZ%R%8l8|H_p)wZ9xRf>9{2&d<3$v>y4zcdD)9l|)OcUV8 z=yGv6+RA?jIWnz`Ar0v)-Sj)SL&FojcS4y@>2u%B%d%hil&xm2ByfDvVbRae_cKeq zx$cX#3wC4)CbEEpxBUj6?!S8Uhs%B~NB0b!LsxDU^4%lKq?_N#`YBv8oV-fswJ#n- z9Si!&P3Nd|VgixkACQtfYM&4=0Yd;)3T2}(X_{k60YU}&0VUp8Z=P8hoN`U51%ei? zCf5)|r&(s#OkwH#)TVYYXTrlY6+*!SQ_U4O(w&pNR||%+Mg7RFw)dMi235KQuM(y| zm-F%(e|i@5Q%zcvtMf$RXP-5=y!VUScW%#nDMPWr7oKYJ5~JCiy^9IFgBe_ryMTUH zmSXfali4V^BUTVv4;u_$(ySL9an^tE$}iB9Y~*i2SU|fO)o1d^QNBU=s7C&710C!8 z@1#S?UYR^9NfdbwaebZP(j!(*8q$6O2nlS`y6lCP_Q2#|g<0C>yirr{&0|Em5uSVt zP1e2*;hKvp^$Ojrbc_R|y`8Juj{q0a@*Z&s4S+7E1|GUZ*nOEYYf*HH1u1Xm#+r z=svxWRa@mxx{QUJgSibFo%K6{(Z#DT1cnsP&CdlO*wYX`>j{!;$3YE>NCtIP;E#(T zT*8yiX51DXgfBX#!PF^0n63p-Y6-wan=!#NNzw8pm0%Y#ShO4ODpKY4j^7<97>SqT zB+`H=iUe;a!YB9k>e?Ki-m!K?%Th4PMl|j^M>!KL+wJ+v0h1vxO@dt#8scrF%8IP8 z6vPo+lYQ%u-^4VeWw%2>YH!_;XtOqY-S+$8*&0phLVNxzC-=G%uG~%ZAK4rKvNY_#6(g=|V?=i{J#{wanfT@sATBifg{rC~b;k(4b4g zX;8V5yAMqOaC1TlIwW)$BB+v`_mSYJ-JN#KPbDtS%uz$ATY-~I17H1EgZS@sh6HpK zRg4t+X2?k6${|6=meVM4EQm`x1B$UryQKcU=@q?8dpXwywbw-ajj=s@&Lsa@ZB}bW zUbxMicVKuV>GA`68(4>8d31QCb**%)qu+0#SaIl$Yrv18Ir@y9yrxR758rhI=5x4-Ntf=G>$e<`Tr zx*eQ;co3S*EdOcv3YkX*-&}oR<+@z^5-oX6F6g9-;)2u|SLx;ZRf!F$;Z9x(ziW)O zCT+wj^234_ZkbhTmMm6RKOY*Ivp~X{yLnVda1STeM4rLlh(cF!*woY+fB*I|`CD7p<2-BX0?64Q>_+z<_x3wIGdbMqGqnyF@r zElmQ;N&(rUu9!eBScc-egcH!LWj`OZj6zOS9PEHFAGSXve4Mjw`~u3C+4ypHuV!pS zr>goHSJ)ia2<_}2O|B}p?#IlXE!~k>^^SzHWnSxw;BWfxPe^`iWfk8E7H7RehLe0T z{j}B(xhAJ>KS^us`V0imGEooIn*_@Na0Ng*ma+#hGhhNu9TN&gL8lh?_RGx)5rl2z8U8iJT-R!=dn}&*}(#MIH&e+|QMsA5kB53a zw@3|pVcW2YPycSnDHGlxV@Zz{AD2k^@*_2LP$8fOj|UYz55L9HM&^`{V@n-U@$rz1 zSMwuB&_UECCaQz=K8SVqo)SDI$kE~Y)NlBcAZfLvamuUKpbr6+5SZ%5ms2k5%K?;< zNE92?<%lAuqzvD>lXEfU$$u;d7P|W?rZ^=!uc@ZP?{`#%$0+mx>{-n)iv)^TD=05- zKTzVHmDp`pp9N|cgNvGBGz!jO0?Hrs5H{kQW058|0g|FYg1f8lDM&B>gfVGtz`bb4}@4!qUn1(E65t}oRk+h^pywl+`BxGMR zI+qb`i!vFV>ffR8ub=m$yxw@O{V3!We{L`Y>of%472a%|Vz=xi+F{@&ews{h0RZ^# z1TpD@75{##+k1$(BUGX%lrxQSej(`~tL=V-UyPa)nN~$=red3B0t$44}FzTkcg2UPm#V}3p2z79-DnS z{b7GEdW=f|x{IJry5Nx>N{p2#7>FlCyQSoCLt*hV9_vI{ja+ZE>;DfeoMF*HoKwSc zsuNz!XfhG4iV#y(5S0QtN~{>&>-M)mi|mh5U&4;;T~b*&Fpagx;a{k65(2R$%%mX+ zl+sV(agzGR&s!gV7ITBeZ{EWopy?m>)oDiM>V){sm7r0$bqjnW=3>f*UH~PycsMMH ze)mz(01B)n8cK9phvi7>{&yO>)b`g2CJrr9-i)3kDYS(b^%GvXjz>J`|LH>S!m* z8rd=+Z0b;C(6=(Iua9RoR_=#_~EmHz6)z`+z(Wyf=bE zgOF&=8%>bpGF@5xqBzO)SedDO0(?$!O;-g(W3^Lh2pR(-0f4ChW@SiV)YkEYFj8Jv zwD>0rtd*s=v&?$d#^P;yna!d3lp`R@`a$UYkv!72=VVj`lX} zgNA74M|NqT!S|rDpS%}Vobn?-d|8XX%beJ2Op?MRzqV_V zPHzOhmzx0QwblIhBr5^56)kav6cwaF*NU0R@` zV!^a!;fv-r%reG3HPNqax~*El^UuRXcMRgqj9#j<>4C;`hl$&>d%MW+Yn!kU7l$-( z#A&y1N84Qc@RA8`zXf!p$Sn5t9r-d^Me(ILE&Rh7E=-PzPrbFl5Ru`TizbL%?rUMPj4vu{RoWg_}y3G*Y!dur}} zEW{K@CvVuQl%arVznHb|d$-J%^zjVPBINtNA-p^J=V9V0W`1KJV?-9&_pN!&DO~G` z&Jlq4;Pa^N_+)^`rQ^B5f9JEr;!@|MzvPn*wBbrx-AO!>#{)31Q|k(@pSe~1g{o|pG_Y|_w-n&?_gVk%U%_5@ z_{ghk`Lq+Rv=k}5eV*Kqr`gA=?Q;K1s7Jnl7BEnjdO5DkY9Y%FVqYs@Ydu|gU_|*> z;DMi(>=%MB^EKETa)N*^1-ubP<<#ZLuJo>( ziFm~#uk;I+2!282J-lOTRjgwJOcs=*f_Kf@y=hA79knkD zmQ`D8fH$*Hq4<=m`nz2Bf)YiPkLTL=Bp>6QO?fp3`5S}b3(IW!9-I`IvJ?f6W(CKM z2J>GUgMMu-oAB+_N{kru>wcXTm16ere2yy518#OX*TMXCIybW zS@!^>OlP8+SsdjwSQqF-Kd|;OI>S%q!LOG3+0Poy8MpE%s>@OP zn3GQ(m?wYD4gBL7ApI>C)IaV4Sfy96@GfAu+XFS>X#al_{=N;s??9A9<=tSinhNia zX~jpk4G=r4mgVwUr{!@^rAmg(uBG44DbtNOms`Cj=kg4e{bl>Ve79`1!4jT2KO~q) zGjx%GN0Q(*Ey@WLL=7I%DhqFo9p`*$$l&zY$YuMu+B$B|*``$+Cn~4-@jmZk12VduJNkLf@0i58tM3t3y#X`=iYwP9WhG^_A^s)ZAL}6aK{G&_c=ps zmr8WT`=J`guN7FmT8M~)9)JAi&ox6}yoKmpNketrX-xnabn2h05doaIZgXoX?Csaf zqLQbVXQ*%fSdbq2U|wK{L0g%(nsX6 zl9uxpwI2HYyKf&}xDrn8@EDmLl{RnHbzGCndQz32Mf7UO$h$8eyfU#LkmcT7p;iuY z4A9EKNZa=70v!X)FQY~K=bswPMCY6 zb*!lwY>k!c4ykqwMPP7-?Jqr2Lv6T($ju4NTTLK@_E$R!c@T)g6AP#&Fwxd#6OKz^ z6o`jRDAVVZ%miha+xjn=jIzh!Dn0Bf&Io!2XUYw6prbbzIqh8r&3HIeG6npPmGYUP zJsIKE*nYThOzgFH#f;4(uK zz@~~D_>ruv&Cts^($N*exn8=fBk0mM3xoh#iL_FVZY5t+x!&&5dLT(=x$d+jr`~)? zMlZ&zQ+=h|6irSh3n5&*ki(YJ7=Lpf;s}3j)T7+oD^-3DxX^@fH>svL;SSajI(C3G z&qgXFQ9zuxlPlizi_>PljI5t1_H#rgT2==LVlsEe3u_=@kgLs_@=bErr`w>6@Od?u z1P-Uw^0Cq;VEgiD+_ z2+2u@W_1+Jp_T(r2_JcC950`n?nJ$PwwLa;*1YF=*41rUjzek0&GgatbiLq5AzZR= zM<)N>NJGmjg@{6VUCh(C)Dce?P)8l#H;}@>w>L*|KEd$|L7e=j11jG8f+h+B=z3}U z6)VXpa%Y5rY0^;!AGo6Rp-biY)wCH7eaP$xcalUjC{gskE0INn=!JCT<2+Y4Hu~{B zzO~y^XNyOiU5RSLUE-lw3*TIV!S{fC;POkPtX$%J@Rf%JJC%`lyN5Vq*tXFet1WGs}k2ma?#uNX?DDi?HS#jnw#=P^|YG2VHw`PEPY`fplLJ%W8v=*&p#kIRynTq zkgj6WY*2Up+1IPzT8l{eYXBtc#Vxb2NrXY@J4c?3N7u`D9EO}tF$olK_GV@{X4)0G zyr$SI*r}|ul;)jCs=6*chB*KJDnA!5R28Clo-O|>pBG<9P0G&a?fZR`*c zCg_!{estWp#xe(dk#`~8~D-M2CT})ltRE3Y3mOe zao+pPXMXs*nSbABRPok3gNAQ>^yIjd{6~<(V92%r_rd7W(8PG33*?4TarSkH9crw> zL`1G0X?n*5)@Ertyr*vdT%&~dX+yeP-WF^`*nZ*aP{u_yf(rY9Ch&lgsSuh7y!aygquT2c|-~bj)szPD*|67j!TOgionzl~{lm z_S4XnAYztiw#0aw)N{-lU#6pjL3s(o!gP56tI%|=sYBwAWsR+QZUIP$N`QOF;a;Mf zGBd^`q4~YSIF;lRI^|*|; zoB>2(f$la{UgnJ&se9>11buV334%=o*g9_UC`l+ZQy>~nE;TMeRU$Uz$kwT-*l()d z|KtJ+8oEDM@e_DzrZDA12w{SDa(1;Fygk98VUgE^8cg;~=*db| zRp%ITVWq!#7<@@<3CQ6y@Vp=?toVoRt+SlgChyGwU`=Wn98-SvQD`|;*gOZ*1scro zQHRNTJR7}*8f;9qcHh(#7-tE9zO`;kRG)SUDRiRG?C!`vOH)cVK()N{RUbP3^E7WW z9uizbvm_KM1r%?3HzYF=@&TrBx5!@3yCe=nO6+gl?`zZO{>Qs*z6MPZ%J@3QxJQEu z=GBMvyT8>WZGvf_piTf3PXi+QzjX$lP-VZyS?S^sFa3O#r1E-f%F53VVyn_ark;K% zC_R*$(^q;uT;SkUMaWBeSGx>=XM#iq&_m+7&fnxS6}95X$@+P~Aox>PBWDnQWB2Ln z$u}(RHWqZEJdJ%v8RuOmOq%Qlz8vtK^-_DgNQ?MelUS;$Eu6&3;ei5)PKUui#(fL6 zaQj&FQvW5Mdtf3~?Bg3dfO8XnH%ME_oe4jyafeM zt63<@Y+?;YzHDZ8-l$!*KW*4YozG=!X&b>lvX^?pjmP5FWw3e^oHPJf@V~trfs~1} zW93$plqfOlz#^s4E3Kmr36EQ z3SL}O>3wnA;L!Wr*_lWhUEa_ic^ot3R|j4*KRy@kICC}YIh z@?1jK?UhT7cXI^Z2hKhGbj~mDtY9~;3r_;F*t^0b_um1aw2Q#4lkA_5ukGPuazINP z{fl=0)Cx7k3GTQ z?2?rdqn_p;lz9mDhb4h)Ux+@-vUxdXrn%`o(ilj|h{9EgcZ}$E+!Yt=P1s0u5KmH) z*z>vXVvJrRX6=Kt8DJw!=t(9-xLNb}m`koBq&zX802Y8O1JAfCJuemXD;I1CjQ+de zV4-W*?gIKcFWh?@%X?WzNH+6EeJY*=a+M1g-pf3EnK`6ez}!hylm!(8s#Om=eyq#_ z_7|uN@Z|(TL{B@iO@?jUg0eXg5>>j%&9VupJHZ#_D63h(ZtH08I0wHLK`xn-4-Sb? zM=hBd@S!!I-4U>ps$_%nzH}$-)eenwNx&W}5d2celHk+YuV#Xf;<{`a|HBD97U;Pq zEKSWXOb6YU3%M~X{>B{uCj*8BOC{tBD4~F&Y#_J4TEtTJhi1n@=hNggS$RNqJ%ljX zEQSsZfM%bPIm}&77m_9vWvz)<^?)4)UFQ;#s~q`N*V4G1k9ZHn;~0o>2E=L)vSiIa z;zZ@%xKudi;y3B-h4*=xh?A}?78!PJJ{pGpDn5F{dGzwTO2R$T^M%^5_8#DvGl7`x zObI;z`=}?HrdKNEc`>U6n2``C(p1pUsQVXQR;;V2u~+H}lf69eR{=b$eY=omLZ$pF zQwF>PZ<~milr8AL!xZ;fLH9^|B&?m*hY}!+ZxU!9$<1=o2vft>o=opKsMi^AK4{04pz zhu@8k?N{GVUd^mda!(eyJqGxrm+x`|KI-Q;?dz?UDJ^n5ul<9|Su@9c1sYg|ayco!QGH%~-M!A#|1w#>>8^ei!RF## zND^K);_Eq)g1gaNJlWG9l22m!Oh{nSJF?<{GO!mWu*a-s$Jno0>Rv$ZAgH*<|?$v65k;J_9V3C#y|_JibhDJ5(K7g8-{7V?rR6!UFGk&nr;Mt$L&} zE!S5<=$(R%2=5*xqAaIfY}a5N&mZBA*ub_*>__>gjJ6w@lRa+9O>F!}0Ou@_KA-(G z6!(6%^dvBMXhWvu7Fg~Ev9{bMQn+~RXyO^H6?rIV{O5K@bvUE+BeQQPj6ff@fJN#SJ|etis7Ru}5leL759P;92b zm3+Y~lPb(=a0LjsvMjuc@ir`|g2IDA?>??y7Z7$30}_O7yhZ07?_L}be){o2H}GM{ zJ**tFPKfkS`2OARAx-p<2o-F{(5x=>(4Bro@brY<1(QdLEFdons6~4u_k=I3#`rq) z@vql<(wWRD<5AWB+hB z_n~Od|A5WDZrr9PE?KZIw#U_%jXZx37;*Q=Nel{CMOhCSwz2!1mA zL^XRr;T~9RhM}rs>Lz+eL(56`zNLYt^LV`52Fir3hSAiYxo1RFL??u}Lu(xv~abZh<5ji z=tTpcB-p>6`W4@C$ElWD(be<)To*bf9n3YqPK*!q?4ZRKy_CbMw41`oCWp*(wke3<~tAze8Qv1n{AWD zOO^FIhTYA>$K`ySKK^wzAZm1K-(Aof_L%Dy0fNa%&r2g?NhFBtL%IKION8(MVCP+r z{^1{YlkUODJcAeigpJ^;l*duBH*bN&h(Pv=NrJ_vS-&Z@>it=|*xUpAQ*gLO+y0~W zftoMFbKx6v6;NlJ1EKMZ;}ye^Gizzec!CKg<#}b{rL_A3_VZQspf@XRM(33Y{qye& z%vAPjVfU%K0g!y3gG1gSYZ0K1!Xj{S^OE3orDxK}?M)^?VdkE(~4PlrW`2Ou8Xf#jU2) zR<{|bF9z&)wwy!0uig6bg9zL)jY|D5#jlFw)Yd<%1ww@2lc|Rj87h-o#=!1)U