From b46392a2c352f819122c923e6370ae5640dd5db5 Mon Sep 17 00:00:00 2001 From: b3hr4d Date: Tue, 27 Oct 2026 00:48:14 +0330 Subject: [PATCH 1/2] Apply the B3Pay design system to B3Note and add on-device AI Restyle the app with the B3Note design system (B3Pay's tokens plus paper): - Tailwind's zinc palette now maps to B3Pay's warm graphite and brand to B3Pay amber (bronze where it is text on a light ground); B3Pay radii, buttons, fields, chips and notices; Schibsted Grotesk and Martian Mono bundled with Fontsource instead of Inter, so no font request leaves the app. - Notes are paper: each note gets one of five papers from its id, with a folded top-right corner, in the list (a stack of small sheets that lift under the pointer) and in the editor (a ruled sheet for writing). - Empty states show a stack of blank paper; waits use the animated mark. - New draft B3Note mark (B3Pay's mark with the node folded into a note) for the header, favicons, app icons and README; scripts/brand-icons.py now generates it. The mark still needs sign-off. Add an on-device AI assistant (lib/localAi.ts): Chrome's built-in Gemini Nano when the browser has it, otherwise Qwen2.5-1.5B-Instruct on the GPU with WebLLM, downloaded once only after the user asks. Every assistant action and "Ask your notes" run in the browser by default, so note text stays inside the end-to-end encryption; the LLM canister remains a second, opt-in choice. AI results show as blocks that are dashed while drafting and solid when ready, and always say where they ran. Settings can remove the downloaded model. The CSP gains 'wasm-unsafe-eval' and the model download hosts (Hugging Face, raw.githubusercontent.com) for WebLLM; nothing else changes on the network. Co-Authored-By: Claude Opus 5.5 --- README.md | 23 +- frontend/index.html | 7 +- frontend/package.json | 3 + frontend/pnpm-lock.yaml | 32 ++ frontend/public/.ic-assets.json5 | 6 +- frontend/public/android-chrome-192x192.png | Bin 6890 -> 3201 bytes frontend/public/android-chrome-512x512.png | Bin 22001 -> 12321 bytes frontend/public/apple-touch-icon.png | Bin 6525 -> 2993 bytes frontend/public/favicon-16x16.png | Bin 663 -> 447 bytes frontend/public/favicon-32x32.png | Bin 1396 -> 813 bytes frontend/public/favicon.ico | Bin 15086 -> 2046 bytes frontend/public/logo-long-dark.svg | 1 + frontend/public/logo-long.svg | 2 +- frontend/public/logo.svg | 2 +- frontend/public/maskable-512x512.png | Bin 17831 -> 11291 bytes frontend/public/safari-pinned-tab.svg | 2 +- frontend/public/site.webmanifest | 6 +- frontend/scripts/brand-icons.py | 149 ++++----- frontend/src/app/features.ts | 22 +- frontend/src/components/AiPanel.tsx | 324 ++++++++++++------ frontend/src/components/AppHeader.tsx | 38 +-- frontend/src/components/LocalAi.tsx | 145 ++++++++ frontend/src/components/NoteList.tsx | 33 +- frontend/src/components/TagEditor.tsx | 4 +- frontend/src/components/brand.tsx | 114 +++++++ frontend/src/components/logoPaths.ts | 12 + frontend/src/components/ui.tsx | 78 +++-- frontend/src/lib/localAi.ts | 259 ++++++++++++++ frontend/src/lib/localAiPrompts.test.ts | 33 ++ frontend/src/lib/localAiPrompts.ts | 63 ++++ frontend/src/router.tsx | 7 +- frontend/src/routes/Ask.tsx | 222 +++++++++--- frontend/src/routes/Landing.tsx | 77 ++++- frontend/src/routes/NoteEditor.tsx | 35 +- frontend/src/routes/Notes.tsx | 18 +- frontend/src/routes/Settings.tsx | 48 ++- frontend/src/routes/SharedNote.tsx | 25 +- frontend/src/routes/SignedInLayout.tsx | 5 +- frontend/src/styles.css | 371 ++++++++++++++++++++- 39 files changed, 1771 insertions(+), 395 deletions(-) create mode 100644 frontend/public/logo-long-dark.svg create mode 100644 frontend/src/components/LocalAi.tsx create mode 100644 frontend/src/components/brand.tsx create mode 100644 frontend/src/components/logoPaths.ts create mode 100644 frontend/src/lib/localAi.ts create mode 100644 frontend/src/lib/localAiPrompts.test.ts create mode 100644 frontend/src/lib/localAiPrompts.ts diff --git a/README.md b/README.md index 49af85f..e2a64f5 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # B3Note -![B3Note](./frontend/public/logo-long.svg) + + + B3Note + **Private notes on the Internet Computer.** Notes are encrypted in your browser, so the canister only ever stores ciphertext. With Internet Identity the key is derived from your identity by the IC's @@ -19,10 +22,15 @@ that **burns after reading**. can request the share's key, and it does so once per view. Readers do not need to sign in. - **Sign in your way.** Use Internet Identity, or a *guest key*: a private key kept in your browser, with a recovery key you can save and import on another device. -- **Optional AI assistant** on the IC's [LLM canister](https://github.com/dfinity/llm), **off by - default**: summarize, suggest a title or tags, improve the writing, translate, and ask questions - across your notes. Text sent to it leaves the end-to-end encryption, so a controller has to switch it on - (`ai_enabled`); until then the app shows no AI features. When on, nothing is sent until you choose an +- **On-device AI assistant**: summarize, suggest a title or tags, improve the writing, fix grammar, + shorten, pull out action items, translate, continue writing, and ask questions across your notes, with + a model that runs in your browser, so nothing leaves the end-to-end encryption. It uses Chrome's + built-in Gemini Nano when the browser has it, and otherwise downloads an open model + (Qwen2.5-1.5B-Instruct, about 1 GB, once, after you ask) and runs it on the GPU with + [WebLLM](https://github.com/mlc-ai/web-llm). Browsers with neither show no on-device AI. +- **Optional AI on the Internet Computer** through the IC's [LLM canister](https://github.com/dfinity/llm), + **off by default**. Text sent to it leaves the end-to-end encryption, so a controller has to switch it + on (`ai_enabled`); when on, it is a second choice in the assistant, nothing is sent until you choose an action, and the app asks for consent first. - **Your data stays yours.** Export to JSON or Markdown, import Markdown files, and delete your account. @@ -60,7 +68,10 @@ that **burns after reading**. signs `(domain, share id, transport key)`. The canister checks the signature, spends a view, and has the share key derived and encrypted to the reader. A transport key can be used once per share, so a replayed request cannot burn another view. -- **AI (when switched on).** The assistant needs plaintext. Only the text you pick for an action (or, +- **On-device AI.** The model reads the note in the browser that decrypted it. The only network traffic + is the model's files on first use (Hugging Face and the MLC model library on GitHub), which is why the + page's CSP allows those hosts and `'wasm-unsafe-eval'` for the model's WebAssembly. +- **AI on the canister (when switched on).** The assistant needs plaintext. Only the text you pick for an action (or, for *Ask your notes*, the few notes your browser ranks as relevant) goes to the LLM canister. Everything else stays encrypted. diff --git a/frontend/index.html b/frontend/index.html index 8aab8fa..38642be 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,15 +6,16 @@ B3Note: private notes on the Internet Computer - + + - + diff --git a/frontend/package.json b/frontend/package.json index 8df1704..de79a61 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,11 +17,14 @@ }, "dependencies": { "@candid-core/schema": "0.3.0", + "@fontsource-variable/schibsted-grotesk": "5.3.0", + "@fontsource/martian-mono": "5.3.0", "@ic-reactor/core": "4.0.0-beta.3", "@ic-reactor/react": "4.0.0-beta.3", "@icp-sdk/auth": "^10.0.1", "@icp-sdk/core": "^6.1.0", "@icp-sdk/vetkeys": "^0.7.0", + "@mlc-ai/web-llm": "0.2.85", "@tanstack/react-query": "^5.104.1", "@tanstack/react-router": "^1.170.41", "idb-keyval": "^6.2.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 839c4d9..bb7aa8d 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@candid-core/schema': specifier: 0.3.0 version: 0.3.0 + '@fontsource-variable/schibsted-grotesk': + specifier: 5.3.0 + version: 5.3.0 + '@fontsource/martian-mono': + specifier: 5.3.0 + version: 5.3.0 '@ic-reactor/core': specifier: 4.0.0-beta.3 version: 4.0.0-beta.3(@candid-core/schema@0.3.0)(@icp-sdk/core@6.1.0)(@noble/curves@1.9.7)(@tanstack/query-core@5.104.1) @@ -26,6 +32,9 @@ importers: '@icp-sdk/vetkeys': specifier: ^0.7.0 version: 0.7.0(@icp-sdk/core@6.1.0) + '@mlc-ai/web-llm': + specifier: 0.2.85 + version: 0.2.85 '@tanstack/react-query': specifier: ^5.104.1 version: 5.104.1(react@19.3.0) @@ -210,6 +219,12 @@ packages: '@noble/hashes': optional: true + '@fontsource-variable/schibsted-grotesk@5.3.0': + resolution: {integrity: sha512-ndS6H/KICWANchlHF3q4UdZD+xrZ3Ji0rUobIuXlgvDUzDfqZDsVuXPXZb636nJZMHE9zZ9Fmwgqazm8hMB50Q==} + + '@fontsource/martian-mono@5.3.0': + resolution: {integrity: sha512-0iTnU6SZi6j5z4ymwlZGep6akvp/xWm+r+WyZO9hMfDJWX+dsFLipQp9zO02S0lkUQ+zAahcZNy3XLiNP+gVqQ==} + '@ic-reactor/core@4.0.0-beta.3': resolution: {integrity: sha512-8UwwxiAHIlu80LC3FLyBdEHhe24uIuUiMofAzU9NvB1cVbM0F/RE9SpGKPyMqwwIobWIg/+cPSWe+WG+5hXv+Q==} engines: {node: '>=18'} @@ -270,6 +285,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mlc-ai/web-llm@0.2.85': + resolution: {integrity: sha512-CwUiSAvDCYnmAZlpPMkurPHDfBLq6fvdRB3vRgoA894hRG/V/BSeY9bmMm6iIIU5VpjJxfb/dLPpJX2at0L9rw==} + '@noble/curves@1.9.7': resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} @@ -981,6 +999,10 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -1581,6 +1603,10 @@ snapshots: optionalDependencies: '@noble/hashes': 2.4.0 + '@fontsource-variable/schibsted-grotesk@5.3.0': {} + + '@fontsource/martian-mono@5.3.0': {} + '@ic-reactor/core@4.0.0-beta.3(@candid-core/schema@0.3.0)(@icp-sdk/core@6.1.0)(@noble/curves@1.9.7)(@tanstack/query-core@5.104.1)': dependencies: '@candid-core/schema': 0.3.0 @@ -1645,6 +1671,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@mlc-ai/web-llm@0.2.85': + dependencies: + loglevel: 1.9.2 + '@noble/curves@1.9.7': dependencies: '@noble/hashes': 1.8.0 @@ -2219,6 +2249,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + loglevel@1.9.2: {} + longest-streak@3.1.0: {} lru-cache@11.5.3: {} diff --git a/frontend/public/.ic-assets.json5 b/frontend/public/.ic-assets.json5 index 32d722b..44fc0ef 100644 --- a/frontend/public/.ic-assets.json5 +++ b/frontend/public/.ic-assets.json5 @@ -1,9 +1,13 @@ // Headers for the asset canister (icp-cli asset-canister recipe). +// +// On-device AI: WebLLM compiles the model's WebAssembly ('wasm-unsafe-eval', not 'unsafe-eval') and +// downloads the model's files once from Hugging Face and the MLC model library on GitHub. Only those +// files come in; no note text goes out. Chrome's built-in model needs none of this. [ { match: "**/*", headers: { - "Content-Security-Policy": "default-src 'self'; script-src 'self'; connect-src 'self' http://localhost:* http://127.0.0.1:* https://icp0.io https://*.icp0.io https://icp-api.io https://ic0.app https://*.ic0.app; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'", + "Content-Security-Policy": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' http://localhost:* http://127.0.0.1:* https://icp0.io https://*.icp0.io https://icp-api.io https://ic0.app https://*.ic0.app https://huggingface.co https://*.huggingface.co https://*.hf.co https://raw.githubusercontent.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'", "Referrer-Policy": "no-referrer", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", diff --git a/frontend/public/android-chrome-192x192.png b/frontend/public/android-chrome-192x192.png index 51fc2b00f33e903500c3faba311df60b4a05b1c3..d46fc3c84f1636e43b3c6f16fba051f66408f392 100644 GIT binary patch literal 3201 zcmZ`+c{r3^8$Zuu>_$_zA+M1oOZGk9m_ib1h_NLjB_$$}WEqN(%o{bxnk`E-T3#C4 z3=ztjO35-ro2BgAjPL3CzU%wzegAmQef{p|oZq>x=REg)|4t&o&I&CcB>(_GTOYD; zKrG|;!^eY+4xi5R0w82yZL!}m44TgQK#_hc)<)SBmpK44uk40Jr4=n)u{LV1?dEWz zoYj6UnN}GUo>QsSiD3cc1r2f+_>DYZxxj4;RrXA03=AnAoe5UiCN zdsIyo57c~eyk%7I;Go0LL`#IS^%b7Ds{+C}!~p3SfYs>l9WHnHkp~Xf7mh z=T;23qw-?Leh(fL*lF@QmjFolSi(>tkqj(yP6$`oLNK8OQ)5+$e}Om*h3%{%IA1|w z_<9+D_;fEMyZs|Bat^@Sp62GmpDr8JI>>Dc0w>Kbx3kPwI*#q3ynk#GQ0JtvF)_Di zGIHZnT>o5U`4D=8BcFMHn@cfyurw9!ldY(4Q;povm#B(>jblK^97oeM-S9=V4lrt~9b-zN!G$@?HFvkwV%0l(tD|U} z`7F;Z)f%xR9QVDtK33LO5h`rs$R>+bZm69D-Rpgs zAxp~Vn?ioLJ|CLue-=F(FIjJm`F>MIA1qBKcvFN>xmxL8(!d=*(er}YS`*TWgU}_6 z5%d-R*)=rg0~sTF1~~;kkuLEAgzU})pe&Vf?gm4LrkHj_gJHkB6N zvbmlxgyLD2=_tlcJw3q#!MkbeOf2L}yn15#ZqXM$H;ju1&vu}B`1uu#i?HHhoBLDt z*XxuZKEum}?fO1Y#TN+T(&{wJBgC``V90I#YfrpWa|L}Zynk6_0AI%xUeg`9yn3B5 zP8_I{(vD|^<6;7MjODBH{VA{GQfhx*}eao}(xcyDHi2#RZB;!!gePF1^?AW@Y ze|k?I{m=J04SfMyDZx+DPneFEQgE#kPPm3e3p5Z2pV=QWSc3C-mvuMv^BAa{gm)b$m*)g zaM)nuJ-830)41Ca%e2?0jZz3bsSb#6vy*uz=_`%@`5n@j*8jq1;E_CL3h zX5aPT`H=#$Q|Wor;ntxe``Vu|l|1wMbv`}l7|Zzi)vOzbIAA{q^`Z&9Z3| z+8TnS$Z76eBW3|o3OF6{?yJ_^)FJH}t>*WP?qEX|ye zQ3o1TLlg5~PsIm$M9w!5Jh@^C-WQ}UKLvVXMYB;IvY&9BxzZJ_#ecm7wj~cn{G==O z&%~JbM7d#sscS3c9ZQ_cnOc-L)+}(1@R97w=yyLcxS>2!V&VMivOcab^U%|oQ_N8u}AeYW0W z+y}fMyyulH+)Epb7)E1!)o-Z1hIa!a_TtV?Yp*Y-PLC~>4&$>M(9qQ>WMkk zM*786w*kR&-^jG_EtfhL#C`cv)i1znoi zGthL^(Mbs=IF*4RWOLGBPP{EJzMGY^hV-qU>hig*Ey3jO9nC2bjd8XpJ*&~scIxDi*kcj)j@@KLq2x`_+t z7X39zi7A|Fo<1wqyN)uHte-O-lw4*=lH$kfzDrp}-&$&FYp|$lE1gUSzAE1mamUwK zADhB;`0yc>^%oMy=T?XV<1gOtd7FLZdjbf%mqGVBD-qq_r^E<(SvH&teD@f0MY}Bc z$*gH#A2b6kST7V(56-m|a>{F67TjGpvNOLOCzZoonrc)%BbIM3zFBH)dz;(Aa<5%a zyOjw}9ex@itzjM?mpFdOOF1m#W`SAADlvtF)kU&V9ou++b;|#1{PCl= zv)^r#{!{HGB|77YrGj<3*pqnKwOQZj<-J4wlijnoGA>@5%*ql?kOIk}qnw?S^ah*x z0|L!MbM<>d9@Z=;Pd{G{cQu{YIxZ$vRSsZmQeW|X3hQ2!w)(QASD!>laew?*?K21!2p1Sl(IC?kqX})rn#Oao!4@T5r#6Pcnuw5GL`Yb zN6y4#$r6IcY<6Q`+9I+{_jXRsdI9p&DvBg~??xJ;#;G&75k#leA3<~dH~_)Dah=mG z?ucrvDf1$EAQW^$W@K0evcf<=2sO2#%txM+JQ-n!NIAR7;^0xlHl9iNwbzLZjP9m) zM>>ytpF7r|zBXjBw_wD&@o>Y4|VL5;#s2=&JT6 zBC1s+^F!hzgxgcMrPkP<3@*v(n*2g2#{7J+&PvD&bmljC7bIh8W!_^_zp>cA;RX4h ry@jjd0Bmh3ps7R1MS|z|4T1}NJ5yr&o?canc!0H~oki6FQtW>KXYY*T literal 6890 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 C$|%|Z diff --git a/frontend/public/android-chrome-512x512.png b/frontend/public/android-chrome-512x512.png index a177ad08a3ccb9f65552bec36d03a4e587f0e929..867d210796404823e900f3934314550b788d91d3 100644 GIT binary patch literal 12321 zcmZvCc|4Tu7xz84ku8$7MHv+-LW#&WEmBz{J5jbmmSo8~lc&-mm244u3WX3^vdt(< zMaY_6gk+bUnR(C5cz*Bu$NQS;3XU?x>1wnX`GbfLo_rr|$1U+zY3KE^ZgP?o4&v3}@-y+GLR(f(MrK*JE2QO~J zMkVfB9|wdiA2;o?lH0&`vx}40mR#C#Ditf)q1mHnygBmOB{Go@cT2@Z@QGyUX~k1p zj-Nh0dxvPt8&>Du?(6I5Gh82}EZb1a=BX;bnD{qgWO3PgG;m6Jq?huhggTf+yL*e+ zPFVkgx6CFcouz6K5DbcCKP>U+3DRg)E2zWxKNiWR|CSfmn1B};--wqqgP-vPyd8?- z?YK>4=*0Ij9+|lf zblUx;cqR=8v_^aJ|M<1bA_=Yon;D1<$_R)-(0#$vbf9m!#?OBP%j^^fco@`}iybj8H1%X0OEKAPo(n1Y;+Mc+cwQQZobR?u*~|o30u0C9 z2DdU9z6FL?T?9`t8P=uSAh9Wf%1qiDpk1+x(p;xy0c~Q?oJmUonuDd_I&It-1dVw# z!og6)EfDmUt})-bpuQkz@2NJr9x!Mt2RqW5fBx9IpsFC~UrF;jOhF|;&^Ci1#wKIv z-&R?qZp8iolVKDvG*%Hju`Y}yXfXTfJo98UfuZKN^PEhEav{KQ&7^G)lOa1Wd~YbW z&Ttnnynop|hRG0Ac)sbh!#czJz)*VF;VP5iEnuj*UGNx_p){CG&gn01DSfMM73^l9V1-Rtk2JK<*(ZAwhrpns3-iS3pYlqY;1S6JZwWo7 zq?v{w>Al3OOi5pWq%Xqd8JNKE^H*32t!u8j^iQ$(1bC5quJLF1nTCpA#Q{K;@ z#m@x_5)8TDa?Q~pre(cjYB3ccriLG6xB@`z1Bk&v;S7aib3u!_uJ_NZyLS*koIBte z$bk6h1p#?<(Mad+Iz$pc46zGf8fpg|ct?KaKkL$nfi$-b3N|qp#=%1K6FLmXv=H8s z3hLkD6u{J!IV?2K|75?u@CAM0lLArZ!pE@ioV?C0CdVyVuu#b*U>6(}2JHY}?0En2 zoBYcI2OP{Gxq?d`J2N+^Y;~d{^LkrC{(DyTu6k4$M|)K z>l{Iiwtij#Oz(dQC;qaKj9p)N5JasuDAZ>xl*=U`a@SZax+u@50!Oa@DVvI#3`jE4 zGN1I$4tX-^aYx0zsyw7`^v#^OltHAJK>}HcUkIu8ba&2<@LT%SBt@$gA02EDk_;Mc z(1=l|e2GZW4<0B!7foAy6Op#Pe9dXJbJKb^Em z-4I$Od#+|d{7<}$Z18v1ebKLjJUmqP;BB=q7+Wo&O+I2Cq}ARd*7FO6DH(mF=z5K* zzEn2Veya7xt}k0Q1y6@IaV63$M~gSvM9KtBWHeb%&gMOTpyKjKX{qYiwTbD9aNo6v zqs4U=q`8rWDHRjDwNj7WNF`tXonz82+r=c0ACtalpKsE7RdxjT1vKy8Ig``1b97Rxv@1N+ z+IM%GieZ9ynD|G-BF!yfDZTfMPuZ#hc-7VYi)(~cTJ!Z~OIfMhf}&$HSKWi{(kiMd zT=HX%|3frOy-1EDwyIv$wap_SIp&|Hq`21VYi0F(i<_UD>~zY+O!|^mQ)8^xp6L0A zveA_Mybr}5Gcf(~BME6)hqcnuO$Xuw2EQF6 zEO)z_)Sa^JzRQjbu9)U8)~GE!aQ>tG_d!kO`TOxylL1S=L3Y&h22Q4=GIttL#5E=p zkVf0)Rmo)#*RmKS_+Hca^1-t9rl8M~tNG2X*j?OS%xtfkAh3on-g$>i?{p zvs!`05-MI#gds#e?7%RY9Ygi5;43!n74drZ4tZpBr0y0%l*T^1MF>5^{`ub>65k`? z%=t4fa_~yp;AA+S+T7xt-Tc`_0>xjT$Zbclhxn9yB*?-W99sB{Mh)=DZ{B7DEPKmd z9JZys`Q84nS4k+|ZO1_fcDejvy#2K|b6xIpyp_Zune3OnP;4h}*ed29f}($qe$;aK ze&km>zx*yd&IluhYp#xMGel9cp{wF9e6lf|mio>g&WkbGDm4dK2_hwz^swyq2YAJB zV;s*!h{}LV`BCO^xM9AbZk*kjKNrhbXRS^kpYvoLH3|4()KiF@La2+k_m>An$EhYl zz#~thE_Z^grzejl#&P~yF+x7)%MTmBC)-n2{vC0j{mWGuM_6tzkRS%a?t%UnC9v!Q zlDvy7MFIx4gB#+nm8s#js?4NN2(nD&|I0ccO>pOs zF1}T!t3ET>B^`N72L%7!vZ3`(ft3IuHhQf>yIG*yeif zBXbj53vXx>>rvOAmscXX?+YW>rXDQ|%6cqEnTk81_uL1&S(UitjzkG+34z_X5qb*6 zc%9Hm8u`v1Czj6Ng?Y%;xf?!F%NkP1ufE;#nWu5}8&2{<L-DCVkG;)4v=RdozZ1dP90AVpD$1plED-Y3b?(o0?7~JI&vBqLNBX%XTZepz0 zQ;434MGQ_Q)jh!*qi?Z9B`yQwK>?%xQr%=}qR>r(?1ueg3LRCh+FMT`k|{>RXOb@w zA&fNJhKm<(G_W%rdwm9Z{W2mQXHbgoRx%r;U}RVXXiZi*gli_k$k)-RIKIor0dLE~ z6-*4~nct|8=_`7y)``Va(EM9S4CYVBH$5ro(KN)su&^!)e*#sY|8|@IPoeYah=D<# zP88k+^}a=Dwsf%xosTxoMGQ&oL4h^ef= zvgy^2Jj4y_x>6N?ZU)7l`X(nxPYWdVlUufOAt^rfr5uN44E{LK1*U%d_Bs%Qd+8Vp zHO5~q9YqXO+`c(=!pu-z@^)gb)@J1E zWKst`H%Evmuv5wp*OLUi3>t~LW4os!kq@PTCeO4+w}Y&{jcbfNk<172h3~Q=Jxzne z^lP>aB1yWlAJ_PvT)Z6&h5$640;c$4r!HEtO&uyLI+4S+hUO?T8+evGC?wDUc zdWTc{Ps`qziokp~ndfp5O&~(k(df}Y_vL!uL(e;An~R5{Eu;H>L}MM)A5h!8H5#9X<_pLmElpoG{#k0w7yV)@)T8sWGs3F<8~zmF!ZTCheV!Im zG|Hw|*+CP-d}-wl^-0U)dx8r7&D&~uFJnAHnDqPR*ZqciRCK=10C}AjV}5@2nRZ`n zm8wZxrDfm3g0W)Z0b(4SC8^lY@mQiXvxK^#w>BtBJ6Sw1VLGJzLD_NsS$nw{h1L)h z{7~WTlTh+4)#r}@YQ2`L-L&Xlrcg6_HLlqvc+7J2rtpQQ)G?L5 zvLVk9D6woa@YW$5i1)mFhI26OkH+cMg`~JF|C*^(_p2iIdC$75?aQwKHk4RG=A z9!yHeqD*TY**Qadf<3EB5iOs@u(N9OK+V8o{o0SG57LX~_&$Hlm7y9{b+w81q__(! zT_i{vKgBEZl)7(qxY3gmlforitn_^TS6bTM#QB!Fdf&3NbXmFdrIhz6@1N+?^aHv2 zqc23yX|;zRz7XQZ>A7dKd`(0KM!ig2Rh2_ymQxj`g$ zpoK;aia0tt)3h_Th-za|`>>&~%?3-bP|1)%1=%YrNVk1y-|yWWqgB{`ISu*C>#eEB z31kl9GajlsE#%+qA&SY=16G26K*k}oiZgSE`uR28kg9=rKY7rA{96-A;u`CK8%=4c z-Q!zG-oj8h#zS7~9I0K^9FsgenI4WmhI)PtfqeaZS4H0_?dFAl!gY<2%b7n<#FWq~ zNRKX=m)M8g8%kY#{#?lXQao44;Cr<+wT7bsJeO{r!=_HHxP0hv7dWXhaVB~4A}gVI zRmIuc(|{_P{Uhd5$6|6xs+rJp16TgR%0n()e(KTYV}3HS#8D!t+Lsca@|@ZSsYL=f zcS7;GFIEFPLP~3=W(gv8RYiGWm!6wBz8(B9!)l>Cedm2M?S;Xl;c6W#p;JZGBghkZ z=)1-;wXigKiS|w;ge+^$o}lzw^lUz9uy5mJ56zLAGy=VVUu}klO?ZHiO#%b2w5 zfu@_0OUhI-nN(S!(L1rJ)Ussz8FC5jalcB>)$O4ika>J)-(RCzRBvfkcQE=!`;@a9 z8!c;3?be#-w4u55`8ZDGEeYM0u7TpEPeh5dua*53J*yHysVt$XI$jdWV1J+c7HK@2 z)_UVS4nEPM{a)7Kp{lVsDV6%twklRV^%3Mnd{SmkzC}Tj(Y$iMMN!{{#Sfe8M#TGj zLM~9uf)yuR_aX0_U83Ch=)h9%9Hq2Ykc{fb+3uH6%T^o8btQ^)@yL%20}VddT}Wk< zUe*2yZQC9v>gvBc0=uJqB$4+mSMwA1@M3V$k%?wA#Gq9@8&7O}%r4$E*g!VLa`=Ne zYyhSt!Ajr2VMYdX{Y~7W84vZwqSL#M{QLr>(S&4NavzT+nEBl3{Dk!p2=k?>sQ)~M zvO;ywdiaaLoy~OVEiG-zO%seoPAQQbBveULi9XIK6)HiKJ7Fh zPqQ{~JaXzjKZr5zU!_o!LNMO67GIbhd-jSpdu5HvppBo^C8K!W&rmmg&Jve^Te=>d ziBRRhDEnTH`;h-39BFjyGu9=LIU!+c;}NyEh>hplVuMGvkk}mIN+Y74%DChBwb}?) zy}qYMINq;hAdSu=t+3)NT63ISu?L%}bA_-OoOE;J#1n*AjD=lvCb=Z@(xd1ASTgm3 zul2=$kQkO?lL_C_Xf0vHVzRu`iSs1#gVt{o`{ChU4p-r@TFh~>k6_3M1ms^o1iheg zxr`A@e!7sarjJRUpk^c@#6;}CHN5_b?a}zX5b*Xs*Z|iw*@S2(x_Qpwn5FUTO2&K# zJKe@x^a*%(^f=!A{f0?Alvc=hUP!op=;DqOBG@KV<%b6s4j_Kq^cJTRu}GA>m&N~- z;@U^ZX+GL3LLNjOK-IVA6qzSkZc!1(kTVGQO^|hu1#D@3ltM3Unq1^){%}bC{%`o8 zk`wmOz{_wSihvg2@rvWgc0{SH()7X2+@|;O39LXn&?8;KgGnBz#;HN~k+YJ5uH)V9Wmn2h!HZQ&GsEAyZUXro?<=9j#2RSo9`S5CbViDM2SNn;83?~YZ&>L zc1Vd~N{r2oC~b@sM_ZX^0_*ev;-`fly?Wpfd!^S3Ug1UdxTtb@RI$048QPCIvem1} zD)Z;GR@&^vCa@}9{8@MrYQKwR89kP!Y_%4|0Uf$g`zj{4ZOAr@$wQ>j`mzs#N1z85 zudr7B9)Hydv#YT(_wZ+)?bT`+Pet5Ke8T21hk-~`<#hg{0i4PS+kha^l1mudah~(< zX<3_M?V9GyYwang@5!U@PU4DaETT^=%yunR@>MwfJ>;0=)#>{WAz|2P(5mIVF$91= zKeGj~1Jd{;wtB(Tg2q&ZmK=i$mbW4md!yOa<73!0nr-E-EIl~g&HG!LjF&xCqP!o6 zh&W6qWnl;Uw`!XXa;w#Q*^m;?Cej)&SWmcL>oR{lx)uK3%&L}k5OKn+j6Z!>2!OgY z*UED7B6!j1QkVNjAL)&VI2|&ErMtds?hWTO(s;$lgOAfe73p0WN~kx-N%pW5 zDzAsXb@=wOZTvsZz`>lUvRg0jWw>oFgTjCpjoQFev`_ZFr4fM^X(MsGtvazt#6TYeur1G;dLLcm2=#H^ zbyY0_{vEvp6b#fS=(7l{^3??op!kIYhmN7xXu+^44oNj^(Z(FtMWfsySEzvtBAlS@ zVwHSb8&fXudRq8mYO^qM=&DY5b8$nEp02*%+F(;&(tLI^aYWVS>5zFt;3@y{IHy+u zy@NXy_%g1r5x=ZFKD=+u5Drh}uusFC%$yi#|H0hJl&@%T$5bqp;!bf~`J7Kv3Hrm| zp0Zdvwquk#J4Cm5E@9G(8?WR^ zgQ_IoSl;?Je?>p&?8=~iuyonLkZ@Ya!u>RdBdc1fYZphovu`-8{JxWY#wl?oc6`|B zcn^mTxcO5Nw3{tw$)!Rl0IVlqySW_)k>*$Nx4A3ZPrJBAZL++w?UUQ={WLYX9V=zN zDSxRwA3ja~=-nU~rq8;u_|rq*VRgd8W85)VMD}JqtBhYo$Z6TK-n?)8T&>rD?Fm4_cKNYD|4f&ws?zU%fSo&-?_AUNY59g`la!t za^(kZJbyvQf0fN$c!)L{BUj9O$6;RF1a zy8D)NJHDR{bn`7)akki+@-If>R{i*-fBME*``3b0wO>0PGNclKrmaA?su4^fVc`A8<^qV* zTOSS0K|1&~trh zxnnRLu%zEC*o#kAA{vs=AG(C9vXlAo`ZRu6|R@!q+S)*T(b5x!%=f>3_K+KG!c33mE;JJ$3dbOtUcCpO>`To5bjR>5 zTgVaGJazV6C^vW=O9c(C2`yaje7jE>Nrd9*=$uUfkDwH7i$aJGSyPX!w60`@d=;Fc zKBp!*FLk7W_>57hnK_PU&9gY#?$y5BuE?{d z5LD_ebT)9Vp@e3BJaG(qaK8wgN+rl@;#Jg>91+?-*Dvh*z6Mq}Y`>*(grjJI z#PKOR6yk8`xvq1y=*+{AtAo#jMW!u%3|8yWEXvgQ!1rLVV)`f;S5-pI4>#(akr8Es zwzcPzgya( z3Y_#99q#bv4GYw2i>KaK%YmWE)h{%~610(Ug~0l2gFUo_42y6u9~3`T>4S$5`IKH4 z(p0mtV;e!U8^y0`!1Exf{@o$p3e?9wvJx`8?5@GM2ItY={nuI2?$Ne!dnGU_#soU~ z7<~%SXWM#K<``V_FSLgt06j)MdqQ%9Uw!@%A%7)c2N=Q@!>)38NN@@~!EMDQwMSEE zKO)ef7%73rIbqmnL&bI(%&dDN93g&%DpbndLvx4&5mYeR2Tfe5{(jOu($zaKh+vMW z(*-=Hvvc1X3yUM?p|!OcC-)B_l=kQAsVxw9xAb=JPs+R7Grq;$M`_podj`IOs+#| zD1^qQzDKuoO!i+J31t%OMv7A4;JvJ4>;IMXE|={dCaZ4;_0y3bJ8K|WsGiSuoa&=46lS~;=Uk>Py6g5) zdKU*wwNub$7E&`MO3}9w=9H<$yw(OV)CuVVCZaXG$z`DO)HbzX~Gel&2$l?;YTuu2}-GOKPtj zH$;AUJalB$J@FIL0sCL`s*+=5# zGr5JYMA!P+PE6)D2L(;I97#J;HZ-eOo#2|l3WvNpZZ8))zK^$UbMeCiBg=n!)vBtk zD$2cJ{k7l6HNBR;>OyNO_)G1*%-?O6?z+O(YqN7@JX2JSg~`7wyVTCcUKg3O>j-R2 za>Zuz4ZA(WR)c+3PvUQ_(mrd^?HH_)z(cs=Ch zwK>$hOy=fM>QH*gr!6v_mbUoGcOcoOC8NUrFNiSv`-FGi(r<53Z6}S1BTf0D)XNL` zZz&D5s6_vI`KA%|#l2u!%>i{AG7PI&oTifo#1>EE8w%(R3^ndOG;vxVKW&v!25dVgle z%w#F>Tz*%HaU0tu>QEOx1)y7jelxd%HLB`Tp0R+F?#1!BN}L5l2T1c z!%cJla3N}bN2 zV&VsJa_z9%bnBA5b9ed4iLMx>&-yVH@t5vkw?Q1==DHFqUK8%qw{+h(m(-aH5ioPaz67_vP#+d% z?{z%MSm+PIz<+N6O#K-gMIa7!rz zTPK9L69Dft;6B3`cC{oyw0aa0QTK?E<6f76yhP6#zYdWPKIJR6f5IXu950X2(E>zmu!gf^vCGV7XgnY{ z4=?vICPbS=m?K?isAERd+tg9ut=3`6NBZmS9E*Tl!KYcU42__X6l9-Kem zl7f^%YNms}3uuva9w4^V#~oonG=r2_jt6bwxNRdfgxk zT7=2$upR?qG&n)@KY3f%A$9=74vIYUF=5VAs1*d_!~cT_T0A?Iyv&?!iUUOPMpybv zhc?z0n{t2{yvdj$C{_rfl#oOoW_?p`*p%D)JPT7&PzJrFxh|;L(qlyg0V4^VJh6sku{O)w0xlD`quYKv*icKibw1Oftl?y=ZB=#FPo@wA^$juBV(8Zg z1og3>!L18A0fJ8KFcD>(%nvY{ADF5W|1pFH$o!(&Ee6B>{}mn#hJ{cOzi)I%+S2k^M|>I)hB_qHp*YMJ(WqEXIqU(Y+=42DsI73_Q4JC^jC>D?Z3n zfqq_xAvPVRq6X?r|1*XUTkA6zufv~#{XKId4M55TF?0enH?eSGh<=cbIUtb7-zYtX z@418TUH-7@3=$zkN08jnGTVd6sSBkc2tuRn#B_|o4;$!}JPV@pF0NU`A$WNJ__$?(Ui(AtA_=Zjl%$2nZAD?$I^a z_PKeU_wzq|fB5nP?6uvyckQ~)IL`At4&t6X)}kO~Aq9az6gt`(#vl+b@DLY7eETPe zMg21fbO)rPq59OnaBrR{lMN9dh^klHEM~jM^n*DPhdQ=UI~89jy;R7E??>5C*;?aR zP>_rqTX#tNuHvpwdaa`+!`qp3gLr)5^g9{3Drr2#VMFqil1!2CfC}&O3m;h=HI4jg zWN{S|g{6GlYK0p}Dz$`Oyq>QPasZZ~l}-Wzv60|_Kub@EffdE50l(zvfLD4-?f?%d zqkvx?Liiw1m^wG`kd5^Jzx4lmJJ4eutYi}s5@KyV|09le-c9yBIx#Vk9Qb+hdvVba z)cPLeL7Fn?y;F zAP$PMz7LI!(rMavdZf8Qq;q{#%HW`LSKOHK2m8RODFnjL$7h)xmq-mncOM^=_$VMievIPJpFbYt8a&jvgDU@hI-QHy zif)BTNJM0JYb$ne6`wK;BxFwvl8mzZ(A4C+Jy9MUin$5slO!f(0X~Eq)Prk8S7}fn z<2YK_)FjiP^z1LN9(`yl!c~c@ep_#l>W)1g)2gvTNt-R|>im zHHX(8HPIx>nrFK>JMZl523jBbXjg4=}5;**5kVkAuNeOVW zp*Kf^m4?ubVRA#>6jhqT8+=C)5onBwdQA|pB6W})g`ldpx0E1`?69V$W@{+%^E;7% zCFP9t_o}DH{Aa(Ho?8_yHoGq#tqunKYl6Xy=`@G);2DC5sVUEUr0yhZ?Ek%!l@Q&- zz*8YX9J5GgXXlo_$E`+2M!ixKF;tubrPNmh|2d_el_NPS0!lWy9;xQ3sVVt*oMQjn zdv=75ps~laYb5`U8Ezy#nnxb?(qWdFUw5il&b4~E5H zc+|BDm&(ilRNiH}mRBp8kA?(y7Vtj64z0!kcR|j#wLuLO%u3|D6;9T6RI;}Hb9W!(yA|g0L zPO0D9ot~cqt7vX(%NL9h4vLSDZ^8C5hjm81B?ETkQn}-laIvCLDI|ZBgrqR?dOL=a zJ-+|dWQD=Vilv={Fql)tRu4E*2Fe&#R@Q`s1Os#xU~g}ooML+dMxn}(oj+B~Qm^ej zg-i1DSW6&3v^Rq{OXcVP>9bcIPxCvG-&z0Z!1{ zI?a)V*p(2tx^(`08NFiLxZFf+~ov z>#O6~aV(~dRojzu|7sIDxQbP&iqS}6lmGs;#mC1dlLAawB=0SdBKEuU3q5`qugA-r zgfC#qmX(j8V6zSvL>&dIjIB3S%bz$}!Am(HWDXNzng>0-mU?h-XNRVC!fXw}luO}z znVA?&CDAup1Z{>>}!O)pNBe$NLPJ@o z2xjngRe+ZO|M~p+v-ieNI2g~LuQvlH=zOo&K8&3UNa?7F0s}9?mU*|~#F6#tPoH{5 zqJ6`FRBp5!$1@tn>fUkhgg~RFy4wHg`24(qWF7EXk``&&Wu2WNfS=`!kr)=pP(*$3 zi~%w~Eg>P{tA@Xc%u+&9LijT{07Urt#>GWMSU6AeC9VVE|6Mlef8TaEQ#h}l@dqc& zd-Jh1f@A;>xK>|^e06L57W>rTI>m{dM4 zQifovO>1s_KUYakE*J6j0~oe!aVO=k5Z=w;#omm%WYZmxEg4_{)(x)+9&!gB zH2S%cFRzZ*+3>8bt$}cHOfeQ3)3*$Go(ZA9>$ji_xA1$BpfJyY`2qLPlL7*(3r_w~ zW$_46%ch^3Cr9M(E8b+9`QuOi{{6(*SnU}-Jv|WRUzwFQ{NJ0(^>S#+Jjnkb5Z?0q zbfia4VaKGK42!Ko@%HqNpeAxwJz@}(lH>8!7J2Mo~O)WKmLNC_vN0$@&H zX9_(@KDD&uF75bx<=Tb0@WyF`y8`FlU0q!b1Rc?3^E;YGUmpJc`$fqbFO|Nis0bfn zVrF)+mSMOtGOX?1b@f`{;K8B3*G55duhUXH`D$W2R?2aduZ2Urb}U~yVRv;-RUl|J zEq->t3C#La3Jz>JU{d(<(bT|~N(4PhaUAJB`|R#;zyIND-@U(Sa1?`HwkY?IVj{3O z3Mwi?sFGp^V=Np09b%8Mz=MTOdv`GLQcHDJ6}O(Qp58rrFyS4LB*wzRfM-dUpowujS@DLFCmfwVMr;$r~a`S~^fs#&{{ z`epBG_Uu{H+gX`#D>YD<*kPHCyZgbnH`(B`4EjQbobZ}yZfeSI55-6H(Myevgj}D`1W4deo&f;JqPUSVrVj`x`0^3# zy*LXZ#Y0a~F&p~#3k`yxR5hpf{ z%TaQlut};g9ibrbLO)z5LqPh$0}rzzFFWA)f$Y|B^1`Cs@1SeHX|~q%yoKnAg+)9; zsW%ZnSJA(}9LgGnH@09a5v z+ruoZ+Xo*2x1b?;@yUyei<8{fgURexh>d_`(Sa~No#>Z;1CH2`-z6-Y5=ECwBG5L0 zLpH>qu!cS{A4^MCzrS`3Oom?eyJ>=$PelrT|9tQCs6&CUIR5XcWJ=y;{cG%$#4%6| z+14I~g^jSxH5A=<(0%qyZCw=vsVE}C0p}?P9NXVdshn0&P-t&&C-DN|fFzY#%tkVr zv$C?X%^r8rQQ_`^X;ZcNfD_lqj#Mkz*mxbzrUC*bRsvbvL(mUMYnjXiRJi`)en8}( zeyyZoG}jpDZC zRKfOSr3sqm4EWy3AjMc$0hF_f$&b z&k!)+8N$H_g;{r8t|VBARFdOI^a5ZR#_xt!c=&LL245u^$Rc|@3<-+Zo0IHz_?uQs zUmox5#qH1a^@9I-YtpyIMu*EEdeO8b@;(5lh5b0VV5Fy~wr9l!^}GOV^feQqxoIjd zNOzw~J@qY6Vg@lYG06pAN}8{qTsgbAz_TZIJJR`y!>oWk`8x{48DTtM4txNOrv2~9 z$AJ<0u;I~35E_lPC&A@n1k#jmCj8pU)YR18%xZ~8?+&On37ELL@T7V51dLYM>!LP_Po`Ox8L@ z=snz_hyS@^hq&jsD~mX+Rvq{4bu-txz!~HauP0(Rr zr@f9S1MmN>Y=;i@2z2q>9KOL<_` z<>gU(cQcUPjXq_|EWmy>d7u1}H6hF|D1g^V;$KKQ@K$5n4lXcxb^*=hXY;!n+m?UP`NlQaLP>|4o2N^;+>%$6fK(-bc^ zYl$C-UFbu1-8<4Wgacn0ep^>r`#Uv*c}{4DQSz*XU~FP{K)n;vAZO7M)!~E@yJ6LRwy0-| z2V~)PAZ`jt@$&Kl#WEl3S1``++1Z*mEUHc1ApM$oqKbpPvy_L zF$FcX(F28$fl`z^HfrtMdVgVJGkrvMyB)F*ZOyf{nh)Il%$$vxY;v2|J%23^`i&l= z#t)On3}~AADW)N{Apfd&piQ51UijraH9)6eHSF|GJ-%XO!yo4?j?p7cWCcR5B?xjt zE>gt&=T5yrPX>Vcz0SIQXyHFW8aQBl99vx3%#4h;BF!ZhD4`c%M5D-yi>a|{%=Zf? zas&UZ6h-*IOqt>9(@ewKpvx5Efx@xPAfTXYy;Pt10_3@op#9_|0QLU_%~Ig@M6O&l z2kN@SSsM0zc*>f;XI?obz3Pl`L0)xLz9vl~QS~*g3t0Mh;eB-$U%+ukBhI$A#j0+q z#_T_BijA*bZ{jpwU7zpUyMKNCi>Kz_zkkGDAVZwIrGnoo;0Rs&R8M$X?Y~kMjh>qM1l_O@^_A@_wRk%f@rPo9FXBI8g62$ALuVY$${?sd*uA1hcya z0E#qI0FLqwQQ(J>%snA(8`Y#$UGhpUMk!Z)AC5Y4v0~)A_*0Kb+2GvtRm;`8f)8Va zkegBnO}OUcl_P)u$LZH)<>lr4{QMZYdc+8#b-CikbE)V3e7j%9{x7I9wDzIz9+RpY z7fAn3(wbD}_Q^X%bUJpt1$nl@JTja!H{ZRB$dn3KWkepLE>08>>|drHnVL=kQ2Z8Z z7%0o4GO=y#?KVOC(y-3s<6~hHQ6-}B*;xfSIqg(dVvtmNiczS=%Vv+l({k*C;CBfu z9HneJiu3O43#78378_;vg^t@i?nUvh*nip&Xfp)dQ)sh?)4)jQv4v`&T(0W;51*>K zq|6oC=6i*0P!g5J*CxYBYm?|{x{;cV=Aqu;kjyt?Y|8?bw5E-s*G*B2Y(*GLnv=(|xPw0kn2OL)a+_bs% z?Q9dfVqoTRZ=t-me`u6woCXhoq5pF$lG0MLI71ZTbUWj$VvM!>Feb>7bDU;oarNmM zC|1NCvk<9f{wARSW9i;0o#B_fcK}ol?%ima^|=0o&k{2I^AJ~js0m;mEp0r))kkoX z2YeOD45RaPrB%%#*gvz%A8PfIK~KppvV(nvu=61o^CQ_}AHxSeV{wvwz4Gl$@V`I(@t`I074?Db>uqu1M%1k&AFARzTpDP3}L zR(&V}vTW;Sv59Hc@eiQc^!OPC0N{WspDX7MNZ^n->>9B>0gu{roKW0aYn|EsI*l1D z8Veo#4ZGs}`k_J#v~i#{hL8tgENiV=z4re#!mO6Cxm4^APmf0=cCSt{@_|av?&F!* zJ>P?cB$Qzt;7?0eQ>Sb_{W}4HVnqoe`?+n%i)Z?l$7k95%9VRM$sn5QsB3O%8@#3E z7*6rMcnC{#bbU7_FjP9$Z_bm*ydN^D7<7JTjS060Uv<1JGT%boKr($-h?sG7`};rV zjXJ};vnb@oVl?Wj%qaZ>MW2|813^wLka*56&;o5+mrXp@1am-kKQ{yY4!wXVJ#5~M z6OjJYOYW#91IyDM#o@ajNVRmlI!BLvd0hZ;Vww@|6_gEXco(DAl?0k|lWoS#SUjF- zx@}!}t`2kr(LbZ>AA?{{GT(PRUn~Uyexp5xV}C4gxIXxy-gl=~qFCu%WSG>T**_*g z8u!p2yoj1^26}X#JdX4a0EF1>vpXGJ8*x&r4|F%p>EkpW7s{!o2HlzjKHrb5H57ci z%NwOLY&c3Wf?#Fo{_Epr$yXQ^rKZe|liL9Wv*26?E4iUvTwJO=e1M*Cu)Ck1?0N~% z_av8X{W8rUn7rtkEN&Qm!NLq^dEGvpXr;4#xhXNiUA!wlF z6rM$)P(V8aXwh9LUVYjP1p0T9QHG-Rc2a?YhQ({o>On68R}TI%b{!H7gRsPV-L0P7 zszA_Sjn+emgLTGE|6G+1>er3vIR=NElyp~;YZJCU`1KvF-6YX^{Nuo^>zIDHcw=KD zf{3cAsfm(`%4W5>bsh<{sr3!3s;Wvv;~sq!6B83OdGzR!5Z2Q$yvCb(gWn80j^?~| zKYPWQ?yfKO&)TBvY<$0kc6Rx1eA{uTZ2!!t(!bcMPJ3a81uYGGy?G<-u=g`h3V;G@ zx+6kn6PxFsdbP+ zoGdyqVR38gXI-#QF#Tgex)OzoXnKqiiF&oyU6#O*rMdUokamXUMK;Qq)`a1!hTyAX zAp2eiE5WME$|5a)0(mHb6c>TMVpYqVX?n}rhP5LHAVLaguB|v<%HT6JBQ!>4syZ~V zc*ZAZ$z1VFq+9;V-JZm6ZT?1hqr0x?))z%GGzp$VAlQKL1+j01hOH2$f?tGOt`eymuL z3Vf&{pl9tB&nD;bp|i8-*eW343c!iE!G)rDB~N1evTR*-@IT6QdpSIz^s>xDX)->B z1;5z-yiEe-jK4??IMbxa2%i)So=;vj+2<96XJ?CB|NHYt;~5arcDE~`UC+0$)8AlF z(Y#=YwPq2*diCgOz?`blG{wEL9d6AOm#R%KinNF@-uo5I)CR0ZQzUguMZMcLq%tV{ zEB{wqZcc1?fG6cR%pA`S&AmGQCbP(~E>#z;h`C$^qxJHZBau79-)kgoNdbUFKra%% z&SNCIX5Xk7GbpSnMR^`tTVTWa%J|Dm-7mf0&Gv{J3#p2DuBkQCDj5O@b49Oy0o@~H z`$5H+yU^K<6Z}WJ03H2^af4;?l5e{a!yL*-% zFc+}_CmB`;hX-6P$4J>ErVJW`H4=b9jvb{pNMML%42Nl3?5R>nvQmJ4vEv1*bb=p< za)o3CK{AEc$H6fSU3G6aL4XGe!0drcyG{SV;eBXJHPYKh6Q^!(_xntmMl<)N#ti?k zYBSd^tU7)?@&j?^Z)td{F+_t{`#u%figWl^RT(Q7cL0H3!yd0S+eG7PT(JV4lt9}E z_S5b!?|+hG_gw#ec`w4vun-2MU}sHDf=c4`f*@q;E!@j&0J8JaKf@I@WGw#24c$(n z)KG@lS#u>rc4hf7)Yo!%PU6Bl;<_f0qi<2d5XQEX(q zLa&y;Kx-@1eA6;;r2-w%Q>euU3gGO&9$yLE=~^v^}FSZq<;^BkI8qt%xNNn1N# zs(tT{OC6=Ti7C1MsB9mJCjb2W6ze^&KDkbc;bmuMm*_mDG5?;ymg$ac+pW`=x$gYb z-0U~o^cH^=pP!5NS*EyHR(NS$<<5q@G9iCG-%#aufsNO!!ILa#6S3gWU(=>1sb@Rm z0%zYgoBLnH2Tk5$`nvL_R_usrW4CPm%Gwj2t#n;`Bf*EX#?e`8lS(AK=2y!kVX9G( zdL!iVfVAp3+|gr8S5dccP`kghQtkA5XkCDjin4nB6?AK5$>~|lN}JNWb(W-4T{Ck3 z&?xDh+X$Zy>6q>jNFey1U)P$D zjW2%!!d=YTXfsa|rLd-(+*mI%<1S2$OtGwy^e_aY<*v+L(=AFjev&A8EaBOxm8oPZ zauT1^kNjY{c4gW8DX1H%9rC|k0AKZ)&eV$l4aCDNf0Y&au(f}|5zby_MWpbW?LT!I zSBThZp*HG?KhGT2zCHYI@*`uOUeTh~Ea5?b00x?WZC%wxH)bEYp%^i~1dnL--ZWNP z;vDY0SSa|+J)&^XF*!4%BN+-nHEsBXtMV1UjhrVdwAc~drd3&wn3Ev=h`twBbpa9$ zwvKaU1OZbIh6>XZc7^OvSZF`~BKEndRMk^t>?fo7+~<5$!WNGDQyRyupJJs{K71amW>tzx?N1lO4*15|O<=ZlG zX1sCXy6XC@y0Slyq5;srk(UGauCt4_#9kxY-Iu>dWl~sG^ud%@DS^Bn17Uu2tQ~!^ z(%QTE2w7+7X|`bg@L$}MnH@ydo-BdwOJz?t$YeRhyo|H1=*bFwU$s)}4Fa|PdbsDQx+~VWz+Q~E4$c22l8}N zc{k)b$n+u+b72#zG-iptN_TLlw011{eM;Q(eY3q*d<_Q77lHn(BSze5qjcOUuo0^K z{8{fAI?RkM)>+BVOP;7+k1f7x-A%YK_`qyaZpk>g+DNpOtj%Y;h;8;vQSbQ(B>dgV)cJK>*)fh2p2stfrLzYWidOm?fRt&C4B z5SFy;?o@BF#_(e!3`XPyq!a)qhvQ0T7Gpo*(7BTBv1Uo4o+Jzq2P|!D49`70S&z)6 z7hY;YH?mi#r+D>Vnx3?2$C(_nOX#Fp)9G!IB78OAVv{OrC)(+y)jT9L@VhB=`05P! z{ddw6RQNdg>AycT1dS}O#ln}IV$(*y5>c;^;OUIV)u_**Q_?r+*YBFmyfd>-m~i6; ziPj5eD#*^io4lpY7bEsD3Xzr@~~PZ`6V zLb3Gq-Y-$gW+B@t3Zb4!3Yo$IF$_wJd)*Ng6%{cYfw6mh>>w!->jXj6Zo+)$y+*OJ zKt0WRtu?TR;0T%!aeDQGKR&`pF<%>>55Af$N1>rAsG4kY%*}v;iAKrJ*C!rd*zBWp z55BdBBLd0QVW=xu-k?eXFfTtal<_{cKTc}xKYP!#nWy)zwZEFj?6;s#|F?yfXDshd z=IXc7=aBY{O7bF0G$`8YpAhe~bbc-!Q}V1D71EqAa>WX#o7vUq55!gr<1|)l+)0A( z10G0z62$fTEfzpg%qinf&PCyrChm|6$!fpLNSJZW;K3~%qWp32GXx;;1hrgk6nvdU zY4M~v76(L+yu*r@$Bq!2)@{0l2ZQYX)k~~xC8qvE? z>gs`of;3aF{*>M*yZsh*g~7nu24-e0EOPa$H6TC3b&=M z!#R5xgZ+k8%kNH%5w@LMY^?z$AN+zl5L0%1V%DxcGvRoAwY3QXhPtpbIa`LPKImS_ zGwVGXjTN_0gSeUwZH9Sx-h!&>L9&a9h$3}gtT)6)re%SXHt|*WkY?c>ziUli0ilOf zxI&6#qMjg)cXgNqZfhpVZ`5^UaWKnsuSws>k1K5na zq?5t$7!=Sx$g|I&R`K1%Y{w``AP4Z8Zuh<@FnJ*a?tS_Z+$_#LO+6A9k5{{D~7cAE=aP3?G7U-Cmw0^2~^wX-xIDFc_aeytnG_9fX0@%t z^K1?HQVdr}tgIPEe>{NN+?B|FYA$PKMHZis&SG+w7J08UftQbbdj-T8g`XqP#%oW$ z?VQ*RPgmy3Dc{7yAD^#}xlWXXz{Ir)u>tRg*NofvG9Hal;`E$#{xtLNinYWhvC0B@ zFz}@}6KgKM%<^{=l}roGJf}aKIZbs3ktuH}3axhCza7dmj$uoW$(ozd_8QndnjJWG zSos(m+UvKuD?3bR{@*h~YZyQ0+dLMz0t59-AFKGkB(8!> zWu1Sl5(=swx98m0oW;(1n6;%-^>1twe<&&8?)$|qaki2m!J!nK+XL_+1j;r+;$S^H zt99lA5i9IZxm`ol!sYMlPM+Sk=icHk>S@u~&#v>3?dvl~WGax{w>K5EF60?gK4Se+ z;o>c=8E$U{Gd-4Hr0}%}Z{AmmyKfeVKlPy`vKJ3md^c9yQ>c2I4GxoQ7!C^MTV6(W zTE^G3@uX=@@TB$rJuJ(Z8*ZPA<`Zm=(ZL*QU?edzBs@@UEA%E+ayrL^dUTpa-{hTi z;k$>4Us!10DNco2FyZ`tN%`U1w@jEl(DekSp}b%20bO3?T2ND9AnVy077KTIicrSt z$5d;PVe2sl%C#M{dz)5)n9oZ$IekRU{h^iL^7HY&9Or$sB7dB{F50zQpt;R#>voZH zDwJczpq(jRNX-bjl-Q#wk9;kZ->dpz$T%|LqLd3Y=b6Lc)2i<9Z_F}5fK*dyLNT}q zvNCsVC_a#5#jmCt%Eo#2)TzQ3vAn3p82Ur)E@ApCl25(wyD^b@@Z#|OuM(0reev`n zK*J$I5V8mK1foA3xh}4U?Cf$V7ZG$VF_xgI7-T;m8^8|VFs49kLJ&LN$_dOl;OAn` zJJeO#ri|}YS5s0_zHZ3K72B?uTu%=NQ*p(nXU^$AXCN@AnM)sHzbVOOiDylV9DV!1 zkZjQZxqu!=_m95!+TnzB@d0c8Z_E>v8n6l8G+MLhh(1-#3nrE^%AqzSgFSbnU$~yW zqKimN=PMNkYv<#v`=*s^ushp~hjN0MPxCFDe(fJ71_yFR)h=z99)w=BkAY7T6?0lZ zK);zsnxg)gM9)IpF05b7?E0Lg2q|*&SMx1ityC>N$=tn+7GNs=&>Zq4E{$mw z_puR|I0{8?BWOyh1S$O^245f0=Br~?l5X`>Q$tuqCqSOwWlSOGtsSIzt1-RKQ}vfW z=mBrVaadR-nT8Gn7%ZZajjLir$KpOSUzm=q3f@#$yjqul%*@T{+ba8$4w1jdo|x(A za%PP~ZIp0r9kZ5|4;EkTc#A7z-TFHM7WAfx_XsJ5xqDYNm(b1G%Ihx=9C-=x)d$RUrCv|scyzRNO;CRmI`a;A+uS~IRY zH_?f+SR0SOsUBlJ54<$vIn(nDdX;`BjeLr~wR1gsmXVs`mzWm5-H%u*=4I-LU3O

(m7_zZQZ|e&L@-fFyYpahC|KpAwjCnpJ&Kmi}^(I^l36WwM!G zhOtAxJ&t0xOV47{gG!;Od%8ldVbA%8veVOPlm_TJrCdEKgwyrT1MQ)H&uLe~_efA0 zP}#cyxDLIe#Ql7RPmldH%hU69P0y+L?B^}Z{1f_&J;OV!WN7ekdUvO8I24ym6}`Ui z#~T;{a92D|BX&hBk0wsTsiX1R zx3UY95gu`=I?HC{dm@e{dA?h2S}vD|rLYSlYIJyOZ2NA?lxHL6`t8A+8`htYjsBAv zOeywxCl~ttKqW6I11hyc4MKP;qqcZqc?m&-?#l8{Uob4;V!!Y_(n;P*Up_*`qd%kb zkw=t#eD0nek#p+RLPYUxT2D=n(`1>Xz(#$n_k-h|V>#+ay@^A+mwmhs77mBje5>3T z{jjTU^5QzwMzUbcrw=0wG&9y^-ZI4hQaM1YdJ5IA7x%>O)4zP|{Z#sRf zV|8m;juR{v0$9dhfB_UU^gQ2yBdSKp&RbOGu&-G0nbJx6Dx_G_y-C(QhjGFi^bGl1 zqcRi-Wxh_;;{Egc??$|~Tw^V7RHNGZ{>2L1O|Olw`?9>k5%MSXJKu#yQYue1%2*;A9zRsN~D<&E4pIQ6(pQX27+% zU*zJYiYvUhMoDwRZgxk}V!GH*N}G2P*0A%GNwbO#FKHKOhE=`WpYO|u-eZc6%kme6 zy4@w`qsa4A0sZ2q3_|DIFa_=w`)+rk$5wPKiL5_PzAJSvod@Ffrr|?KTdDrn3n1Zu z3V1>mX=yv}^%negP(MGkQf9o&Fz6l|X($N-heIP&X*xLi))_i6k)mF*P_QORoXDIi zhF*q-o3~U~O|1;M6`)J6_*4AgXH+AQ?qKiz!I+H#cVjm?Mn>iHzjnS0n4dVu4 z%-K}As>}x6=J9#MR66mX(T#m-(+Z6WFI_p98r5%FomSG4G@Cxy+PZA-zHY}OqD*~T z);zVpmwGsTu1Pv48A!CiWN^iG0le ze)dlJns4+zL!{ebu=Q;eUo5w^IbswJwU9hTpSGCKn&8B>d{&| zw(-_|u(`*VKEJ(8Wk`0C`9;m^DZO2M+7eFMk}*D&W7ypm>Cw0~6HGLhZnV&j73p!n zmEo>9+yg$hwF?sp2qDpBO4sI~YQ|La?Z5;_&v^12sWa1l*ypYi9P)0?p-_OWIA-)^G6-}oyz)ECL zsM3YbX(Nf1-!svhnQ>)86!WiNzaVF3kafeOD`U&gj&QSXl&J}8_2GjaU{H|4sf?Mm z-c*{KY_cEEL{5g3j=!qDKqK;5XjwIEjfS@E15?}95g)lxRtM=|v~q5&Zrhe_ zm!N=d8+DEY{E4)>ET2v43W>UDTnqvDqAy_933e5+7X4$c1kGvun&Adps%^4I+ z>on(;nmLj^#%)Y!tR7BKh*JkRz&~n4^Ff{%t+)yIbek_5eVhFlucn1fTh=Bj=kROz zVjHcP^J{r|9w3A^LjV>j(2rXA5tnKDqW;2i5yjvLVo2bS5W>kjgcGYgOA?tjt_v-C z)I2eIjn~<|rDV5!^3}t7NC@QByO(T{X#u~F-upF*-dsd2X`Dq47r%e*V31dufbKh5 znJ`WV_fhj_ORlgS1;^5f^5;meR*ogE-g09%!R@&%_#_C z4Cp}GmOpyP81=O7h7SQrZVz$bEgb33lX>S3T@owOFU;TP*(D+wM@!I-~6n1 zl*1j00!XlF0RTyPOi&6(Voxq4^vk!7+?taj#pUB+ukro22P|xHhd;jjglu+o{a&)o zbtEe*J$|mgO!BgrM?mMX8|&D6v3RhYan=_$QQD5sbbieQ{y(YJgXtsMbO`4%=5!?G zGrmko8+jiTg{eXE`xe_*%hZh5^_e|*gTUza@TYPLp-_#LI0DgiE)8aUAPz(<;`H96 z3NF?gn^3O{O}B><^4$YxS9A!-lTReEFf#;B6X}O4WGE}u9`k)TKH`Gr65``CtumE zkH))RK)`h7&pBL0kCQPcQr%rXwocxQTr>K>4BpzBJ@3a^M|ac_D;yAWo`-V?pQRtYjv3zD;fF)KDTj&~I`O3)7}Rd` z%XacEmeCC$@aq?YTLEG&EYi{7((!E#Q9=>WWcyA_L0cUH7jujo!F!#otS~u4FV)aB z9Mg%HBJ(HEzV|x<<~ui`{H+zi;LdG*myT5IMMtiFI$u>oLz)PEb#<}w;_R%fvAK!S zy^?r7St#o(FFb!U&palfkG9NLx;!MjZ1F#6^3`BFl)A{$HBxM~vk=(qj?60~MGk(| z2M;upD61M$e@qT9&|nmudFaS4IOdM=lJPKYe)B$vj)0RPaMx7FtQ>!u_TFrBO7|4N zha8@!eC6QaaAO3%GL(D9260m<7t-_;f6K>A+C6Sb$X&)hUe7hClO zAm>_d*$a7t>{!MZ{=MIk_g0e+I?Bt}0Wt_MXIo>Tbm+?zHhK8)A*`KG+6oVttO>sU z^YnUsZMI1Og079i>zOk?AR_E@aZV6qY!wTZC+luHC3i!}< z?n+fETq0Hqvia!jY$ZjB;^!Qu=!7OaHQOrXUJkV>^lau=KFms~sD?P)~}YEm6`rzf5p3bi2;UF2ZKJp$qc!If4&WVUaba0QI+`Lt0MGA82yl zsyQ5#^*C^T=1cD$F8BS_a&-lm5B{&7`517?<7!X+GGK%FR=0~d2i8-$rEx;d*V75e zY{vKQF@vmq$-{u|4${`bQY~FDa`FTxzvAqI(hX~C2EiWIIwTOrtoHGaw^Wm1XxuK~ zHg1=?((Z8r*QRhi=VKgBB^KR3v6|n0e_&xA=TR>c7Pm9K7?f(c;-cfFsLYuk) z#GUqou30bU$jztwH=r%2AFG}muAZCo-%0=Gtip`d)%zAYgM8+F4J+t5fC}62XI-7n zG-3L|ul$FYy~r#OlkvCQlKL76*dhHdj^=k%?#OR%r09T=c#m4P$5rg^@&5k4y1Kfq z+8c%Wrd?UicHG`-=C6I=-GbkCo~GmY^Xq0BDLiea;{|iP@+_rtvS{_dce#Qfe-wgV zD5JB#iu;CB;{{oQMKjGvCR?(~C33Lr*ZCLs*B`fhwjpllKEgCSyKTr0rsd5G?JF4TF9m?hFZJww7Z z7t-UZtCWq(-*qoBgJn-pvjiZD0H&}2pAD>Y zm?vlm5SR^>aoEvOj93WfO@Ed?PkL()Ca39$CiRG0LD}Prk7Yp-w`ugn-zL> zh9G<7NF$2*VEgxANq{L>0Y_gx;ONC4)CJZhd=T(uJ+ClHUPijA8$ua+iJ2x z%blm75+}pk9%3Bph;#UT9Y*L#*;)W9=qmdWAy8-U8>X_XnxYLJXm2ld#@`6yIxw{&^53Vt=> zktI9&22=dplKD=Z2=K0|D?as(4%O@3?#P|kHx;+%n32o*RkhuB z-%HK1N4*muMQd>ctX+g2TucP9Z}QfOAAd*^am~yF#zo%T$y`LY$OM+^eD4?M*YUPT zrsyE|;^t5)gwn>Ca^!6smmlc|rc{bR>2_f`#Ppnuv}3BzJI;K*sYz4 zcMB@l(&PWVGJ^#P#KHdsz_RaGpFrkk7nz4Y;b-~=O5SVz+4*7AeVL;!q-))3t#A6y zI-Z*eCHuo4jZr^2fBa;WmX-#W3`362&(FaY4;~~rKJ$m62Uy*I+W(T{z;00R!iRl?Y^;>1)8eB5IEb?2(tE4Zv zrDpy67I**TY7C-esW;lI$%K=8xE4SBFa4BQMbhU6VI5M$YtDr8rNz0B{TUK!^ZFcE z<;|Ai;PkH=b&`PSdqfuDic`&zts=wp+P`+NNZ1Z zJ7B+$>yf8SI>UzV3Vv`o4S$*7=hbM(fl*(2HB=8l?*g)^9JY;YOSh|Gvo5u(*8q33 zP&jb&7Zi8nipWCPkSXItB_ zLVF{p?qMCYYsa>w+v+lmGPs1e8Cf zr>6tuDlMRi=po-$gUsyRll{D#G4}0|Pv0=wDpWKULJZJVvEwPB-vUlH0L|HGkLia& zPDn>d$@^n`L%a_3`E2HUz*7HvixKqh9A8gf*Chnm-nSw7M=rjEVCrdyurz*n* z`gunfz2NG6RQRXxOKM=&N*ltP0d&JTZifxlr5&0>Tlsc!$wn_(oB)bb2x=0FK`&y_ z*uTNQCp7J;9u6=U4+l z2imnbFP~d;<{I?qsSeHbgRDeMsCakR)#agk%0Ew&%i35C9)PTOD@7j0lU3a28=9Q* zse4bxn_sa$vT*+<;(6PDGX`wZUmR$Ug4>=R<9ob`RjHFjBvHs2@ z*g}`vPf3e>G&-55CeJ{OKk@R3nVD?B@ftuuv=8xa&{I6@!824md-}qa511%(0$F|V z9~T!I3`jJ!NbZNxq>?055M=)n(rqdzl|c<;wrvjn2qGbOJ47T($WP4N73I#_jA_jXWsW@aD>k zq+J-sJd9rh|6D?d4&Z(Y2Ceo0YQUP>TD}wif$P)lNkIFJ4@h|QXZ;=qS4;Ws&e1@% z?njc1O_a?}>WyO25y(MIoqS1hpZ9Q~vHKnpvb8ZBX4tjTSw1EYcf&%cAi>{X5iof1 z(Zm77%di@r{{H?Ic<}H=O^koiqQZdRyc8cW;}dq$qMoDl#Ng4vLVD zoD8M9rbBEQ8iXWM$yB)K7(yZvol`gzN;=!TcRC~->gt%sOqu5`Z1(+p@9X{(_w~BJ zvG&^gyS{69*0Y}Vygy0Jy1BZ~*6K1R(;S@HGC?QxO8h*;rslF4T1>WvoaP7q@d$|@ z(u8|G^}_S_m3B?er;R&#bcWf#x$;29`f|)So5I^GK2+lZ)vf`j`#IVhveqT4@rjvi zNE?Zn(B0APrC7*tj*?mt&(IQkaNFNtss87+OSWT@W)18=1J~>p+oZCT9;PyljEpK4 z%cq=+CR(Par-cHic?!xSb_TZ2>T@|_1I)c4{@|fjQVu%TKE0=t(&u%I+&Lz=nje2N zh^&~;P>GXm)a7cY@fPrjd?nc4L-on^zU7UPNtH1>v|N^~pDZumq=J1T{`dpQy)iMH zYFVJOYuJ9ujNII4rDRRd-667gLMZ}Pi}F0ShP9F97s$l$#A^gS%g>d>=zKvcnXtbX zF*u;2E?99R!~LACvY7hMg?tnucAdIg`w!wZk&0%wZj~a4`;U+p#|UB|q`A4dqC%%Y zQDrFPd($z5UyWgK*VhAi8;Aftf8QK~c(^NUuZ>b84}8ll4}r1O2qn`9*n{02ugk7) zp|XIo=>=*+7(~hC_DIY|%Lm&f5)FyL!9m&(V6|DE)3HAVn#IpL(LQ!{xw%QffgLo6 zu5(9|{rB8|^LTDIapSyDn3DUc2*p@l5S?WX#h%Z!6)3OBp!PFdf3jrogl;f;vlnQ3 z*%`ln5RM|0V%HP1cL!ISK2RvJD#s;l$k!1}2+j)`4q0!akAI^5I+xXTmS$s3!0s8( zUU;ijO4f-sX)Z%ag3wljUFdL8X*BV~UT@B|Ry(Z+c7jy864~ezd!18FhJ>o)b8?*4 z4-rZr*zip%Si2%p1;ay{$fHo`0OzGWJ~ubVn=hTE7L>_t^a(2jH5#jNu&wQY{Rtw` zcquVr)2VQ>&uqCLoO<>_;b9Xh>~#4n#X!6!NjP?ps7Vsrag~=WJebY>TGHLrbbN`} z6AY-EsNs1_S4A2Da``I?@A3b)jL~^LQptv!zkqW_f*dDM*E*DViO14?F<79`GdHSf zXq-!XS(YC;Xg-h%@e#W3SaYt3!PqU0=%}cHI*guz!Yf=L~CL~R#gbEDu&R{7i;_UM#Hw30Bp;~#7?Jb#wm?JIEDONb*-q5 ziQdO9yz9PLg>uH_b}Zu`{WD&?sGrr><0r_a)SsP~Hwn)(`LLIZk49m8vDd|l7u8vKj7bp8W*9!CE(U)JQ@2(dCJiBzMSWP>|$=c^UYq%|?y*v-W z(edu`N3*8>{>6i4KM-W~4h3apJ7(Z2fNMBDS#xt_B zN9yd?3f}kcQdRgaT4!babxP(LsRGUyiPL^iNl0S}(}>oZTm@R9Hb7v|hY9IGT2H0x zL>ove9S1-R`GBVjjBy+ZF1xv9aQ^HHs?$1lo!D#z`6!#-3KC?{rm)+d?5Ajw?%}7k8Z$lLa5Yn`viLTPc zzb);*-*{)%;`s49gBP7305ZEgr87xNE%<$H3cUxgA(zqpn|8!UkR(;kLrd zB8|(U^@YO#@@wnrJ_w(0*+L|vllDVwX*ZGQusGQ@Rk7m!_6Da34(kbvnRySj?_|jz zy6S+Kg|_&^F4Hpt$1N>e@&gy1SK3U+UtB|DOfAqy52X>odC8mh5gF4R(4gQ~?maaL zk3Ikw5upK}Z!e7goj7OLq(;-TkS18l-TuRmA?gI7m-zmP9%c|$bmw9gwW^B?*$1#} zF+0etc5yT=S{vbf5`u7Z+kaSyaS8WGI64d~2fbyuYxD%XR$4$ z&Du_q$cXjI^Zd?r5`zv&HMMcH2ODAnB~5O4L?m>atYu|Ye$zW##h=8LXqcKraOyr~ z<%+>X8+jgdy?4X?0!PdPqF9!{67oie{O2OKvAQp+!`udU{sc{_aGOAN4Gji^Awq)h z#P#1xN^W+Qcqb%7)oIHBbV#D5*AIMIg4@4{HiIT!Swx<|JOspde`9Ax%zqdfxl z%*k?agz{dOmv|%*mL}FzduC-JKd2mA;lX!aOpGV3m~8HgEwnyp3gkb3{I%q-*F%MZ3lOGJ^oYBnV=?7z-vphux9>4lDZgvp z;-E&lWHZqoPDNGy8AVe}-zmG$SIF%ydE`-%Jp46prGpd5ML>N?S3ioUxBb5|GS~Uc z631I#@wl!IKI72`}DSdn@Nna}anX4U4hmVKt3fOmO z63FGt2~%{lup17ck`gAXQ$b7|+6&}homc0ZX8cm=+xzu55d`lQQadoU_rOPei}UAA zhyHGE7G}v>Dp*Z``g=cnYnPPQe%vvL85H?+Qn4VezLq1rZ}WZiZsE!ww?2h z>AGBOu&+2}tV@?t*tPrepG)$ZGp`uJF&I9i}XxldT0DZm8@8%%r`AO`!Bh>uOZd*z>YFR&C4bz1=zogJ!BopWzrC%w@2?blpLX zW%v{++WrT3mfNXBM?mw36odMi5fFk!t2>f=tM;7Y*`kH48TQQkoy>)18`=^Uxh2b+ zhg3{@Aak-}U*Ayo=e@d)(xQ>8qn6D75Y=|apw!k0t4>orBEK@A0$oO{XE~$rMv9#4 zL`|FcXE&pJUufQ5o`0TSaYR*BFP4FKq@R)z#+Q8A6CH-J!(>4vWOaG~{$Elvishm} zRS|Y>Fp~-N?THyiqW4JAryv{~PCY(iU}l91*UTX_#RMi6BY9Zg?4M)_FOK}=LbT+} z7||)1=>-Te5qlw|;*Ncc+#Tg_K+p@e1Da(@)a|?mXFwR&mfJ~UHPPoHnO(E#y32xdn8`O;e^{(Izem;vA zN>BG*E=!K;bXKA7OmWaXqDRP>h8ra E0kYK3RsaA1 diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png index c4e2a644d83284316fd88866e71f110bd60132a9..52e81e00ceefb98677f728520b3d39fc37d45e33 100644 GIT binary patch literal 2993 zcmaKuX*kqv8^&kGkZtVBmI@(T)+}NEDtlSRnCy+jh)4_yF+)fal2oX_?7KXcj5S13 zj4YF7$b%Y8QzS8CdFMIaZ}0KG?}z(7@AE#+>$g2_-V{4<)zh$dy1wrm|&$OWghqi-OFhyhRx<8NV&P6UHzkpi%lml z9B~_pEBW?#FwZb-Xp%*rjIKs{f9yA8RkcsV?fQie8;0Rdc`%0}^GY2#)cLOo?OAe) zod7^_@vQQ@LjEYw1`4aL$UC0|>OsH+Tsdgx5UD)t1FqAwf>ULk*k^G0TZ z$Pu`k^|7aY4p5lM7_r$#>?qlWcR)9ZSBO?1(B}LSLd2k~O0>`fKrpyb>DUr+3_ijv}fa7Fo{oqI2-}MKdPj|Xc z2T$*+x5Neqs*ik)a_8L4ckevW!N12_DGoo7tpW{hl0OjpnC zCCNmGI114B&B`6#T0mwlxru#d$oOC4ic4(Y*x9FeDZg!Nt1Ts{fH*(D#ZsiHa*juR zx!30L!id@RTdtqC6Cavr6Zj!HjEy$oUtN&MxaD`);9;jSd?_z2J&rCP`d0i$gvJ~G*>NvVh3vXze z?Zbmt$$r1^L54EY!uNaMUey2h-`a0}@|MAv^vS=x#Ec^ud54gA5ih7SfL!a%@)Fxw zUFdJ$TKnGn-srQrzBACd^IkP{xINi(BSQx_#683^GF<+GO+7MkUA|BSJk~YOI+9@$ zNzdBf`4bnqGPXR`L?WrA8#AB8tUKus>I@t6U37dJ7}>O*ggQ(He-_Lk)%1f{RUWK2 zUO}v`1P`%dDHB|p$(5uwlk)pVy1DLeK8 z@@)*Y=#_B8jh|t~6VxhiWA{YKm1mq%1L+)6)31=utKUjtPH9xV;8CFU-l5gCo4*3Y z1HyoXZoV1`yZWY5h!1f_W$Y`VhRgzp>n5MzR(-PDZ?c%-r?_s7I*^$rUiEL$)!_-ZL%!J5~-A zanQ@&%LTmtfC1xSgeZ9)H?wtZ`;Hir!SVXGw1#_6{ezBvyXYJYr|!`_UxLv6IzXEffG)4DBj;AqKFUADbCttWX2&|)Y46u$y1x( zkS21I$n?R|W-5B#@VTccsiDs|*$DhW`juS2I>=Z3E}F8GGdRrXunlfoOTzRg8!Hdm zOEL*YS3Vs3isIbRqqJQzZQj)xBHi|-yNzPECQWWb57J_m8Kj!wm=tAj1e3s%72OOd-)s~*<_H+XeR zZwRCSr)>>957ew;DGQ~!yEHmH3N7m7-VlBh7Q}mLcR+;J8%U38JUM8;dtBoU=_k*I zk>RH(ZOUkPQg<>FK9zjWX?>oVq!IR7S*Q4slASH6WFz(iLRm^$HKa>qA0umFiY?A# zIt)sZ#8#7pXyKtnYjDlh2;7x>!_Es{S;Dm5iWJ*%Q|ttD#HkTb>bqJUGV+vVo7&jt zcK949T5gQp9C@0G*VE9?kR~Sa3QGfhwI7&wTvtOmd=cMew8c0_2kuBEH|*;%14^CV*yE*@`I0f%28 zPtLXa)K=9zuXx`QIyW*WoAtI(@bLqv@hHKlm7KzNmnZbAqQLW=eXBb6SE4usNAJsSd}9|5 z$d`|n+S~3&3J%yaI!c>%cgVY;e=l~1+*IfI>?r}NS7DCIG}uA2f!e_g`}1r%x{&Q2 zS6>LM4nLJtVKa`ZM$OwX?uNG&szXoz(=dw0bQwxysmcHYC0NkwEh(q}YCPRyg3mj% zu_$0kBC;F?C!r7iKc7a__}!g`J}Sb-CWS<-L@b-QV(~>eR&31_i}&7gBmk_DOfNaM zTB7c9_gHBw!1Aw5N^BT#a&H*;zBaLDS3dOhAuq5i79P!SDzE{|`f-|Bx)T9NcPa@u z{l~l#LVVDq%U_ko9w#lpSkaAL(opP;4PuuBp%=79Q~fD6bD05)0z=q05NmV0 Kb2VqM@&5wVhLNlQ literal 6525 zcmcJURa9I{u=fd0aJRw2Ug1A#4$rt+E7IgK6}#^lrWqHVaEj zzRb2t1%X$7SGDk^vk&@0= zw>wU{2a=LU(s{H$YZQ=>!(4yNmZ^HUyEi+n4A=9hFo@^Pk|K^PM^M3R;oON z_Fke*5Q$6&CnwR0^nJ;_Pcnw25b}a)Wn|JI8J-qIE4s0|nmAaGqILre0)beh0_e#9 zb#|_qu}haE9?eA}ksiBKz7ot7wm{d$FlouZpMl5r4i1%7RS{F<4i zbreIcf{7_j;Eib-bHRr#a4NxX)~$vNkKpNCwO~y&sk4Y|gm1O#oBHB(i^O7447>lb z=rx)>xEzUzS*DK)g?_21V0FWwR-TX%h`q6%$a+(JwY_cS36B|4scPeE&mf@WeAwoa zrU!dh^%Y976YVY@c@NC=!R2>|W|SQrU&Ruojb6-aYxF5nH*L{6t#yk1=J;#0?#`;9 zz=kDd4jg0cT z-J;2o@(^A*>zI&^kdh$_9$wx&kPGt!L{*g#iX(SXS_-s3#RU6YprAM9K<{67$X<5- zqs0=O2?8sS{Vb45Q%4$=kdV+|J595*cvd$OKe%w-dN~xzkMTnwWi|1|jg<;^l9V6+ zwhZK^H&^yL;(1;L(<5!F&HpDCC8z%@<`lUp# z)mm1sSPR9xp4)U~JybAWm;1=#;S-boz&) zN)kU7*VQ7E&nQZ+?t<@UM#d}spo_f?>%uw8NHS;E2*BYZX`RB zYNjEP#SQ%G)vFJ3Dp`fLvj6frMA%vA#+4v7Ns(%$L&=!YE#%Am+giFX;n4hKGD=}N zr*d`pi#i!)Zt`zFC#JwQ#V;?dc;n*ZAvdgPQF&PvQ2U20TunCxY}nuw99#xTC&x`l zgf-&HCYK$Tyoiot*Wx$rWtHz&7Qsl=b;?wahG7~Q z7!=pz6UDX{iPv}AR%PN9BV#bN)+w_|?KQ<2tO!B#?!cE7Z7QmMt%&_73m_MlDx;;f zSj99s4N#Hr{pYxj(;!1SVr5>6;peXg_PBC%Zl85_Lpq@q61Lo*ey zKKhuD8$8hFa@e%T+g$w4jA72hHN(~Ehy_HX!@^dF+ci2sD$RRIamt}P?UxZJ&%osv z<-IzVMICG@$gw9;wRT`BQC0GBrq%56+Kyk%>@Hc1hf<$l5h;3=Z`7c$ zSa+UZ0<}LJzA04p%$3Nt$KF=#a4F4`x%PD@eiQx!xpq8l`INz`_Sw(~?Q(rQk*Ubd z1$M2X>Vg6Vd-8SO>ks`fdZEv1*)RMtiB`G;?_*w`*3Ax&*di*Fz;1LGG+hWpG?CQ6 zft&PWba$o8!*L3LKYZr1M??Ru+NqrgM;a=0UJeQ+e`{S4TpSlV!RhSkjOY8^M{aaPlvV?t@rp|YDfM1Kg^ac%V_&c<=A*tu&TA1bG9-nsq7jxGf zd!E1z0QffW+oX+Az18TiHV?OPgG|L2j&@eVKkFq+X8yq{j9!dR~ruP}os|97aYx z^O4cD`l*`|GBG%o@e1;fjDA?Y-N{Fc6W_V!A^FE_X43=NDx2|R6<8} z5;D3yv7M#jOTKCsQQxx5HN*K58tJ( zGN0X6%IqL*rSG#V$a6wo`Tc$HEfofX!HBj_T!D25(3>x#uBw~QH?U_$nBK=@;gJM9 zUM>PHHX_!7pB`tgUg_5iIvAgkEyuEt?s|O0g*Wxi3H-9(^!iRA>iNaPU7w|+)5w!; zFlGM|GOgcNsryx-$*X0BnDLwC{ne;GBa7<0g7lQ(Z+gRy)&}se8rQzsb1PhCW^KpM zB7|O0Y8e{?+C)#Y89~nJCgOpMH739XyDaSUJAhJV3wArllU2H~J=kLQ2SXii#N4JGq7qpU(zYw_?qvQ`4x2AmpW6-pWa|gsA$-)+H`{ z&rE2nXQhc$kb+MuXj<}`tf@Cd^3MqSZ1yFv)dEpo5cC#_aWt56cwTn<(ApPT0@@y_ zFFKkdGO+$KaOJLw6y3W81|*-7zyl+|kB`^uE_5pe){A0?ex@3o;yptnzVrpr=l7ym zCyUp2zCv{-R~`8(rp=GxNeYsLu;})p9-8=_Sg!mm3E%n)zL9IV&l*(!I7xij>lI1< zbnl#4)>l>u#HEKb>F(rf@teYP>G{7O?@Y?^rkd-+*<=KFry~Sj<_!?%vp+thrc(U+ zOTJ)Fp0L;tuc;BOcT1V9wR|7dlI2@I?y}%p<;N%w^XutmOiWx`M*5t?gy-@^n!uEG zWhnXCxw(`<;3fv?)~B9_vRUR&W`UQw4^{G&a8WH^DN3gHWYh*Gb6KKgf#dpZ!4e~; zOLXHFJb0@0$wwb|j+sPG5L=iCY)iMZvx82?&mGf6jA#Cw1*o^Tl`QUB1=6s;IV+vs z@33y zaONP+4Bs`!5)8}4s70IxM1K8k_hb(RCtCmR^coF+RZ}XXs}SIX;$oLE`DQNreoRKM zITx7d>L1J3N}Cfr#-xi|p68QNpR+-pl=836KyJq(Q;r~85fCWaTkV_G zCO>ymz^yx#m-YHS3Z-fCsSRT1Bo=1CE9j|k|9MJa@jYkhepS(tsMIID=5^nqRcQ=2 zm~4UI!>XnZ?`>oaQ*1oG4x1w{mAWQFY7eiYZQk}j{s;^VtRK}3$VM>xAEs$Y#cqmA zY5fC`cW@WXlyURo{xN!ktKYmnW=kJDY@+H(oZqmVdaJ#QJ-p4C+5O!+WrvKj zoE@8tqg4sR_QgU)m87+X&o|iA0=Ee=0#EFG(7joTG*Q_&wwdwY0X9vGkTw_@g3sOklO8fpeFcZpp0|#oyTK@kc0CTbp4;;@LBXxx9dS^=VfTA;&j@fZe%U0ImvUpovn~GF9bjoE6k92$j3aqPo=|lL+tnX`pNMiBkFQX zkfuxwcbXp1;`aEkc_iqG1HHpUO{uD?$`24zjjjJ3Kj^u4`rt2chH?rjS>4k35U7Mn zJ6|}*Rqk3^u-K5dviQ^0>NZZXIP|wG>wNV>Bt@{7m#~H5UgfU58IcW7bGTT{=d_#& zso`Nk5rISnQNuwao4>1j4hu*o3B*1%oMlS>)1?@mG`i2)8< zt!}tE1%*0V3cBeLx`=qts2bhdhu6?7U27KLl7cAoDx;a2nwz^H{*12&oF^P|Z9Mz9 z5sLTc{2ZcIa_~0<1A~C`KP_NJ9YFN=a3Q%PzC>37TGhArcOWw7!!9=fax=fU82_2O zI7w!|>f_kq^;75WpB-l|(xx1OaMWPP^+zU5yPD0xd$CbIS8B!a>)xT*Z>1zF7`|*_ z8Sv-vW-Z=F%kQiA*b7&~+>+jDjK3RVVOj+V0PyhdXL;&co&piQl8TTel0a1U>{h|c z+T>GNWA=T6QwFidY>qOno@!BF)XLW*IF`Qus?)og_R6Uf>83{JAoEMDd3Qy6kq=o( z`}R&ny=9SBQ&Y7)c}WhDgOUpXz+ZMYgFos%DgeC)B_i6jZz!<8rYPW>0ZD-Js; z_-9d&Z4=vRmY$Bz{QNxr){~M@72&mpsKJ6N^)giQ%h#`ICbXwftaZ53O6`eMel?Yq zFN^t>tyK&pNd11_#R%m8a{YQ1)biwFR=aeWq+R>2nyJ&fmvU0Lo*@jsHZ6=bDqWIt zM&T`)XzbQ-!a!0zh3rN{(BqxHS)eXliiAaq7Y>#5+Sj4N)3G{Wmj$~SA73G7xcYMZ zPgVBCT_k)@pES$@?_CikszSB8x|mPWh8_)>viqchIYMmlXjzBXtlL=p48l^{*$K!e zCSU<-Yzn9%nAtODKIWu(OCcEjSH8#MTol=cDgPPnhDMh31UDQ9#QUQRL5Qb0vwcWa zXl@u0;`d}OIiu>j;&be4E$`6U8CLN7*QuH+Ov|@`%cK0cgqlN7ZTa%@?tun-2vli0Um&pS0HEKXv5Nkms+hV2`rb9=uU?J1!{?Oq2#7bPsQ4C^1ah^bdv zE28*?>2Wk8R+=0?U1UdDz5;W*{xkAw6b& z3iJZ5!>i(y7*_@zmy9q6LOBa)NjscQM}CN0SaXWn+HkYDAIjm7GF29Db)Zd+24Uiv zbgod9tC9kOZ_FebH!LsW9yz`r6%L3fJV{~48bmf}cI8)=iWWiYgfBq}hX zG8v#)MG@RTLOU2oEuPO(kQQ6>m8)sFDd6c0l|t}aS^4<*6!>PpY=ZJysZH!!D43W7 zPGy&5*;0r|GvnAPNR@Er-4#OHJa*rL_>>F6a`HS2@@LqO^eRLtVCd|Lj+xb2RvJF# zBxVi=Srh&CGFoco{*c@ji=pRB)g94Mzesk$ab*> zc`7O@6-MDT|D9$sM2@gyA&lGkPO_g$j~lDyb-%reNbWnssiXb<*^jv+MrMGIgSL}7 zivK+8Qj?b-Kr4MJRulP>M*FUIViU?63$)RPBHd8sEG;7g1Nw}iXtawlPQ$^qqu-fjsx2rKwa#ydi?v%Mb81M>;aN6|H(buP?rcUVeF>7V*z!^`H`r6cV ziIyExG#XhfMpHRvhbg(#?Rq*6o!AnmrS7ZHlIv_KBqb?YCJ*@>^P23-8Cp9~T7*oD zkEfY9e=9Byvzu~aBblGva&j~0u)02P54f!#-V=D1gB{OqGm*sxHhlbEBoCy~bQ>h} z?&#|5G+riYC<@GbAu~iy+uIVjouX`oAVs)ZGQUi0HQyP_a8)cjLZ^d7t5(ej^d{yA zXuab#E9KM9C$rR_vP0yS&j|?^Yt01iX)1kEU>b>e3KV#6x6yyCAl_MBeS}8YP5!qm z(dp^w5;q&+^)7I6ub019xPTM9_h@N2ChvB)AXuuIR4=0Nl|hq3#-WkslVfcc=Q2#o zpTZ{OEKkfsfe+0>z*vIfRArVx9SA*h(k_}ZV%r2&Fuh*Q5;y1^u2GXx(bCo$G$^%a zMTe{$1sznkw0Cqs0LgU7WF}YYp+4|(h(G|^(PF)aNN~n})>{+4^aV;4&cx9aVoL}A zeVCoq;s}=wV}$nH{>~*Q;fL|-06^s1lJRkI=vyPYWR_tc^R&t=`0+AV@&af~ArGhz z?`&dOca=A%BeMqsNJ_yT9viZmOGUV> zt& zjl6j{pC%_S?0tu%uqDd^ZN_xss6`HI8Pfs-T i!fxm$3hsXu6Bb6su;C1yDIya+g8@-gSE!LQ5Bncbu9KVq diff --git a/frontend/public/favicon-16x16.png b/frontend/public/favicon-16x16.png index 88d16d2d61a6e9e01afdbac4b1e5f460753b8e34..8de6d05cb759168ff023c5a96e7d32d5ea743afa 100644 GIT binary patch delta 422 zcmV;X0a^Z+1-}E3BYy#ONklY5PiFC$j9X$2o{nE1~E~v6~#iW6vW0x z5QS)IX=x*BK(Vw@Ay#Uo_yck#icO+H!NMwq0VC-ytUJr?amgllL|Q^qaaT`${j<4 zOlYY8b}*El#edrF1v2R|97+R?PNCi#9XPEPLZJ{G$I%>Yy91=Iyrdf#25ufn`5x&0 z1ZtnIqHF%uFesKvSl`Iv`sNnPE2|3hJLxKZp^mu#p7n;-YV{p=_YY_^e$^nmwM{$% zX*pwJk2d{)w7g#bxdIbQ~Zc99qPuY?eTfn61WS7Xcwx(aqNm=_hrDgqT7O0Z$8p#^Sb$Me(kNyW2gBuSb8$kXb=FlD&Ah`$d8gV)hyHIYa>>0zKM zAhRqx1>gu-1dM|Bd_Es9isC%Sal@OAV4E!&9ka{FeispQx!eX;QxF6hY!njL>2wV+ z&{1j;hc7+$Du0r4n|Zc)K^B?Id5U_CZn}LI%hdMOZ+)LNnnUP(Go4O1putf-A=gHW7_RZ?p@rg(qYh? zKJHQ&%DN%Z3;i@TrWt=eN#am^2;vPw$UbyFU#r#5rc$Y&(C2=m(Fi7!$*(O9G~+K8 zR9a59H}QDwRF8BRoU6Jso4gmalpMhFA~dkGF>eCTMPs zaUn*%m>A72XX1eaLE~v5YK+E2jaXtd{srm*#8$)Ai1_0l5G)s2IZ)p>yRE}+X=j_= zvYC(0yf^*6uQPAjpwrf$mNJZDs?(Tb{9(9F0=VYD&2k#UZ-2glu8xj@u1;f_G%68M zj?u=X?K8*vr8}d$N(O3aZ2Mzv$RC?C zkNv(%CzgTI^j*r2T=Fr+W6S>J>zTYg{ve_g%K$hnUIh;@1QUoi3S|T3a+!dQzZ-!X zFcX-F2r3yEzHpfi9XTrS?D=dUrF;s)YNYmNB?CX6o_{8DEGqy4h+&^Sp!#tNkOM5K z9xx7rk^z~ZcqUC5ehHJlD_VpN@Y^I{vo2TBGCPj%Tnaa>~=z>}K% z(UF0|10jG~Kuw?@g;)mg9@vh!5pi%R8CdvWlR5iBU}@>i?fH?5{5k#)2qPZG6Mv9fhk?a_L{gzeom_gElvv(V+&l`bg#5nub#f5>B0c5tgXG(Hr-QjRX?-25oqWx4L}pj!T>r5 zto(M;Toa=_o@Yd!4xSU)suRF94{m7ua-4V7IQ;_v0RR7WmEBbU000I_L_t&o0K^n5 VRuJ!7hWr2k002ovPDHLkV1gR-XY2p~ delta 1378 zcmV-o1)ch>2J{M$BYy=cNklaMZ$VdkR zN?-r=yV&C2f0UoX5>9e@?&I8ZzH{!q=f4%nGLuM_nN$H(RDV>w3Gp0RIQ`9WTo<^# ztgP&^nlh)R000*u?gRG_bz=5Xz)}AGF|Z9buFSVU4ZwK_2jo*^fdt)$Aqv1d!IR*v z7#o;Nmsc~!^L#PH`y>p4vu^M%tW8B%PbqC{Y;25}OII}mN~)`?i*`Dl4cKE%kRgt9 zfG@z=5jdA*V1F_pzQbH`d3kw*(w=Gtlr%9hkt|8lCNwQ_hJ#Ev{~GdBI2C08jzj!} zxni_+O9oI=Q*%xf#iuUd%{5?2&6##SOYcSpf5eMF^w6~}_fJm-~7(WOA zS=jrm!otF%&CSg&Hjc4h{c;P$Ir*BfGoV6&y+@KnUVjE`1j36Ft{uKeY4XZf~Pa%Jd#1C??q;ZI;X2mqq8K=&Omlgv&G9NYu> zquFfUFMrpC^Jkfa-syP(tZMJ!|GSs;lNX=-tW7%I%}Lx$UO9#H9qhVAId65Y8yr7B zusVLt%_wa$3)WyT)D#sJ%|>5aTf5Tjc2}X8q>-@s zHkpHh0KDLJvQS<4*-I#?<|yd_9>V!JdeCUJvAu;M0sth*IP#6>Q8PXHPt4R+ciei< zfHW#>l-63U)`-^D)(K`0kyk@M4qS;MbV(I}ZhaTqCt~*aKDen7Ug8M1kM((u9q8>< zFn`28Dm=z=`ll1uOm9fI_IX}L-1Mm%uJpV66E!^Fi!@KkOzs6g0R4u_%1SptG%EoF z?d|wm)(RiTma6r#yvu)96G$(u<-!-U>pGt7uFs&fo zC*bHyR&)TOFQAJt4$eP9vk07qxzKfBMSqIHt4-GXSliIt_nS6F->6B}6U|10SFVR@ z*dYkQHi1gllctC7g7auN>Vxw;~lH~C>-Ae|LpPz4vkB`4?Ee>W9qTCJs-RJZ5%IA=Q4Kp{2=lhogfE5>fep?Zd zS>Zqn2M?l2;UouFF6fJixyPB*YJatTO9Fu6;(;^v@}-I?im^6Cez>~2x=TJ$Ute#+ zD~i1lkC&8`j0FKFiy-#mvD@uIYaI@UgbL_W>iX-zw`j6=!VWyue+~;kRaI3Q(mm;a zo`(yRU2}FTc7wk{KVPY%x&?5~?ymzJ&a>Qz?Z`m%tQ501BLn~XT)GQ616%B|@feJr zB@3fya576hB&!i0gIQs6FBU)x3Gc7jHSqV~E}c$yMtSIzw5UUPCu!OL9sdge0RR79 kI}TX@000I_L_t&o0HK3R$x-uM4FCWD07*qoM6N<$f<~Q?xFJv-Ozv=bQ?~mU(zx&5s&$;)U&;8sB00h8*gaqIV8YDshF!<@A zf5k;m02urn2Jk$@+%w{GSY5Mh3==vf-&Csl7x&23dbpJ5%zBj+7!@1?#| zYi^Ss^t&9?i2+@PN!DmumKw5ihhvfV!s$C;T=zS-%mvn#6)Epbi1 z*N_zlJ5%Dpd=}RHBY!6C`_z`QFHct(8rup8$>4e zS=>9Au49s2z6JehXt-4l?0#FwD`eioB=o@S%k5!&d0L_2mbXbwTD{DdV` zsWnjo15PlMOTMEQ9H)eEx9Y6>&T0!)b`_E)4t_CdF1>RHGCj2;j#AuJWAr_W;|#*` z?P)^r5*bK`_KsCMdHX6&T{N|gi)%>^$$H#$Rzbn1Eq4?et=bb6bZO8A%dihWy62lahD7%pmah-+L^!A=$*c&E+R+Dh>#Y?1lDm zFYsrc>jT#WCGlUACBST0Pkj5X*Xz2^YDd(wMKGBf^|M zyVtNLjTsX&Xzu{S=*KN|8{+6op)&XO^9@-l+vK$bn$bsG5pjS>ksp1Ec~kq_!-iQ0 zW~~SH%ko>gk>OzH;z12_E*CN}xzoEEfgof3ZY7B@F(j31yT$g9%Yj%SO>LX#KxXlr z06IP8%g*ELLG4nGjBkt~ozyC=m)0R-BkM&-Jb!1BC!VTOO~ZX5r>&$u7?;H}uJaPV zE8kNeIE{`?l0e>mv@-M$0>EaMj^r*4_s%R8a^I@SocL2mTfN-?^IzNB#o zEFq5>6<^goaYx|yFq7C(Mo+f%(at2c?@)tN2B_5YSVV#RQ}>yR zk!>r@V-=oDnq{XJkp|wKp(>AL!ka+CAM9q0q7ejRqRLAK+f}};IBw4i*JZZ0O?@r; zot|?*ubr^GEfw6QLg*^LWke~<*1Zpa!@dCBX1A+_evrPK1_hwj-JX0AjkFQ2m)dWK!)aKG9>}n2GIX%jbnO z09v?&Y|NkchN60Bf=A9rLvQ3 zZd*%aC+)_FGVrMgLDj;eIrd@}n=O!~Pu&&Me_my^`Vm!ft~#G`>yo;NE-XvtHIlTdTX;r|P;D*f-{6psXEz z4Nre8Y4Q_mVq)s@1e@(Jv{B3H>91x}Hd0GH1Mu|zlUF9{rAwvEjz{VjM=Lp+jFk0- ziR-665xw(4Q0nu1>yDCsh47)HLdhH#o9l_TM1}8hUG;97trCF|r|e3cmtL;w5s5_Y zfOi+ei?hZi3vQ;o!Pdt;sQ%!MV9tPL?*m<62Zeu0cV~(DEbdVcbN|aP%dvi(S63po zzI_yF9N8$>?l`iYk^KGfx4RSbA9-)IBek{i2j;J>fy>8^?ZLtQhOut1U+ILxshIqs rU>v?B3K{fhbW9EzR22Ck?Z?WnlAYK5n!yjS(o8TxMF%SW_wD`%5_yt; literal 15086 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 avux+b2jex6`j9Sy#D`g3$Crj1`u_p`hwZ5V diff --git a/frontend/public/logo-long-dark.svg b/frontend/public/logo-long-dark.svg new file mode 100644 index 0000000..3a97e2c --- /dev/null +++ b/frontend/public/logo-long-dark.svg @@ -0,0 +1 @@ +B3Note diff --git a/frontend/public/logo-long.svg b/frontend/public/logo-long.svg index f780c0a..7addb75 100644 --- a/frontend/public/logo-long.svg +++ b/frontend/public/logo-long.svg @@ -1 +1 @@ -B3Note \ No newline at end of file +B3Note diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg index 9d1ddd0..d198082 100644 --- a/frontend/public/logo.svg +++ b/frontend/public/logo.svg @@ -1 +1 @@ - \ No newline at end of file +B3Note diff --git a/frontend/public/maskable-512x512.png b/frontend/public/maskable-512x512.png index cb0453e7563b0169caded47f70a9c31b7abfb04f..27edfdd2aecd096d756cac60e44ff99f46452c4c 100644 GIT binary patch literal 11291 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 \ No newline at end of file + diff --git a/frontend/public/site.webmanifest b/frontend/public/site.webmanifest index 33e05f5..49e178c 100644 --- a/frontend/public/site.webmanifest +++ b/frontend/public/site.webmanifest @@ -1,7 +1,7 @@ { "name": "B3Note", "short_name": "B3Note", - "description": "End-to-end encrypted notes on the Internet Computer, with burn-after-reading links.", + "description": "End-to-end encrypted notes on the Internet Computer, with burn-after-reading links and on-device AI.", "start_url": "/notes", "icons": [ { "src": "/logo.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" }, @@ -9,7 +9,7 @@ { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { "src": "/maskable-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ], - "theme_color": "#6d4aff", - "background_color": "#000000", + "theme_color": "#121110", + "background_color": "#121110", "display": "standalone" } diff --git a/frontend/scripts/brand-icons.py b/frontend/scripts/brand-icons.py index b77dd8d..7d2a018 100644 --- a/frontend/scripts/brand-icons.py +++ b/frontend/scripts/brand-icons.py @@ -1,106 +1,69 @@ -"""Generates the B3Note icons: the B3 family's mark (B3Forge's geometry, a grey B with a 3 and a -multicolour dot) with the product's letter, a gradient N, plugged into it. +"""Generates the B3Note icons: B3Pay's mark (a stem of blocks and a 3) with its node folded into a +note, as B3Wallet and B3Forge change only what the node does. The mark is a DRAFT until it is signed +off and added to B3Pay's tools/logo/build.js; the geometry below is copied from the B3Note design system. python3 frontend/scripts/brand-icons.py

-writes the SVGs below. The PNGs in frontend/public are these rasterized at the sizes their names -give: icon.svg for apple-touch-icon (180) and android-chrome (192, 512), maskable.svg for -maskable-512x512, and mark.svg, transparent, for the favicons (16, 32, and 16+32+48 in favicon.ico). -mark.svg is also logo.svg, mono.svg is safari-pinned-tab.svg. +writes the SVGs below. The PNGs in frontend/public are these rasterized at the sizes their names give: +icon.svg for apple-touch-icon (180) and android-chrome (192, 512), maskable.svg for maskable-512x512, +and favicon.svg for the favicons (16, 32, and 16+32+48 in favicon.ico). favicon.svg is also logo.svg, +mono.svg is safari-pinned-tab.svg, logo-long.svg and logo-long-dark.svg are the README's lockup. """ -import math, sys, os +import os, sys OUT = sys.argv[1] -GREY = "#808080" -# Top to bottom, sampled from B3Forge's F bar. -GRADIENT = ["#29abe2", "#348ccc", "#4a4198", "#8e2480", "#ea1f79", "#f04543", "#f3712b", "#f9a037", "#fbb03b"] -# Clockwise from 3 o'clock, sampled from B3Forge's dot. -CONIC = ["#802482", "#4a4298", "#3b71b9", "#2ba6de", "#91ae90", "#c3af68", "#f89634", "#f4722b", "#f0552c", "#ef3855", "#ed2471", "#b5217d"] - -# B3ForgeIcon.tsx, in its 366 x 500 space. -B_OUTLINE = "M11 452.549V485.99C11 485.996 11.0045 486 11.01 486H216.751C349.884 482.439 404.347 306.604 289.369 241.31C287.553 240.123 287.553 238.936 289.369 237.749C382.562 165.331 328.541 21.6845 209.932 11H11.01C11.0045 11 11 11.0045 11 11.01V44.4507" -THREE = "M177.152 75.5514C198.455 75.5515 215.402 83.3523 226.95 95.8664C238.322 108.191 243.794 124.358 243.794 140.195C243.794 156.033 238.322 172.2 226.95 184.524C218.939 193.206 208.328 199.618 195.468 202.732C202.831 212.498 207.09 224.888 207.09 238.756C207.09 252.15 203.117 264.163 196.213 273.768C212.269 276.356 225.371 283.353 235.027 293.323C247.25 305.943 253.249 322.604 253.34 339.028C253.432 355.447 247.622 372.128 235.6 384.783C223.429 397.596 205.606 405.528 183.219 405.529H94.8667C88.1403 405.529 82.6875 400.075 82.6872 393.349C82.6873 386.623 88.1402 381.17 94.8667 381.17H183.219C199.561 381.169 210.785 375.538 217.94 368.007C225.245 360.316 229.042 349.871 228.983 339.163C228.923 328.461 225.012 317.995 217.53 310.271C210.191 302.694 198.708 297.022 182.008 297.022H153.696C152.031 297.149 150.341 297.218 148.629 297.218C119.284 297.218 96.5496 278.162 91.3104 250.936H52.4109C45.6844 250.936 40.2314 245.483 40.2314 238.756C40.2314 232.03 45.6844 226.577 52.4109 226.577H91.3104C96.5496 199.351 119.284 180.295 148.629 180.295C150.296 180.295 151.942 180.359 153.565 180.481H177.152C192.157 180.48 202.441 175.165 209.048 168.005C215.83 160.655 219.435 150.59 219.435 140.195C219.435 129.801 215.83 119.736 209.048 112.386C202.441 105.226 192.157 99.9104 177.152 99.9104H92.4308C85.7042 99.9104 80.2513 94.4574 80.2513 87.7309C80.2513 81.0043 85.7042 75.5514 92.4308 75.5514H177.152ZM148.629 199.782C125.84 199.782 109.655 215.967 109.655 238.756C109.655 261.545 125.84 277.731 148.629 277.731C171.418 277.731 187.603 261.545 187.603 238.756C187.603 215.967 171.418 199.782 148.629 199.782Z" -DOT = (148.628, 238.756, 33.0) -# B3Forge's exported icons draw the mark bolder than the component (about 29 units). -STROKE = 28 -THREE_OUTLINE = 5 -# The N, left of the connector, as tall as B3Forge's F. -N_TOP, N_BOTTOM = 177.0, 300.5 -N_LEFT, N_RIGHT = -76.0, 4.0 -CONTENT = (N_LEFT - STROKE / 2, -3.0, 369.0, 500.0) # x0, y0, x1, y1 in mark space - - -def lerp(a, b, t): - pa = [int(a[i:i + 2], 16) for i in (1, 3, 5)] - pb = [int(b[i:i + 2], 16) for i in (1, 3, 5)] - return "#%02x%02x%02x" % tuple(round(x + (y - x) * t) for x, y in zip(pa, pb)) - - -def conic_slices(cx, cy, r, mono=None): - parts, n = [], 120 - for i in range(n): - a0, a1 = 2 * math.pi * i / n, 2 * math.pi * (i + 1.15) / n - pos = i / n * len(CONIC) - k = int(pos) - colour = mono or lerp(CONIC[k % len(CONIC)], CONIC[(k + 1) % len(CONIC)], pos - k) - x0, y0 = cx + r * math.cos(a0), cy + r * math.sin(a0) - x1, y1 = cx + r * math.cos(a1), cy + r * math.sin(a1) - parts.append(f'') - return "".join(parts) - - -def mark(mono=None): - grey = mono or GREY - n_stroke = mono or "url(#b3note-n)" - n_path = f"M{N_LEFT} {N_BOTTOM}V{N_TOP}L{N_RIGHT} {N_BOTTOM}V{N_TOP}" - defs = "" if mono else ( - f'' - + "".join(f'' for i, c in enumerate(GRADIENT)) - + "") - return (defs - + f'' - + f'' - + f'' - + conic_slices(*DOT, mono=mono)) - - -def svg(size, fill, background=None, mono=None, square=True): - """`fill`: the share of the canvas the mark's height takes.""" - x0, y0, x1, y1 = CONTENT - w, h = x1 - x0, y1 - y0 - if square: - scale = size * fill / h - tx = (size - w * scale) / 2 - x0 * scale - ty = (size - h * scale) / 2 - y0 * scale - vb_w = vb_h = size - else: - scale, tx, ty = 1, -x0, -y0 - vb_w, vb_h = w, h - bg = f'' if background else "" - return (f'' - + bg + f'' + mark(mono) + "") - - -def wordmark(): - """The mark beside "B3Note", for the README.""" - x0, y0, x1, y1 = CONTENT - scale = 200 / (y1 - y0) - tx, ty = 20 - x0 * scale, 20 - y0 * scale - text_x = 20 + (x1 - x0) * scale + 36 - return (f'' - + f'' + mark() + "" - + f'' - + f'B3Note') +INK_DARK, INK_LIGHT = "#f1ede6", "#1a1714" # B3Pay ink on dark and on light grounds +NODE_DARK, NODE_LIGHT = "#f0b35a", "#e8a33d" # B3Pay accent: the node is always amber +SURFACE = "#121110" # B3Pay's graphite surface + +# B3Pay's mark on its 96-unit grid, with the node's box (14.7 units) holding a folded sheet. +SHEET = "M21.4 38.65H24.35A1 1 0 0 1 25.35 39.65V46.4H32.1A1 1 0 0 1 33.1 47.4V50.35A3 3 0 0 1 30.1 53.35H21.4A3 3 0 0 1 18.4 50.35V41.65A3 3 0 0 1 21.4 38.65Z" +FOLD = "M27.1 38.65L33.1 44.65H28.1A1 1 0 0 1 27.1 43.65Z" +THREE = "M65.25 62.5C65.25 59.7152 64.1437 57.0445 62.1745 55.0755C60.2055 53.1063 57.5348 52 54.75 52H39.75C38.0932 52 36.75 50.6568 36.75 49V43C36.75 41.3432 38.0932 40 39.75 40H52.75C55.0042 40 57.1662 39.1045 58.7605 37.5105C60.3545 35.9162 61.25 33.7542 61.25 31.5C61.25 29.2458 60.3545 27.0837 58.7605 25.4895C57.1662 23.8955 55.0042 23 52.75 23H39.75C38.0932 23 36.75 21.6568 36.75 20V14C36.75 12.3431 38.0932 11 39.75 11H52.75C58.187 11 63.4012 13.1598 67.2458 17.0043C71.0902 20.8488 73.25 26.063 73.25 31.5C73.25 36.3135 71.5563 40.9735 68.4652 44.6635C71.1983 46.765 73.4115 49.4663 74.9347 52.5588C76.4578 55.6515 77.25 59.0528 77.25 62.5C77.25 68.4673 74.8795 74.1903 70.66 78.41C66.4403 82.6295 60.7172 85 54.75 85H39.75C38.0932 85 36.75 83.6568 36.75 82V76C36.75 74.3432 38.0932 73 39.75 73H54.75C57.5348 73 60.2055 71.8937 62.1745 69.9245C64.1437 67.9555 65.25 65.2848 65.25 62.5Z" +# The outlined wordmark "B3Note": Rubik 600, kerned, -0.03em, in B3Pay's lockup frame. +LOCKUP_VIEWBOX = "218.75 -758.333 4423.717 978.333" +WORD_X = 1110.278 +WORD = "M99 0Q88 0 81 -7Q74 -14 74 -25V-675Q74 -686 81 -693Q88 -700 99 -700H396Q479 -700 532 -676Q585 -652 611 -608Q636 -564 636 -505Q636 -468 622 -440Q609 -412 589 -394Q570 -376 552 -367Q591 -349 621 -307Q650 -266 650 -209Q650 -146 622 -99Q594 -52 539 -26Q484 0 405 0ZM233 -117H386Q435 -117 460 -145Q485 -172 485 -210Q485 -249 460 -275Q434 -302 386 -302H233ZM233 -417H377Q424 -417 448 -441Q471 -464 471 -501Q471 -538 448 -560Q424 -583 377 -583H233Z M983 10Q909 10 856 -8Q803 -26 769 -54Q735 -82 718 -115Q701 -149 700 -180Q700 -189 706 -195Q712 -201 721 -201H831Q842 -201 849 -196Q856 -192 862 -181Q870 -158 888 -144Q906 -130 931 -124Q955 -119 983 -119Q1039 -119 1071 -145Q1103 -171 1103 -218Q1103 -266 1073 -287Q1043 -308 988 -308H881Q871 -308 864 -315Q857 -322 857 -332V-381Q857 -393 861 -401Q866 -409 871 -413L1043 -571H761Q750 -571 743 -578Q736 -585 736 -595V-675Q736 -686 743 -693Q750 -700 761 -700H1205Q1216 -700 1223 -693Q1230 -686 1230 -675V-603Q1230 -592 1226 -585Q1222 -577 1216 -573L1053 -412L1063 -411Q1122 -405 1168 -382Q1214 -359 1241 -316Q1267 -274 1267 -209Q1267 -141 1230 -92Q1193 -43 1129 -17Q1065 10 983 10Z M1378 0Q1367 0 1360 -7Q1353 -14 1353 -25V-675Q1353 -686 1360 -693Q1367 -700 1378 -700H1469Q1484 -700 1491 -693Q1498 -686 1500 -682L1767 -265V-675Q1767 -686 1774 -693Q1781 -700 1791 -700H1893Q1904 -700 1911 -693Q1918 -686 1918 -675V-25Q1918 -15 1911 -7Q1904 0 1893 0H1801Q1786 0 1779 -7Q1773 -15 1770 -18L1504 -422V-25Q1504 -14 1497 -7Q1490 0 1479 0Z M2263 10Q2180 10 2124 -17Q2068 -45 2039 -96Q2009 -147 2006 -215Q2005 -235 2005 -260Q2005 -286 2006 -305Q2009 -374 2040 -425Q2071 -475 2127 -503Q2183 -530 2263 -530Q2342 -530 2398 -503Q2454 -475 2485 -425Q2516 -374 2519 -305Q2521 -286 2521 -260Q2521 -235 2519 -215Q2516 -147 2486 -96Q2457 -45 2401 -17Q2345 10 2263 10ZM2263 -100Q2312 -100 2337 -131Q2362 -161 2364 -220Q2365 -235 2365 -260Q2365 -285 2364 -300Q2362 -358 2337 -389Q2312 -420 2263 -420Q2214 -420 2189 -389Q2163 -358 2161 -300Q2160 -285 2160 -260Q2160 -235 2161 -220Q2163 -161 2189 -131Q2214 -100 2263 -100Z M2843 0Q2781 0 2737 -21Q2693 -41 2671 -84Q2648 -127 2648 -192V-402H2570Q2559 -402 2552 -409Q2545 -416 2545 -427V-495Q2545 -506 2552 -513Q2559 -520 2570 -520H2648V-685Q2648 -696 2655 -703Q2663 -710 2673 -710H2771Q2782 -710 2789 -703Q2796 -696 2796 -685V-520H2921Q2931 -520 2938 -513Q2945 -506 2945 -495V-427Q2945 -416 2938 -409Q2931 -402 2921 -402H2796V-204Q2796 -166 2809 -145Q2823 -123 2857 -123H2930Q2940 -123 2947 -116Q2954 -109 2954 -99V-25Q2954 -14 2947 -7Q2940 0 2930 0Z M3243 10Q3128 10 3061 -54Q2993 -118 2989 -240Q2989 -248 2989 -262Q2989 -275 2989 -283Q2993 -360 3024 -415Q3055 -471 3111 -500Q3167 -530 3242 -530Q3326 -530 3383 -496Q3439 -461 3467 -402Q3496 -342 3496 -265V-242Q3496 -232 3489 -225Q3481 -218 3471 -218H3144Q3144 -217 3144 -215Q3144 -212 3144 -210Q3145 -178 3156 -151Q3168 -125 3189 -109Q3211 -93 3241 -93Q3266 -93 3282 -100Q3298 -107 3309 -117Q3319 -127 3324 -134Q3333 -144 3339 -147Q3344 -149 3356 -149H3460Q3470 -149 3476 -144Q3483 -138 3482 -128Q3481 -112 3465 -88Q3450 -65 3420 -42Q3390 -20 3345 -5Q3301 10 3243 10ZM3144 -309H3340V-311Q3340 -347 3329 -374Q3317 -400 3295 -415Q3273 -430 3242 -430Q3211 -430 3189 -415Q3167 -400 3156 -374Q3144 -347 3144 -311Z" + + +def mark(ink, node): + return ( + f'' + f'' + f'' + f'' + ) + + +def tile(scale, radius): + """The mark at `scale` on a graphite square; radius 0 is full-bleed (the platform rounds it).""" + offset = 48 * (1 - scale) + rect = f'' + return ( + 'B3Note' + f'{rect}{mark(INK_DARK, NODE_DARK)}\n' + ) + + +def lockup(ink, node): + w = float(LOCKUP_VIEWBOX.split()[2]) / float(LOCKUP_VIEWBOX.split()[3]) * 48 + return ( + f'B3Note' + f'{mark(ink, node)}' + f'\n' + ) os.makedirs(OUT, exist_ok=True) variants = { - "mark.svg": svg(512, 0.86), # transparent, tight: favicons and the app header - "icon.svg": svg(512, 0.667, background="#000"), # home-screen icon (iOS shows transparency as black) - "maskable.svg": svg(512, 0.54, background="#000"), # Android maskable: inside the 80% safe zone - "mono.svg": svg(512, 0.86, mono="#000"), # Safari pinned tab - "logo-long.svg": wordmark(), + "favicon.svg": tile(0.7, 22), # B3Pay's favicon: the app icon tile, which holds at 16px + "icon.svg": tile(0.7, 0), # home-screen icon, full-bleed: iOS and Android round it themselves + "maskable.svg": tile(0.56, 0), # Android maskable: inside the 80% safe zone + "mono.svg": '' + + mark("#000", "#000") + + "\n", # Safari pinned tab: one ink, Safari colors it + "logo-long.svg": lockup(INK_LIGHT, NODE_LIGHT), # the README on light grounds + "logo-long-dark.svg": lockup(INK_DARK, NODE_DARK), # the README on dark grounds } for name, text in variants.items(): with open(os.path.join(OUT, name), "w") as f: diff --git a/frontend/src/app/features.ts b/frontend/src/app/features.ts index cc48fc3..6cefeeb 100644 --- a/frontend/src/app/features.ts +++ b/frontend/src/app/features.ts @@ -1,11 +1,29 @@ -/** What this deployment offers, from the backend's public `get_config`. */ +/** What this deployment and this browser offer. */ import { useClient } from "@ic-reactor/react" import { useQuery } from "@tanstack/react-query" +import { useEffect, useSyncExternalStore } from "react" +import { detectLocalAi, localAiStatus, subscribeLocalAi, type LocalAiStatus } from "../lib/localAi" import { backendOf } from "../reactor" -/** Whether a controller switched the AI assistant on (it is off by default). */ +/** Whether a controller switched the canister's AI assistant on (it is off by default). */ export function useAiEnabled(): boolean { const client = useClient() const config = useQuery(client.queryOptions(backendOf(client), "get_config")) return config.data?.ai_enabled === true } + +/** The on-device model: which one this browser can run, and whether it is ready. */ +export function useLocalAi(): LocalAiStatus { + const status = useSyncExternalStore(subscribeLocalAi, localAiStatus, localAiStatus) + useEffect(() => { + void detectLocalAi() + }, []) + return status +} + +/** Whether any assistant can run: on this device, or on the canister when it is switched on. */ +export function useAnyAi(): { local: LocalAiStatus; cloud: boolean; any: boolean } { + const local = useLocalAi() + const cloud = useAiEnabled() + return { local, cloud, any: cloud || (local.status !== "unavailable" && local.status !== "checking") } +} diff --git a/frontend/src/components/AiPanel.tsx b/frontend/src/components/AiPanel.tsx index 04f2c07..25ca1b4 100644 --- a/frontend/src/components/AiPanel.tsx +++ b/frontend/src/components/AiPanel.tsx @@ -1,8 +1,9 @@ -import { Check, Copy, RotateCcw, Sparkles } from "lucide-react" +import { Check, Copy, RotateCcw } from "lucide-react" import { useClient } from "@ic-reactor/react" import { useMutation, useQuery } from "@tanstack/react-query" -import { useState } from "react" +import { useEffect, useRef, useState } from "react" import { toast } from "sonner" +import { useAnyAi } from "../app/features" import { AI_ACTIONS, LANGUAGES, @@ -13,11 +14,14 @@ import { type AiAction, } from "../lib/ai" import { errorMessage } from "../lib/errors" +import { localAiError, runLocalAi } from "../lib/localAi" +import { cleanLocalResult, localActionPrompt } from "../lib/localAiPrompts" import { normalizeTags, type NoteContent } from "../lib/note" import { AiConsentDialog } from "./AiConsentDialog" import { Dialog } from "./Dialog" +import { AiBlock, AiMemo, LocalAiSetup } from "./LocalAi" import { Markdown } from "./Markdown" -import { Button, Notice, Select, Spinner, cn } from "./ui" +import { Button, Notice, Select, cn } from "./ui" import { backendOf } from "../reactor" export interface AiSelection { @@ -26,6 +30,16 @@ export interface AiSelection { text: string } +type Engine = "local" | "cloud" + +interface Run { + action: AiAction + engine: Engine + state: "drafting" | "ready" | "failed" + text: string + error?: string +} + const HEADINGS: Partial> = { Summarize: "Summary", ActionItems: "Action items", @@ -46,38 +60,83 @@ export function AiPanel({ selection: AiSelection | null onApply: (patch: Partial) => void }) { - const [action, setAction] = useState(null) + const { local, cloud } = useAnyAi() + const localUsable = local.status !== "unavailable" + const [engine, setEngine] = useState(localUsable ? "local" : "cloud") const [language, setLanguage] = useState("English") - const [result, setResult] = useState(null) + const [run, setRun] = useState(null) const [consentFor, setConsentFor] = useState(null) + const abort = useRef(null) const client = useClient() const backend = backendOf(client) - const account = useQuery({ ...client.queryOptions(backend, "get_account"), enabled: open }) + const account = useQuery({ ...client.queryOptions(backend, "get_account"), enabled: open && cloud }) // AI requests change nothing a read shows: invalidate nothing. const assist = useMutation(client.mutationOptions(backend, "ai_assist", { invalidates: [] })) - const maxBytes = account.data?.limits.max_ai_input_bytes ?? 8_000 + // The on-device model is the default; the canister's is the choice when this browser can't run one. + useEffect(() => { + if (!localUsable && cloud) setEngine("cloud") + }, [localUsable, cloud]) + useEffect(() => () => abort.current?.abort(), []) + + const maxBytes = engine === "cloud" ? (account.data?.limits.max_ai_input_bytes ?? 8_000) : 6_000 const input = selection ? selection.text : noteAsPrompt(content.title, content.body) const tooLong = encoder.encode(input).length > maxBytes - const aiDisabled = account.data?.ai_enabled === false + const localReady = local.status === "ready" - const run = async (chosen: AiAction) => { + const runCloud = async (chosen: AiAction) => { if (!hasAiConsent()) { setConsentFor(chosen) return } - setAction(chosen) - setResult(null) + setRun({ action: chosen, engine: "cloud", state: "drafting", text: "" }) try { const reply = await assist.mutateAsync({ task: toAiTask(chosen.id, language), text: input }) - setResult(reply.text) + setRun({ action: chosen, engine: "cloud", state: "ready", text: reply.text }) } catch (error) { - toast.error(errorMessage(error)) + setRun({ action: chosen, engine: "cloud", state: "failed", text: "", error: errorMessage(error) }) } } + const runLocal = async (chosen: AiAction) => { + abort.current?.abort() + const controller = new AbortController() + abort.current = controller + setRun({ action: chosen, engine: "local", state: "drafting", text: "" }) + try { + const raw = await runLocalAi( + localActionPrompt(chosen.id, input, language), + controller.signal, + (text) => { + if (!controller.signal.aborted) setRun((r) => (r && r.state === "drafting" ? { ...r, text } : r)) + }, + ) + if (controller.signal.aborted) return + const text = cleanLocalResult(chosen.id, raw) + setRun((r) => + r + ? text + ? { ...r, state: "ready", text } + : { ...r, state: "failed", error: "The model gave back nothing. Try again." } + : r, + ) + } catch (error) { + if (controller.signal.aborted) return + setRun((r) => (r ? { ...r, state: "failed", error: localAiError(error) } : r)) + } + } + + const start = (chosen: AiAction) => void (engine === "cloud" ? runCloud(chosen) : runLocal(chosen)) + + // Stopping keeps what was written so far, so it can still be used. + const stop = () => { + abort.current?.abort() + setRun((r) => (r && r.state === "drafting" ? (r.text.trim() ? { ...r, state: "ready" } : null) : r)) + } + const apply = () => { - if (!action || !result) return + if (!run || run.state !== "ready") return + const { action, text: result } = run if (action.apply === "title") onApply({ title: result.split("\n")[0] }) else if (action.apply === "tags") onApply({ tags: normalizeTags([...content.tags, ...parseTags(result)]) }) @@ -97,20 +156,26 @@ export function AiPanel({ } const close = () => { - setAction(null) - setResult(null) + abort.current?.abort() + setRun(null) onClose() } - const applyLabel = action + const applyLabel = run ? { title: "Use as title", tags: "Add tags", replace: selection ? "Replace selection" : "Replace note", append: "Add to note", - }[action.apply] + }[run.action.apply] : "" + const where = + run?.engine === "cloud" + ? "sent to the Internet Computer's LLM canister" + : "read on this device, nothing left it" + const model = run?.engine === "cloud" ? account.data?.ai_model : "model" in local ? local.model : undefined + return ( <> - {aiDisabled ? ( - The AI assistant is turned off on this deployment. - ) : tooLong ? ( - - This is too long for the assistant (more than {Math.round(maxBytes / 1000)} KB). Select a part of - the note in Write mode and try again. - - ) : result !== null && action ? ( -
-
- {action.apply === "tags" ? ( -

- {parseTags(result) - .map((t) => `#${t}`) - .join(" ")} -

- ) : ( - - )} -
-
- - - -
-
- ) : assist.isPending ? ( -
- -
- ) : ( -
-
- {AI_ACTIONS.filter((a) => a.id !== "Translate").map((a) => ( +
+ {cloud && localUsable ? ( +
+ {( + [ + ["local", "On this device"], + ["cloud", "Internet Computer"], + ] as const + ).map(([value, label]) => ( ))}
-
- Translate into - ({ value: l, label: l }))} + className="flex-1" + /> + +
+ - -
-

- Runs on the Internet Computer's LLM canister{account.data ? ` (${account.data.ai_model})` : ""}. - Only this text is sent; your other notes stay encrypted. -

-
- )} + + )} +
{ const chosen = consentFor setConsentFor(null) - if (chosen) void run(chosen) + if (chosen) void runCloud(chosen) }} /> diff --git a/frontend/src/components/AppHeader.tsx b/frontend/src/components/AppHeader.tsx index 8575b10..b4bca08 100644 --- a/frontend/src/components/AppHeader.tsx +++ b/frontend/src/components/AppHeader.tsx @@ -1,11 +1,12 @@ import { Link, useNavigate } from "@tanstack/react-router" -import { LogOut, Monitor, Moon, NotebookPen, Settings, Sparkles, Sun } from "lucide-react" +import { LogOut, Monitor, Moon, NotebookPen, Settings, Sun } from "lucide-react" import { useState } from "react" -import { useAiEnabled } from "../app/features" +import { useAnyAi } from "../app/features" import { signOut, useSession } from "../app/session" import { setTheme, useTheme, type Theme } from "../app/theme" import { shortPrincipal } from "../lib/format" -import { Button, cn } from "./ui" +import { Logo, SheetMark } from "./brand" +import { Button } from "./ui" const NEXT_THEME: Record = { system: "light", light: "dark", dark: "system" } @@ -25,31 +26,22 @@ export function ThemeToggle() { ) } -export function Logo({ className }: { className?: string }) { - return ( - - - - B3Note - - - ) -} - +// B3Pay's nav: muted until current, then ink with a brand underline. const navLink = - "inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100" -const activeNavLink = "bg-zinc-100 text-zinc-900 dark:bg-zinc-800 dark:text-zinc-100" + "inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-semibold text-zinc-600 hover:bg-brand-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-brand-900 dark:hover:text-zinc-100" +const activeNavLink = "rounded-b-none text-zinc-900 shadow-[inset_0_-2px_0_var(--brand)] dark:text-zinc-100" export function AppHeader() { const { signedIn, principal, kind } = useSession() - const aiEnabled = useAiEnabled() + const { any: aiEnabled } = useAnyAi() const navigate = useNavigate() const [busy, setBusy] = useState(false) return ( -
+
- + + {signedIn ? (