pax_global_header00006660000000000000000000000064150156730200014511gustar00rootroot0000000000000052 comment=6643492c70a47672dfaacc5a65e71ca1f763e6ad slides/000077500000000000000000000000001501567302000123405ustar00rootroot00000000000000slides/.gitignore000066400000000000000000000001031501567302000143220ustar00rootroot00000000000000build __pycache__ slides.spec slides .dependencies-installed .venv slides/.gitmodules000066400000000000000000000001121501567302000145070ustar00rootroot00000000000000[submodule "btui"] path = btui url = https://github.com/bruce-hill/btui slides/Makefile000066400000000000000000000014411501567302000140000ustar00rootroot00000000000000PREFIX=${HOME}/.local all: dist/slides/slides dist/slides/slides: btui/Python/libbtui.so slides.py .dependencies-installed source .venv/bin/activate && pyinstaller -y --onedir --add-binary btui/Python/libbtui.so:. slides.py btui/Python/libbtui.so: btui/Makefile make -C btui python btui/Makefile: git submodule update --init --recursive virtualenv: .dependencies-installed .venv/bin/activate .venv/bin/activate: python3 -m venv .venv .dependencies-installed: requirements.txt .venv/bin/activate source .venv/bin/activate && pip install -r requirements.txt && date >.dependencies-installed install: dist/slides/slides install -d $(PREFIX)/share/slides/ cp -r dist/slides/* $(PREFIX)/share/slides/ ln -sf $(PREFIX)/share/slides/slides ~/.local/bin/slides .PHONY: all install virtualenv slides/README.md000066400000000000000000000013031501567302000136140ustar00rootroot00000000000000# slides - A terminal slide presenter Slides is a simple terminal application that uses [btui](https://github.com/bruce-hill/btui). ## Running To run the slides program from this directory, run this command to set up a python virtual environment with the necessary requirements: ``` make virtualenv ``` Then, you can view a markdown file by running `./slides.py sample.md` ## Building If you want to create a standalone executable that can be installed, run: ``` make ``` ...which will build a standalone executable file called `./dist/slides/slides` ## Installing To install, run `make install` or `make PREFIX=/usr/local install` if you want to install to a location other than `~/.local/bin`. slides/btui/000077500000000000000000000000001501567302000133035ustar00rootroot00000000000000slides/genji.txt000066400000000000000000000010541501567302000141750ustar00rootroot00000000000000はじめより我はと思ひ上がりたまへる御方がた、めざましきものにお としめ嫉みたまふ。同じほど、それより下臈の更衣たちは、ましてや すからず。朝夕の宮仕へにつけても、人の心をのみ動かし、恨みを負 ふ積もりにやありけむ、いと篤しくなりゆき、もの心細げに里がちな るを、いよいよあかずあはれなるものに思ほして、人のそしりをもえ 憚らせたまはず、世のためしにもなりぬべき御もてなしなり。 slides/requirements.txt000066400000000000000000000001401501567302000156170ustar00rootroot00000000000000climage==0.2.2 marko==2.1.3 Pygments==2.19.1 wcwidth==0.2.13 pyinstaller==6.12.0 pillow==11.1.0 slides/sample.md000077500000000000000000000064071501567302000141550ustar00rootroot00000000000000#!./slides.py # Slides Demo *by Bruce Hill* **slides** is a terminal slide presenter. You can see the slide number in the bottom left corner. Slides are separated by three or more dashes on a line: `---` To advance to the next slide, press **Right** or **Space** or **J**. --------------------------------------------------------- # Slide Navigation You can go back a slide by pressing **Left** or **Backspace** or **K**. You can go to the *first* slide by pressing **Home** or **H**. You can go to the *last* slide by pressing **End** or **L**. You can also type in a slide number and hit **Enter** to go to that slide. --------------------------------------------------------- # Basic Features This is **italic**, *bold*, and `inline code` The paragraph continues on line two. - A list - With items 1. and 2. some 3. numbered ones > Block quote > that spans multiple lines Here's a shell example: ```run ls --color=auto ``` --------------------------------------------------------- # Demos For runnable demos, you can use ```` ```demo ```` and press **Enter** to run the demo ```demo seq 1000 | less ``` Another demo: ```demo for i in `seq 5`; do echo "$i" sleep 1 done echo "DONE" ``` --------------------------------------------------------- # Syntax Highlighting Some code: ```Python def sing_bottles(n:int): for i in reversed(range(n)): if i > 1: print(f"{i} bottles of beer on the wall,") print(f"{i} bottles of beer!") print("Take one down, pass it around...") elif i == 1: print("One bottle of beer on the wall,") print("One bottle of beer!") print("Take it down, pass it around...") else: print("No more bottles of beer on the wall!") print("Go to the store, buy some more...") sing_bottles(n) ``` ```markdown This is some *markdown* text with **asterisks**. ``` ``` Some basic emojis: 🌎💨🔥 They do render correctly with appropriate box size (But your mileage may vary, depending on how your terminal renders emojis and what font you use) ``` --------------------------------------------------------- # Embedding files You can embed files with the syntax `![title](./file.txt)`: You can scroll up/down with **Up**/**Down**, the **mouse wheel**, or **Ctrl-U**/**Ctrl-D**. ![Tale of Genji](genji.txt) ![The source for this presentation](sample.md) --------------------------------------------------------- # Images ![](thumbsup.jpg) Supported! (kinda) --------------------------------------------------------- # Re-running Code You can re-run code by pressing **Ctrl-R** or **R**. It is currently: ```run date ``` Some random numbers: ```run openssl rand -hex 16 ``` ```run hexdump -n 128 /dev/urandom ``` --------------------------------------------------------- # Inspecting the Source You can inspect a slide's code at any point using the **Backtick** key `` ` `` ```run hexdump -n 128 /dev/urandom ``` --------------------------------------------------------- # Get the Code You can get the source code [at the github repo](https://github.com/bruce-hill/slides) (links are `[link text](url)`, press **Enter** to launch) --------------------------------------------------------- # The End Thanks for your time! slides/slides.py000077500000000000000000000442761501567302000142150ustar00rootroot00000000000000#!/usr/bin/env python3 # # This file contains a simple test program for demonstrating some basic Python # BTUI usage. # import PIL import btui.Python.btui as btui import climage import marko import os import re import subprocess import time import webbrowser from collections import namedtuple from marko.helpers import MarkoExtension from marko.inline import * from pygments import highlight from pygments.formatters import Terminal256Formatter from pygments.lexers import get_lexer_by_name from pygments.util import ClassNotFound from wcwidth import wcswidth Slide = namedtuple("Slide", ("filename", "text")) class Demo(namedtuple("_Demo", ("element", "demo"))): __slots__ = () def __call__(self): self.demo() FORMATTER = Terminal256Formatter(style="native") BULLET = "\033[1m\033(0`\033(B\033[m" terminal_width, terminal_height = 0, 0 highlighted_element = None def render_width(text:str)->int: # Strip out escape sequences that are used: text = text.replace(BULLET, "*") text = re.sub("\033\\[[\\d;]*.", "", text) text = re.sub("\033\\(.", "", text) text = re.sub("\t", " ", text) width = wcswidth(text) assert(width >= 0) # Can happen if we missed some escape characters return width def is_image(path:str)->bool: return any(path.lower().endswith(ext) for ext in ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp')) def boxed(text:str, line_numbers=False, box_color="", min_width=0): text = re.sub("\t", " ", text) lines = text.splitlines() width = 2 + max(render_width(line) for line in lines) + 2 if line_numbers: width += len(str(len(lines))) + 1 width = max(min_width, width) rendered = [box_color + "\033(0l" + "q"*(width-2) + "k\033(B" + "\033[m"] for i,line in enumerate(lines): pad = width - render_width(line) - 4 if line_numbers: num_w = len(str(len(lines))) pad -= num_w+1 rendered.append(f"{box_color}\033(0x\033(B\033[0;2m{box_color}{i+1:>{num_w}}\033(0x\033(B\033[m {line}{' '*pad} {box_color}\033(0x\033(B\033[m") else: rendered.append(f"{box_color}\033(0x\033(B\033[m {line}{' '*pad} {box_color}\033(0x\033(B\033[m") rendered.append(box_color + "\033(0m" + "q"*(width-2) + "j\033(B" + "\033[m") return "\n".join(rendered) class TerminalRenderer(marko.Renderer): width = 40 relative_filename = "." def render_document(self, element): return self.render_children(element) def render_children(self, element): if isinstance(element, str): return element # Avoid treating it as an object return super().render_children(element) def render_heading(self, element): title = " "+self.render_children(element)+" " if element.level == 1: #return "\033[1;34m" + pyfiglet.figlet_format(title, width=200, font="big").rstrip('\n') + "\033[m\n\n" line = "\033[1;7m" + " "*TerminalRenderer.width + "\033[22;27m\n" return line + f"\033[1;7m{title:^{TerminalRenderer.width}}\033[22;27m\n" + line + "\n" else: return f"\033[1;7m{title:^{TerminalRenderer.width}}\033[22;27m\n\n" _list_depth = 0 def render_list(self, element) -> str: lines = [] if element.ordered: for num, child in enumerate(element.children, element.start): child.bullet = f"\033[1m{num:>2}.\033[m " child_rendered = self.render(child).strip('\n').replace('\n', '\n ') lines.append(child_rendered) else: for child in element.children: child.bullet = " "+BULLET+" " child_rendered = self.render(child).strip('\n').replace('\n', '\n ') lines.append(child_rendered) return "\n".join(lines) + "\n\n" def render_list_item(self, element) -> str: indent = " "*self._list_depth self._list_depth += 1 result = indent + element.bullet + "\n".join(self.render(child).strip('\n') for child in element.children) self._list_depth -= 1 return result def render_image(self, element) -> str: path = element.dest if os.path.isabs(element.dest) else os.path.join(os.path.dirname(self.relative_filename), element.dest) if is_image(path): img = PIL.Image.open(path) img_width, img_height = img.size render_width, render_height = img_width, img_height two_thirds_width = max(40, 2*terminal_width//3) if two_thirds_width < render_width: render_width = two_thirds_width render_height = int(img_height * (render_width/img_width)/2) two_thirds_height = max(40, 2*terminal_height//3) if two_thirds_height < render_height: render_height = two_thirds_height render_width = int(img_width * 2*(render_height/img_height)) option_string = markdown.render(element.children[0]) if element.children else "" for w in re.findall(r'width=(\d+)%', option_string): render_width = int(terminal_width * float(w)/100) render_height = int(img_height * (render_width/img_width)/2) for h in re.findall(r'height=(\d+)%', option_string): render_height = int(terminal_height * float(h)/100) render_width = int(img_width * 2*(render_height/img_height)) output = climage.convert(path, width=render_width, is_truecolor=True, is_256color=False, is_unicode=True) if element is highlighted_element: output = output.replace("\n", "\033[33m*\033[m\n", count=1) else: output = output.replace("\n", " \n", count=1) return output + "\n" try: # Embedded file: with open(path) as f: contents = f.read() except FileNotFoundError: return f"\n\033[31;1m\033[m\n" extension = path.rpartition(".")[2] if "." in path else "" try: lexer = get_lexer_by_name(extension, stripall=True) except ClassNotFound: code = contents else: code = highlight(contents, lexer, FORMATTER) title = self.render_children(element) or path heading = f"\033[1;36;7m{title:^{TerminalRenderer.width}}\033[22;27m" return "\n" + heading + "\n" + boxed(code, line_numbers=True, box_color="\033[36m", min_width=TerminalRenderer.width) + "\n\n" def render_link(self, element) -> str: title = self.render_children(element) or element.dest return f"\033[{'1;' if element is highlighted_element else ''}4;34m{title}\033[m" def render_emphasis(self, element) -> str: return f"\033[3m{self.render_children(element)}\033[23m" def render_strong_emphasis(self, element) -> str: return f"\033[1m{self.render_children(element)}\033[22m" def render_strikethrough(self, element) -> str: return f"\033[9m{self.render_children(element)}\033[29m" def render_code_span(self, element) -> str: return f"\033[1;32;48;2;40;50;40m{element.children}\033[22;39;49m" def render_raw_text(self, element) -> str: assert(isinstance(element.children, str)) return str(element.children) def render_literal(self, element) -> str: return self.render_raw_text(element) def render_code_block(self, element) -> str: raw_code = element.children[0].children if element.lang == "run": lexer = get_lexer_by_name("bash", stripall=True) code = highlight(raw_code, lexer, FORMATTER) output = subprocess.check_output( ["bash", "-c", raw_code.strip()], stdin=open("/dev/null", "r"), cwd=os.path.dirname(TerminalRenderer.relative_filename) or '.', ).decode("utf-8").rstrip("\n") return boxed("\033[33;1m$\033[m " + code + "\n" + output, line_numbers=False, box_color="\033[33m", min_width=TerminalRenderer.width) + "\n\n" elif element.lang == "demo": lexer = get_lexer_by_name("bash", stripall=True) code = highlight(raw_code, lexer, FORMATTER) title = "Demo (press Enter to run)" if element is highlighted_element else "Demo" heading = f"\033[1;32;7m{title:^{TerminalRenderer.width}}\033[22;27m" return heading + "\n" + boxed("\033[33;1m$\033[m " + code, line_numbers=False, box_color="\033[32m", min_width=TerminalRenderer.width) + "\n\n" code = raw_code assert(isinstance(code, str)) try: lexer = get_lexer_by_name(element.lang, stripall=True) except ClassNotFound: pass else: code = highlight(code, lexer, FORMATTER) return boxed(code, line_numbers=True, box_color="\033[34m", min_width=TerminalRenderer.width) + "\n\n" def render_fenced_code(self, element) -> str: return self.render_code_block(element) def render_quote(self, element) -> str: return f"\033[34;3m{self.render_children(element)}\033[39;23m" def render_paragraph(self, element) -> str: return f"{self.render_children(element).strip()}\n\n" def get_demos(element) -> list: if isinstance(element, (marko.block.CodeBlock, marko.block.FencedCode)): if element.lang == "demo": def demo(): raw_code = element.children[0].children subprocess.run( ["bash", "-c", raw_code.strip()], cwd=os.path.dirname(TerminalRenderer.relative_filename) or '.', ) return [Demo(element, demo)] elif isinstance(element, marko.inline.Link): def demo(): webbrowser.open(element.dest, new=1) return [Demo(element, demo)] elif isinstance(element, marko.inline.Image): path = element.dest if os.path.isabs(element.dest) else os.path.join(os.path.dirname(TerminalRenderer.relative_filename), element.dest) if is_image(path): return [Demo(element, lambda: PIL.Image.open(path).show())] elif isinstance(element, marko.element.Element): if hasattr(element, "children"): demos = [] for child in element.children: demos += get_demos(child) return demos else: raise ValueError(f"No children! {element}") elif not isinstance(element, str): raise ValueError(f"Not an element! {element}") return [] markdown = marko.Markdown(renderer=TerminalRenderer) def show_slide(bt:btui.BTUI, slides:[Slide], index:int, *, scroll=0, raw=False, demo_index=0) -> int: global highlighted_element slide = slides[index] with bt.buffered(): bt.clear() if raw: lexer = get_lexer_by_name("markdown", stripall=True) code = highlight(slide.text, lexer, FORMATTER) for i,line in enumerate(code.splitlines()): bt.move(0, i) bt.write(line) return if slide.text.strip(): TerminalRenderer.relative_filename = slide.filename ast = markdown.parse(slide.text) demos = get_demos(ast) highlighted_element = demos[demo_index].element if demos else None TerminalRenderer.width = bt.width//4 rendered = markdown.render(ast) TerminalRenderer.width = max(render_width(line) for line in rendered.splitlines()) rendered = markdown.render(ast) lines = rendered.splitlines() width = max(render_width(line) for line in lines) height = len(lines) x = max(0, (bt.width - width)//2) y = max(0, (bt.height - height)//2) - scroll for i,line in enumerate(rendered.splitlines()): if y + i in range(bt.height): bt.move(x, y + i) bt.write(line) else: width,height = 0,0 pos_str = f"{index+1}/{len(slides)}" bt.move(bt.width-len(pos_str), bt.height) with bt.attributes("dim"): bt.write(pos_str) return height def draw_time(bt:btui.BTUI, start_time:float): elapsed = time.perf_counter() - start_time bt.move(0, terminal_height-1) with bt.attributes("dim"): if elapsed >= 3600: bt.write(f" {int(elapsed//3600)}:{int((elapsed % 3600)//60):02}:{int(elapsed % 60):02}") else: bt.write(f" {int(elapsed//60):2}:{int(elapsed % 60):02}") def present(slides:[str]): global terminal_width, terminal_height redraw = True index, prev_index = 0, None raw = False scroll = 0 render_height = 0 search = '' start_time = time.perf_counter() with btui.open() as bt: terminal_width, terminal_height = bt.width, bt.height key = None while key != 'q' and key != 'Ctrl-c': if index != prev_index: redraw = True scroll = 0 ast = markdown.parse(slides[index].text) demos = get_demos(ast) demo_index = 0 if redraw: render_height = show_slide(bt, slides, index, scroll=scroll, raw=raw, demo_index=demo_index) draw_time(bt, start_time) redraw = False prev_index = index key, mx, my = bt.getkey(10) draw_time(bt, start_time) if key is None: pass elif key == 'Left' or key == 'k' or key == 'Backspace': index = max(0, index - 1) elif key == 'Right' or key == 'Space' or key == 'j': index = min(len(slides)-1, index + 1) elif key == "Up" or key == "Mouse wheel up": redraw = True scroll = max(0, scroll-1) elif key == "Ctrl-u": redraw = True scroll = max(0, scroll-10) elif key == "Down" or key == "Mouse wheel down": scroll = min(scroll+1, max(0, render_height-bt.height-1)) redraw = True elif key == "Ctrl-d": scroll = min(scroll+10, max(0, render_height-bt.height-1)) redraw = True elif key == 'Ctrl-r' or key == 'r': redraw = True elif key == 'Home' or key == 'h': index = 0 elif key == 'End' or key == 'l': index = len(slides)-1 elif key == 'Ctrl-z': bt.suspend() redraw = True elif key == 'Enter': if len(demos) > 0: with bt.disabled(): bt.flush() # Clear screen and move to top: print('\033[2J\033[H', flush=True, end="") demos[demo_index]() if demo_index + 1 < len(demos): demo_index += 1 redraw = True elif key == 'Tab': if demo_index + 1 < len(demos): demo_index += 1 redraw = True elif key == 'Shift-Tab': if demo_index > 0: demo_index -= 1 redraw = True elif key == '`': raw = not raw redraw = True elif key == "Resize": terminal_width, terminal_height = bt.width, bt.height redraw = True elif key in '0123456789': bt.move(1, bt.height) with bt.attributes("bold"): bt.write("Go to slide: ") index_str = '' while key in '0123456789': bt.write(key) index_str += key key, mx, my = bt.getkey() if key == 'Enter': index = max(0, min(len(slides)-1, int(index_str)-1)) elif key == '/': bt.move(1, bt.height) with bt.attributes("bold"): bt.write("Go to slide: ") search = '' key, mx, my = bt.getkey() while key not in ('Ctrl-c', 'Enter'): if key == 'Backspace': if search: search = search[:-1] bt.write('\b \b') else: search += key bt.write(key) key, mx, my = bt.getkey() if key == 'Enter': for offset in range(len(slides)): if search.lower() in slides[(index + 1 + offset) % len(slides)].text.lower(): index = (index + 1 + offset) % len(slides) break elif key == 'n': # Next search result for offset in range(len(slides)): if search.lower() in slides[(index + 1 + offset) % len(slides)].text.lower(): index = (index + 1 + offset) % len(slides) break elif key == 'p': # Previous search result for offset in range(len(slides)): if search.lower() in slides[(index - 1 - offset + 2*len(slides)) % len(slides)].text.lower(): index = (index - 1 - offset + 2*len(slides)) % len(slides) break if __name__ == "__main__": import sys if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} file1.slides [file2.slides...]") sys.exit(1) slides = [] for filename in sys.argv[1:]: try: with open(filename) as f: text = f.read() if any(filename.endswith(ext) for ext in (".slides", ".md", ".txt")): if text.startswith("#!"): _,text = text.split('\n', maxsplit=1) slides += [Slide(filename, slide.strip()) for slide in re.split(r'(?m)^\-{3,}$', text)] else: extension = filename.rpartition(".")[2] if "." in filename else "" slides += [Slide(filename, f"```{extension}\n{text.strip()}\n```")] except FileNotFoundError: print(f"File not found: {filename}") sys.exit(1) present(slides) slides/thumbsup.jpg000077500000000000000000001357261501567302000147320ustar00rootroot00000000000000JFIFC     C   D }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@IbE &Z_Z*#ЛQ3C%!T2Z (E!Aₕp8SqHoQQCҁe@sQrX?1 [rEH!CԒ.MV4Z-H8 BІ4-J@F19nZ{JHZvbF㷸C7+DB: 39fZj3ҽzJ=Ng&iBDHv1m8">jnc' }h͋Q&ߓT(95.E4cUб~̛ :d[,JCp"w~~͡$=E?!r ;qOh^ȒI+Vl~ͣ^kevSXM<4OҰ9#EHAi&בJda gs~ķHX[gwj⩏] ;!¸gjW=rT]Y[6rooj73H.I8ǵj锢 бZnO"5t'q~*e";C{ |W L[ab׋!ZTVef̹2nTfMSE9>*iĬǧ2su>Dez]FwKRw_jZv0nO}~ۥ5tCt9>3K IarTv>/i!ׯ!)7Q-q㫭Z2D:(+ 03ַ-OQd\I#EMJҧn:Sj;KEY-qO\r^M?2@u]MvbP{}+'l[md+isɔc˫YʸP6G Z}f&rV/T#XT99BVZ,(ҴrF2ꆪ`38/>iP/ZD&!{s6vޕ5aQ MrI^yq6zDקҳl,ƽ+w,=YTRZE`9ކ'Uҧ}J9vg<{,gGt/s̭ҫ:_C/YWn59ϙp**/ :q_Mݟ{^ MY4J) NFBgc2*[i zvܪ&|l,PG]5o)9VVFMbrhpyPI#UvF@pZ3ٿ ?48xJDEj?7t+ r6ZYSA%n\sQ!B=+;>M) fe`cBLPpsCJ\i\ Ua@cd`N9B럭sB2!(f[) U9I0ha>cz Z,tܗxE+iC*9kc1#!qR OlJ{1D|/+K(FO&Hn1R; Ҙ4 aq@}8v($c9U?ʁ~0 ֋ )B;.a5q8u7$ q67iء}OCxin_nqN$c^ 撹h$j]ɱ&>w&ǟH ͽKGOv-o>Մ u$V#/W8^jJC#Wzd6K%,?Փϩ?#&쎀<+GVʛDvG#yN)6%ypֱi1v) !6wa?>ddkD"~Z|zVjѕ"V[i7a{8'Jǃ;L6IfգVV${4`n[Nnfg|' }N(,]U*me#ZΔERɎ5k?9)ҼX?9h$r<1WoG{[@zW]LK6gd@-wv<њu{oPqZv9]MOF]ROr(yD_@u,O1B6!F`r$]&3fYǛ=V\2dz9mOΉ*!E\ZGÚ׸8R9˴mt&*2zgFC'P+FNZje-Ȉl0sMhn34!2!$!Oq;mŇ4 * ֕)ˏ™ x -kDu69Cce vyiI81 =2: H[<4Q)w\7njр'AHos jpԆ8*#٣Y4nYn;F>RxOf{?N`@UZޜ"rTZ\ : ̘$ssI(;s)>c&ٳjR1)5.KKXHo1r:K x! #ҞIP yn+&E }rFEKc2JIlZR2={1j\dj7Qj{]_G7_!5X=LQݞQ[QGW˥<51\q E} 'qJ-+Dr`0? ͛>Li{H0vɮue5x ~;y7F[88LFφxM}}⮙{8r2k(ccJz#{XIW (=H\֚2>L&VMH'&Z&| \RzVܶ kGqԧ&TY)$cTYqڴ1L #kO?ZZcPg5fI[6WVbbBx4!e8whdqCe\gGBWg5bkD̐= PG= 3ސ-94&(Z0xv%XAr Dc OoJ8Jbb @t$ېhUƶBUrgHy桚"dn)n<  60iX= x6d8) &CqAB'֐ g~u@isցx~tۭKg6g?0@~Hcvθy'oVyycfonN957*^Y!pyNJ76aծ%~l։tVLniSUn;zѵ k3vs<,5H}7_(k)lgWg|5s]8x_>FV3<ܜzڸiGt1ϝ۹y-3U =km!:M]Ԧ? LѐrT_ qz5i\.iẕV'T36GhCz;EfݒUy%~ :!"MI 3z֑ܦ9I"S`TH%iN#mSډ- 1Yc&!ɭd-j6a'"Hi$zS4E#XI0Zy:SV 9 dL؁51cG\ ) ON39V,8b hL N酆sӸMjKTSd:wıqR˒i)A@uMĉ~_zqNi3j""2O ly})\ ס0l9M"O<{Qq+0=1T@[<P[>#Hxb`}j\4$j=)sh ${-"`*0hUZ=t83g }Ubc~>$ZKJ9!2)De@UP8W_p5F NqkUH9ëTsۃv@O=jnR )\\wU'KakO&Ɛw c'=3PEfՓ[Mv#fw1f:u>sV5gx~$`W9)R:Tw](GNͤVE2;m>u9c8Z pęm y5.$a;M-Zrn gY0N6>ҵI;ZmȤq:WH!H̙Y >Ԩ ŝ8jZjpARI=ss(6y_(bF^kx?Dhɱ][ݤ~AzTѰՊԐ˕+SZGgg~Ͼ-ALCrqifuc붹2M'/'\gnT6V!jl|tc iE\͔bTPI:U3\k Kw(C4E3Y$9KA ɦ QAR)!p8[7Hq]nKAA c=MQ H w>6$'X>75i&lRxXf2"Q7%Ul8]qGObP;#[M"BXZ! 48cҪbc>Z0{XsLDN=j$FВ4li V)Y.ޠ։!q$c4Ib)ث xH1M R`CPX}E 3IlMDIܦ;_-q[ qִ6v=#᷃}b!)myԨ\&o%ƒcw|ʅ|ašFSir-+C:@!z,f~$}+QWҔ%6Yu 2oQ,\ZhɳG1+EqzZOrCҳe*MKv( /U@㞄UXIQ)210?:9;K3+1RԾ2h$ru tG$gKGml/@+~dw;IOVeuo&6ﺩMف|K,5D㒢N)7pMM|4,VYd)$MrN;]cu% ן[ 7)|`??7Afڤ~Dެ_5V?PcH(^5eI)- 4GBC-5Y~ruϨM$ 2ݯ)s3g>l4n=jBstDסF\y-w <-qSs hna7sJm E&of"ȤH9B,ee"pwٴZ5#d9$/)HǘHxir\-=GJ\d$Yq?5NFCDQlh+dT"SV+i6ēX}(f|r+7p.=΂)@THp* UZbaiV3ҥ1x=7&S,h̀jS񊛔=4$Y݁Xد!IVm0AZ#!O?NԉbCI҂Üd W4 @8 \zl(:pE+We ⳓ-C!з0*I L;D[M,;U@֮.kkŰFuX(?;uiOX+fCϋ.&;9@\H'YkrnXskKS% ՄL ]WMdM*QR'~'ҤY#aplIY3*+aۅ:R;o N kt/j#̷4&êƦI ?Jc+hP՚vGX4ňN_lc>P>[4!%>?rlku@;}U%kɟ^"gwl'5Gvx9#M"gʇ&(8GRQqڎRCE;XEiۜuHD< fv#D%1vndň)9r%t&Q8`dG`9\pP{`Ȑϭ/2]bHyySJa{aHŸƺc)V$Z&) Obƒv傰bX6 l50=cZ} 92N4K l6V\C/b+,-X-; h S&4[l[`vV(8;"ҚV.FG<բ:拔H@=)piIjIVP$ f}S$<+x}dkɬƒL|TzIŲ9 :qY3Xѷw!!WOD٧RHo|Y?JJLbZ%܇䅏9 5bVLEF3RP1l其W+Z̈?& RmnXSI2徟:6B:qQ*';6Uf+ Mţ/<1vry3:]g3J7[gc-͡UZAx7 #=MO*(Clj"R±s<_U~.4V~4 6=KN}`uFR?Jך4WAmQ pc`A>> p]6GZnch8\cn"hW$7?Z5L` wZUE\.sҴG3nf0}mٕbL) ڼT8Cf} ʒ5 { 2>Ҭm -\ "Q!RƋ6}+#x0ȥ>5)j4Vi @"TA'횤63nXz|V?"cKamI>QIe#mlMUZ`Y79HKSW3Jۉszԏ1xnzZݣB9#9GSB7U%8~>>,7m&y=O$QsS+bJNgҏg)Ҽ34wnSg?9NsG%cM9tH/[PAS:VФ+tJ^@'ں5vbs+8+fn'veV\ٯQZ|Ѳ0=_c'H֏ \x㓚9uC~ h>w6GDMΩ>xvvQ*3$5zYaEݟ9xĨ$ x^|DG YZ].l1rs}^= mfTMgšh`+T3ӿn3|cU{⨭ +ڪ32#?j'IeXWT'sQq𮖭hf@W|#ta+"ޭ !@1NQ>~<Džn1j~7Q&(kFÐhp[YF|}kG+ktZ[N1ZP4'_钉tɧ9BƮV;/ x 2t.٭TQvOiлhЄsSU<+5ÿ)T3i QXo[0GZ\ට{ÑVw& Ã\$uS=Ke`&#+8+#=+ $iMEbf rQH<sL֓E>ks/\+u9AQG|u_, 6 #bv/H|Q?%rEmKy8K\~~$gZYB:&IȯJ)rHMv͡o* KY%cT(F˶4X[s3j@>aϭ{԰ ^,:F2Ί{-M:5e3k<,tt4O[AnH1=$Nlۊt;DۘKf6B*5vY:S7T̉E98IcT^MOrRR_WLb3_&/SofjuY0 9G9 2p}dʣq6/:ɚ,jR-;6Nhp?\ThDx @JIAiQn\ޤl DbuQ a O v&LGr)/4HW=6bJ=oHʎq]q5fWֵFS<9n>¾!ծ&L1֝x75?q+i9TVv=k7"FG~=+U- Q% MIjE)K0"PY7 =ُ8$5MC=1|QK>U$X92ұ)6XZ˺VY0~; gaI62k G1)ᦕ ե^ `һfߍ>w|քW<50qJꤸi\H2t`In6;z=-ȋsF[ymoEq\GOTzσh‚i~$ZC"ipvAQ4u:σ.tBM+ͫtYk>12܎ɎQ/KE*7 =XeZgKwd,oJȯ^v<*s`moDXeE^'T𦷠I\4FVE$Ezhj *EwEIXyFћr;c&U)m5U' dq)8YDs5" _+cFN[Ŋ~=O/d*@sG]AP f _y֥K񿗩]|s\3̼ƨ~)g\+ϽrO0SUJ]ƼIᫍoiX9cxƥXFk'l=Y ĖerQ c"2NI@Ұ Qa<)E=k&mZ!7)XA)f80ӮeT(W\*fvV3pE .YO]=.<̞R?r")hdޓYRumʈC} d9Hd$|5>L9E ϵR"\uò!kqԐ7ΡԓR3t@KQn[>T4SvƐ+֞Xg޵C,j q6 ЙEVV@Zcqip|RabcISi ')8).RƛzR\8秥0sJø$q7;Ɔ9ϵ sHK-10M{|Z%8-mCjX!QMh7F'vQv>z5K8I$j>4+mZ'cw&Cgdg$~?뷟,V<ڎώZY\q5ж8[8Wl3ޥs9T#18Jeh5eҳLYI!gn#?Ul>q>SV,r~龆!Md#Da5iK0kUHG5^9V`7V.\@\T:)3RXUקdRr-gּJnH)=8e=W2V7M kyO;F&^Br[itgϪ[2dl a{pJ+CdѮ+dsj\I5)Zras/!txQ:Yw5K6|35LhZr`xlX-W4ZiDvepGk'vm*Gl;j躺{dsS)]" 5T`4x53n>ҔQ#}hHW084ZhG(&H|['塰c MES֣ -ޚcltqal6;n,x+nu6cOґ)fb=+@aŎG4'󦊈aKbDh&($? C՘KC@@$&Lq1wjPiaqHis %i]  M OAP82u9hQQb!F='MgѩdG9b/R^^JtGcOC 3Z#fn""Y#𮘜3?<,|CyZRK =iX}ׇ6!`*=yj` &S9Ͻ+&+z^|zu0YH#N 8~7LckXJI2A[F9u+eU"TY?/N{Upeoe!=EWrbecni"9xehuXo w}kT;?&i dyiN^xő綧nAmcY:։!&7.wO|;xaEΫ 'ufb>Z4V~샴v-M3-`⻑==1>2hXFvzW䶧1搫43HJiNQ~s7-}TLe؈EC4L&=++Ʈ**\__yɶ[A;ZH4jM$RA1ޡh_e(U1ҳlJ'[؈|km L9<4/H %ǠoF&R3P.ⶵ!R3NFB:P @b gdfQ0jxXLg^!11|??C;_ ۣY;czз6V25}Rx%" @VCzjRu{fTK$aȳ)s2Йpr4)kW+\EM`F߆#<Ituys\rN P(*.v%kMy6 |3&9Bsڕo;VȫO~i+F@Wu8xbA]',X/YjWYsOgcDa2ֶC!DH/[bh6XIܤYUb6Vk͹cT\QRA$bZ.oRƑqu.^E M[\71N*cVܐASOg֜Y#HɔOel&œ vE4ngAzfG*NA>'tyU025h#Ca郹hodn?JHrF89¶+E͚xUk$V 30O1ѳqu1R 7mt=+ULrKr &b+6)9U"6Kw7Zr[#Ӹe j}s ƏfEGW\ZQ-Wȹh:+?e!؎Mȶ+30 );v!{cW(M=EMaDs !g8Ea~GphXU42Q#q@X=G=\X5YHQW$|:LJ8RxG<WmMCܴ$RlNX= a޳v)=+:7G/G^20 M oټ\bvaLN*_~-f9Ld4?WOZwRgYfA9+uF6>>QI t=qq3'4B |zhp\__ ܅tEt-Y#g:WC+LAR+>;, +8HG71i MRf o#yy%ɁH>&$[&(N)>Q-ҦLH4"Ia*Ԍ"d ϥ&Ɋ,[[y3\L7GIh@&]E CH$r͸j(i$$SI)HzDZOmW($=:n22n3lu=[ WpzSkZ%2gsVgLY~$Kn0+N]__iw 8 `'Z:ٔfgkdcE)Lԭ+5Ԟk^$VV-brX6Uo$o@y31`EE;[8rq~l4F6sTd)$dEP<+.TnsJ[]i:59~ U17k7fbs9:)N۴eZ>ݶחTs&{֩r͙EC8Wl#)4LBa~ZsgV SQ-GZiIEXXg0W6KLMz1w8篋<*`T_xN7nq޻aDŽpV qJ6R7(53R$c>(7W SR|^.KlC1)<94[ThAVm26B1m;Nr0S$M (qVUǥJC6Uq؊@C'ٺ+8 13G!'ּsҰ*rO4p#˹'64gԂƐրjÃsRa5)1&F;Cb)P4̧J7qI ӱW9$X܏^R=A4<1#KEX 1֚4[#șPqYMc՟_&2%csئAҔ;ζ88vf z1]VԆUli߸JkmՊHt@U}*bixP͏ƵU\`2kTq!% k{Mx,h"b7$+N$xvl1Huv\zL,ac/~U";- m\9S 3SСiy1T7cX^Z9ΈI Rh3KV'Qi^3H!qޮDQ(:E$#nq s&Ѽ][[ض`@-ʃ׭Zi. T[$RZ={صO5]a|[tA7UZb^nQ[8; P׋])\F뎵OsOC۴]%WNek7{ ̙ sQ Tsؚųu=dR8.4tu\Z;J=7E-ֽU.yutP܆+҄x󅏙i߆X]Z< $;סJG,H ׭Le$i]blM' !Gh$)$a3"^zVҴa~n})Hqo+XƟ(_0l3gGH2ϵHFL9횙ri3rX4$UKI84(ANF5V,"7l|{Si*nI4zͰ8VƵaJbZm; t&00"lz9`1b# b!V6hmÚGCI2aWDY cj[WҧBuf9|,۞0j\?$8O9j&`CJŰGH@o2P婴)sT9 #E"@G>6ZeZH4݊)j:q+>dRe[]hOP}i@>liwLzvb5J#-33TҩLyfe?lūg+0'Jd(`2;kSDj><cl.8GsխIb6Ni~򵃙%+N+ R~_]goC6!l+[hgs kt$?:ę]:SvAQMJ@C3GP4tb4{֩!cQ}j%,uYudʒ1esF8d+<2 k45qb ϵ5"yJX+E2y YlU9CNySf|gJӛBlBCҗ0XossvO%J.cQS 23K4ACU; F>{ -B=,r01SmJj/oҗ=ʱf;8R݂Mhsαr`Pz \h?#L}11qJᏂW/rqGKtT]X_Lq[sV;fdQbL\zW;V4LˇWH׭I6t \&kdG֒hG3̚:}(}əJigo'y湞9mN4*UMCpbz#6(&pEK'8 Е;xQL5&l 9Z2 RW;.=Q9UkC@7Ld;UE\;}:-(cҺ!91%Rv8-(x<~ܱo~^*4umRi~P7zםR:c#+⧂|t.;iKS-$w/xoJ]1fˬs(\C^_&/MZg|;umwjZ\)l5҅нsccpIcnH9iVDpĤzZFXSƹqO٘J Q#ɡxS<*i@Pӕ]+HN惜 QơJCW-!XIա-sk*Z"s2tD׊dٱxӱ:NE[ x$}#~9YySK)GzpG?w'č`iq]Tt<ƥ5_xK;}5Z"ϡ1t⼇ OR=KQ.84]X)rTWW yF5$- F~g}9++mCu:p&z& _[7qN:: z48S[R4gE(@3ƻs9&hB.CŎp/vNLG51͓ДSH\E2M4#>8ް: : Pfn8PG_z$hTxVs )$/855Y~]GCŶ ףn?-5(' W%hЕ7Ù㺴^EDSg¨bv%p@¸Xɻ7ҼX19KMH^k$bm1A)!zӯX<-\|->Sc+;%[$!цҪ#?iB [X1vԹTcМj9%2##=N& ҷR VL/O,^Xr}Ȩ>΢3 2jGӘTF})}y'8"bY-Čxf2[;ZIjyb|Z<,M9(XXsbzUZ`s[ė,ŧJm\RI,J2:(xB=STziPM`xR)0,0MMrERvԴR gŭl@zsIDɖ9_-،$8.SG+6:]2w# HU3<9/2$`iz1tȓ)֑ft ]Gl\5 VrV:+同6$Q]MtI楻I-Ar44fw Қfrv*7coUz QSg?V4$D_*BF dt+FpI8*GQZA#t%&FmWu6yՏ#+k'XuQr_"K+ozv/dXGRmfN-s'x-g Yy ]F,ɓk[2ɜM;h"ߛ)NZK<{ s#o9hd˲0x&DNO.29^MS'Ju Wާd,y%ٷ!#nݑ@jA tD.799 r}k--oBk^O;3 7`=j#_& C)R9mKQ RC ue*&xbgpVLJÛUkvQ9ZBͩAiZq\/s uk}=MfPFMKָ'+h,Ξ,YW<ѥ;O^-bcE'޵Pw! ܂žN I'MmgU 6V"»bVYD3g}qjsr>3Nir8*=Vhskޕ)4z0E@ =s\;nJuq'ڹZ4P+O'XHS'`LbgS.A>=*6tL"Ҷjtz7|VaU8^ ǙV~)屺$rnpkڣXѱ-^v$vz[%H+܂c^frX管w|ֺb *J%ʘ8Sje UxBrqֵC1%cMe'SYd Clhvp6=k.Ϧ%T(䁎ht]a5na|'3W-6Pux@=kGIur (n)lqHC RV>s RkSǬUpdS #Q6#ⶻ38K CW&$#B HIGh_VW{W\L]ud,pAQ1Q #zjh5ejluR.֍'#8k[xΘ Ts yfL/_a'8SD䡛NخH#{AMr;y`WDjY1o-vttSvU*:U3_JK– ,KEh0E#zdE/j͕|3jMzTBbSlV:?ۇkHhtQjť;kgQ3̀Mfsŗ|ʒve# 3qXE{85ƛ,ͽX8lTE Xٝ< ScKFqZ(U$[} V~5$QXqI#ɯR }48KD4*[NKo*;e8&gAC9VPF3kt}twہ EqU$/s vV^_YFCfdS%7095VT\uЌNxidek`2qY#jEdBUR*TQg*ZhG^x67$*lg5T*1f|3sU :׳ yr:t%]*fn,ܼۘV62I#?i JS LXHGC`Ii:&m\iDۏEQ,D%j$$56^lVe9b( { k>;qE9(jPC7|.~L=3HFb1^3四%ԑTmd8 %M"3Ԯң*IjN88Qm205% ?8qiѰZ=tʥE$^j!wAbo0),1XIXV6?%ڱ\g=z{eK`Lݳ/4'R;4,jVEj%sNې/VMS"A`kZ3U#r]%Zqhv/^+U$;6~% 5fql}R8''2952x5îKm _j/1q"}ߜQ'9juѼqE9'qW4Sm~2TZvyϕ8wG&OJ7KYvۏDv: @ֹ˵]4I޳4DIva'=&i;F{mW)7` 5Ӷzf-jϫL0]pmhxx$gU6x2z7 g'p>VSymc# c5ef}6W>#d?xvul~8뷱5ζؒZs(uGU=U5|q,nQXwvtZW i>l-r:{Qyհ$8Z)<8[OR(jF,Ѵ{T_1:M[{Iվ\mf5h#<^*1qؓk˩=Ψ#eU\1+" HrM5-ѥuUI%sxż8Q ZT')"re|/Oֻ"J-VvF >#+{B@[N1m@}^]im|-d<JȬtRo-ԝK# rNWfi9j.fTxȖHD=+9 ;;I=+9R/,tEV p*m$s] , ymc 4u%bFDj;v5O*KTz$Z. #hUtGV(ǁV-ylVf @tPn۽[{kcrv؇\5gyt=r+ϭB= r=FA:4k*qkbEsZ&qOеi_i<\ Nɞ=|YG[Cr[.:;YjTnxGS(l{{ٲ=k9;W^ŖxQ eIJt=3^M7>W*7Ja7>[/bkKu5[j~K9Rg7t#!cyF29 :TXҺ3dfFLf|sDI%fR0p~`k]YM09ǵq!/5 NA?f: ͊$ؒh8[t"F\H".{bUYmnUAV3|*ǽz9l>k@6YE&HECq@Y#ƘXce3;عs>F+܅uKWS H#cv1"c  n['5MDJwf+ q/4g# :-P@)wEhu_zǠ~,aRfEX؇'vK̍?^?֓SHFGzjnK'.r}jHJhGa֭W0uτi B7\ x<n,ОkNm3Es,}kw*"ǻVeicmr2=뮜[g<7[ڕ+إʭ-0͂zWzGRGiQ7ܓYKsϖ d=X2;3=Y`4vZܔ)^kVhxv5˩pIQЩxv `(Dt1ʌNN6uM EJkSץ&&6675M9b@D4H>t:O}t^ޕTL9ggDQYM5۔zeu?DܶW!r#4MVoܕVoZ^R\跭e߆={Ctu1Қ~T><CB9NfWiT0؈/@ܜģn$tMW`xMZIR44 &_3&78N lx/ ̃ dǁZŒO#sʔLhQcn횥6_}x~\݌׃W>* Z9y%]𣖻FVVeS\JxA1LR%S*ӹ^F8rNlFAPh9g5"Gf3X(:}# u}G vz#|]ere@{Vqv;m ,C^9\zPCk4WhadVab a[ 'o LNvı+ M)b8T$4+YJ~QWd;.}֖9"眧\ϛ 'h۹3H˂IT 7cy6]4D\mWKcbȤc'=9@o}*kX&dn˩3[CVr͓pGA}FrkZ|o*#$pk)+ rq-䛇~S?aQA\h- inv@\"dC덙al՗}';r֜W'9.QqץI!Z|jP&cky{k$>#J=E Q!I8E>Pp1H- dҸ*ʗgjWXiFaノzK>LacD>Ԭw &! 6NۙOc]629]RՓFўG5&\ҵ]MOZ։ֱ>HܯJPG<\<)6!iAʞ3L?\iJfve$Ĭ12 yL5Yn'gSk VhԳDcfKA'#) p3u=~d|Q]st a+FbN;05i7*k 3kc%N^nMRKS[T~U nxC_W^e:Ƣ5DQZ&1ʣ+ixwSмQS4GYoijZwrtKx2ǡshW˽w|R燔M_ٺ+y4fԗWH@I6u9ߘi.nMuߘrQ^4 /9 k51 35υ"+Œ=B&&{Rw- `Т>٢;_yֺH gC<{UeDMg aœ}͹lzE-8ɭ\zi45QM|MY5}ISϽiEݚJ J?g+;WIhxXG(W\ |8SKcV܋hyJĎAu-- I}:J5Xx&]ސ9汛ԥe2Tv=_k5NV&G[uRW%m[ r';A.dhLm5b4sw撐3:9h4Vd:F~q=xUQW<^|yw=XAߑFZ]xWJ ě?S[("Y]Ŏzh6^Hҳ&Y_ca4hⱳz6=d`ý2>$U*#8)H # nÎI]Q9d[ 8wu6\̡$o٭'c=Jyr+7)\\ϕ8,p3WDiܙJѹ"EaZT)ykۿ>i ? Q#\msNVm[{/X\9DqcKbDC*0օUL$Ь@POJMϜ$ksiI\٣eUN?:h?2@˞Qձ,}FzܛY\ .;爫u,TcFr=t`c^>f[x3W5B:u2qMpn0uu&-:ȬNu >VSlا#;[-9Th<䶑1FA^EZv=*rOY.$Ү>م3:Q+1to"\Wt?<>$8yd&>NZ矷a&OsYKqu5n\-@5st,P1d#RvV3<׵sH'K`"HYK(mdu0f[H?Z.ibEdf֣pxD֚ܛn4Yjy]ljI,>6S ׅVWgBڴ,W9•qZ肱nlA9t+qEw,Z[ fټiaS{MR-jHaEV#,)m% 4f _ivHIJCٻ't-B}cQ5d貪xL^]L2%o\ֲ{-Do(UqmKQQF_]kwK)l6½+1WikHx;wH-D^ ɡ*Vg_ qT esZNrHQdaY:ek~%;յ[==GQ'՚6&wҎ<9xUA)Z3_uO]-9$+ūsx 43r/H.Șkh3qb߶Ōp[xXK(J}+U ꏖo>DŽ]E.߆LmI@ v<:|M9ULX;o ;^ܙ%' >#>B\#[$+YVi)g*\8^6"#+u'TQg7v1=fuƖ|? m^::luLX*Ռf9OpMsךHބ9,Dy=:TN6$意B)Y:["V(Zzp9'rC_VU=1{Ur{D]T4Ѥgr@75nňuj3'Χwq-~ij)J]Pdһ=1*z?Ɵ`}>Ź!Ԃ½tHk>?>h-#-\v|lK}OoҴܸg7dy)MPrswgUV[txg|²/>R65:\LGVNǀ|AIeؼ<bs.-ۙ3č+jws]H8.v.XL4/Qq8u*Ue&}U,51u@MʒE)$EvG $ v&=jZӰ ~E2XEi2.&Bx,M\M!9R3+c)XXfe (+ vt[2(^DJG]^[43=ja+P xJ4HZladEj9T7 vD[AUʉJk2 `:39'D% R8H`9] MnH z5\N:Xk{K:_:yly'+VReђo)-H/6^HjSǖRNHڽSe9'.B]A5"cPxMG˃*+ՖZx&pHz&dC~Sɓ$:$J ״v^I\:yr (=ïLKR{lH5ܴ#DcI5 #Qmg!=j.7csY\DNu޹u4Z3*?\_4Pg5k,s͞}ѱE8Ȭ]To@r}&UDi!832YWl;wf?nV0ӃXJLX͟zwQn)6*lZ4U`AzYDhNmE|ǖGZ u"ӾǯmNDbK ^w15:nRԚOK{g}NόqM&vx)"B>cҽ4?e#JLh%F7}hj6d`j#v|Wc!kl;G=kxQ> ^z3WdgƩ>м։vMo׌w2x7m<c kf1=>CV~<;K{/EQٷcp|-'g$xjx̲-Gj@Z+fr| Sa}jIx=^W5TJOsp5:} B=zW,9<$)`(ɧ9 )Nkx̪d,s !3|؆$zAzp FY20hJ#{!8rE1r<("kMۑ`F1IUs[Ẽyu#δ-wRqSWG ״" Xs\gfttrsQ*0$G(!zSfFAW4ݏNSEeks#9ZLҹ^K ㅭUKҢeXbJ8j!kn=NdqC|N=?Va:$h>U5rG9?Sew\Խ~&1 `R3:isM3)͖&^v88#t%հ$OL/'] bN6#CT6YZ4VkxB>5d&dIX> =< "Lс1DSNwJΓ 'lz售{4i˙.p<)HE+E 9%N C$MsȢ)of3)*<e'Rc3TiQb>TzҾ;Lewbz֫c[_z/CBX5_͹>$ZؓXy>ONc<{:VAIby?iϋ׈>A#*xw6`_g<_Q=sá,ұ'Ƹ'ZR=Yu(l(qlhh#cbIF;tn7 j>46O`d挹o85v2r ky9 LA/Ah /R+q3i3Xӹ2qf jĿe,3Kfԫ}j̥LgIGdrx֩J%y"q#t]EleQXOz$"REePՙ zΣ׋Vլ>dL]hq`.(ѱbi\OCzȯ:NtU5#4CNQʗp1vөtsJfd`u)"{<9yQ ̓ҭLTH ʵS<=0?}L$vIZ yV#<&Dq!؉lwvz{W=I!鞆1g>I9^F7p$F`zȖligl։x7QTelB֠:UF֧V- m6 EoǭhhՆ47"+JT^nRZN 1/&[R@ b8F"֫i0SF]5)>]"E[HKBN~Q^]Jv=ZuQ-F9:tR5 M W3s vl W{^4عZ6bd( ŝrIZ$u=$uĻ'\E mO)_ci42[ ?52y.g[ljJ);ElqJ$2&=DYQZf}m;]P9tjq4_K\|ѹA!d&S9&ekAZTO) &\>d#\FWX 6Z+T: U{ R>J "mL1ƈMD 3PR#K ߭\J.lg3pc֢-ek.o#DkmOcjw{4Տ.fM.uK%i%rcЉYWH1g%2\iɂ ()= u/zn;wPTzb+idR6!sQ쮍w*qڤ{-^J4 NiZG-ĉ[ƚG^>,z]J(ܑj*YBY}E\k]_J TiI8C0J -AH-YNvg1M 1PtŜuʍN&~Q$ 0=WTDO!cJ9"=*1w.XdžR JұX*wZsyJj9l7f,d~4dVCXNwEN+5uQ\~ѦzQ:3s]tkYuG9=+޽hպ>rœ[C0+:\T}tZm⹯ϭнH\k˜gKPQ P.[Dsz$Ap+ӥ]=ϗZql }kЅTϘ2AqwBHkP(5Қ Oi+b> Q[I8rT(L赆lm2ƻ+6ĕɼ=/sN["RZ#U,\Tj-\f.6ɰt|qIJzmd87CpQ.S[QV 짩>[.{JwGj(xK6浥=,lbFOZ|l1rؐHɎyL'噀7sCF8K{HKO0q޺tmyD:ILAGMgR7- }zV\[IgPi(*{A$l#T9kxy>aWe]hd81ub(%5dRlք'%ͥŀE^Mx?\]oxz(D\I:RH]~"' NI|oثJ y+V XnN]GF$0Ƹp 2z#%ZyJ#GL6f35[ڂ!cbG `Esc=r,zҦrwr{WD12b2d`x.7ޕϜ'<01ߥzPs#+ed+*wu0X4vZp#5zX|>Ma`y5*\5 e#iBAEG1֠ (RCؕSgr4od*J)\vBnZ՘F,%?fXגci.DžϽ5T\5UʲZf8#4f=jX.J=Mh[ ·DQĆ1@ PƄhI UFLJa_;~>`kTqbH&ކ|'->"m-Yi46̖2c|OCHߊ㲘M1 mO:$i \5w:!^m3H#:5^*rYHźZ<$\Yd>t>1xNHuۓdһ-B'| V}NLFz T>(69dtR.jinxW3^BGa=gs*#Zu(fZ[ɠlnTcsm""3=Ąōy$zv.GUmAB `8E[q^l>ͼFW =Ľә@ y4Gb\X,$e48jxc5S\OQ5dqXTzR7-Ҽh޹${ Y # @I$]Gаj^ġsY =dt@ٷYy\sl[P@xɃ5c#X\O@ W;ZuV9ǽt @A]ev>ح r9j ZIXwY4n9ڰiQlxFqJH󜡩cG