From 049091068771c17b7afaf48adfe39173358e6ed9 Mon Sep 17 00:00:00 2001 From: Keysat Date: Sat, 20 Jun 2026 08:42:29 -0500 Subject: [PATCH] =?UTF-8?q?Add=20installable=20PWA=20(Option=20A=20?= =?UTF-8?q?=E2=80=94=20iPhone-first,=20no=20service=20worker)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the app installable to the iOS home screen and launch standalone (full-screen, no browser chrome, dark status bar). Add manifest.webmanifest, square app icons (ten31-app-icon.svg -> 192/512/apple-touch-icon), the apple-mobile-web-app + manifest tags, viewport-fit=cover, and a pre-auth /manifest.webmanifest route. No service worker by design. --- AGENTS.md | 7 ++++--- ROADMAP.md | 27 +++++++++++++++++++++++++++ backend/server.py | 5 +++++ design/tokens.tokens.json | 2 +- frontend/assets/apple-touch-icon.png | Bin 0 -> 4282 bytes frontend/assets/icon-192.png | Bin 0 -> 4564 bytes frontend/assets/icon-512.png | Bin 0 -> 15126 bytes frontend/assets/ten31-app-icon.svg | 10 ++++++++++ frontend/index.html | 15 ++++++++++++++- frontend/manifest.webmanifest | 17 +++++++++++++++++ 10 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 frontend/assets/apple-touch-icon.png create mode 100644 frontend/assets/icon-192.png create mode 100644 frontend/assets/icon-512.png create mode 100644 frontend/assets/ten31-app-icon.svg create mode 100644 frontend/manifest.webmanifest diff --git a/AGENTS.md b/AGENTS.md index 66c7d9c..45c8803 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,11 +107,12 @@ Subsystem rules live in `docs/guides/` and lazy-load in Claude Code via `.claude ## Current state -_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 0–7 + P3b + drag-reorder + **8a–8i — Phase 8 complete** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign — feature-complete; next is deploy + real-phone device-test** (nothing verified on a real phone yet). Build reference: `design/phase8-conformance.md` (the 8a–8i spec; now fully landed — archive-eligible). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys.** History: git log + `start9/0.4/startos/versions/`._ +_**Box live at v0.1.0:94**; `main` ahead by mobile Phases 0–7 + P3b + drag-reorder + **8a–8i — Phase 8 complete** + **installable PWA (Option A)** — **all deploy-pending** (no s9pk built). **The fundraising grid + email capture is the canonical system of record.** Active thread: **mobile-first redesign — feature-complete; next is deploy + real-phone device-test** (nothing verified on a real phone yet). Build reference: `design/phase8-conformance.md` (the 8a–8i spec; now fully landed — archive-eligible). **Plan (Grant, 2026-06-19): finish features first → then Grant device-tests + deploys.** History: git log + `start9/0.4/startos/versions/`._ - **Mobile redesign — 4 core surfaces built (Grid · Contacts · Pipeline · Reminders), each a rules-of-hooks-safe `useIsMobile()` → `Mobile*`/`Desktop*` pair (desktop untouched).** Foundation: bottom-tab bar + `:root` mobile vars; 4-stage enum; derived grid signals injected-on-GET/stripped-on-write at both points; mobile writes use **one-row endpoints only** (log-communication, pipeline link/stage, reminders, `update-row`) — never whole-grid PUT. - **Phase 8 done (8a–8i — complete)** — cards/details/sheets/shell re-authored to the dc anatomy. The durable per-primitive record is the **Design convention's primitives list**; per-phase "what changed" is in git log. Recent: **8h** Grid-detail loose ends (G4 tappable stage card, G5 dedicated Reminder card, Open-in-Grid deep-link on all three detail surfaces); **8i** shell — `BottomTabIcon` SVG line-icons replace the bottom-tab emoji glyphs + the `·Ten31·` `.mobile-wordmark` in the top bar (page title now `desktop-only`); the dc top bar's quick-log/theme/account cluster already existed. jsdom-verified (throwaway 375px harness). - **Live (deployed):** W2 NL query (v94); W1 reminders (v93); grid Pipeline (v88); Matrix intake + Gmail capture (DWD) + daily digest; Thesis/Architect (dual-approval); outreach — all draft-only. - **Tests:** **40/40 backend green** (`python3 backend/run_tests.py`), `py_compile` clean. Mobile surfaces interaction-verified via throwaway 375px jsdom harnesses (deleted after); harness recipe + the `Simulate.change` input gotcha live in the `mobile-surface-verification` memory. -- **Next — feature-complete; deploy + device-test:** Phase 8 is fully landed (8i closed S1/S2; **Pipeline accordion skipped** per Grant). Remaining is **deploy P0–P8 + P3b in one s9pk** (**authorize + version-bump first**, per `docs/guides/packaging.md`) and **device-test light/dark on a real phone** — nothing mobile has run on a real device yet (smoke/jsdom only). -- **Open / risks:** all mobile work + light theme **built but never deployed or device-tested** (smoke/jsdom only); `MobileDetailRow` unused-but-retained (legacy-usage sweep); Pipeline detail "Committed" tile shows grid-committed not deal-expected (forecast in a footnote); `handle_get_opportunity` (single-opp GET) deliberately does NOT inject `existing_investor`/`last_contact_date` — no surface needs it (the card uses the list injection; the detail derives `existing` from the contact fetch); W2 happy-path only; **Claude/Architect path unverified live on the box**; v2.0 reserve-asset spine **not canonical**; doc drift — `crm-overview.md`/`EVALUATION.md` still call `lp_profiles` live. +- **Installable PWA — BUILT 2026-06-20 (Option A, iPhone-first, no service worker):** `frontend/manifest.webmanifest` (standalone, `theme_color` `#0b1118`) + square icons (`ten31-app-icon.svg` → `icon-192`/`icon-512`/`apple-touch-icon`) + `` manifest/`apple-mobile-web-app-*`/`viewport-fit=cover` metas + one pre-auth `/manifest.webmanifest` route (`application/manifest+json`). Adds to home screen → full-screen standalone, dark status bar. No SW by design (iOS A2HS needs none; avoids the stale-shell class). render-smoke + live-curl verified. Detail in `ROADMAP.md` "Mobile PWA". **Deploy:** rides the same s9pk. +- **Next — feature-complete; deploy + device-test:** Phase 8 is fully landed (8i closed S1/S2; **Pipeline accordion skipped** per Grant) + the installable PWA. Remaining is **deploy P0–P8 + P3b + PWA in one s9pk** (**authorize + version-bump first**, per `docs/guides/packaging.md`) and **device-test light/dark on a real phone** (incl. installing to the iOS home screen + standalone launch) — nothing mobile has run on a real device yet (smoke/jsdom only). +- **Open / risks:** all mobile work + light theme **built but never deployed or device-tested** (smoke/jsdom only); `MobileDetailRow` unused-but-retained (legacy-usage sweep); Pipeline detail "Committed" tile shows grid-committed not deal-expected (forecast in a footnote); `handle_get_opportunity` (single-opp GET) deliberately does NOT inject `existing_investor`/`last_contact_date` — no surface needs it (the card uses the list injection; the detail derives `existing` from the contact fetch); W2 happy-path only; **Claude/Architect path unverified live on the box**; v2.0 reserve-asset spine **not canonical**; doc drift — `crm-overview.md`/`EVALUATION.md` still call `lp_profiles` live; **PWA iOS status bar is fixed `black` at launch** (`apple-mobile-web-app-status-bar-style`) — in *light* theme it's a black strip above the light header (proper fix = `black-translucent` + header top-safe-area padding + theme-synced `theme-color`; deferred, validate on-device; dark is default so low-priority); PWA manifest/icons sent with no `Cache-Control` (consistent with all static routes — a manifest change post-install may be served stale by iOS until it re-fetches). diff --git a/ROADMAP.md b/ROADMAP.md index 8203248..3ab31ce 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -280,6 +280,33 @@ backlog. **Scoped 2026-06-19 (plan below); not yet started.** The comps are signed-off prototypes, **not drop-in** (Claude Design runtime, seed data) — each surface is re-authored in the app's React idiom and wired to the **real API**.* +#### Mobile PWA — installable home-screen app — BUILT 2026-06-20 (deploy pending) + +**Option A (iPhone-first, no service worker).** Makes the app installable to the iOS home +screen and launch **standalone** (full-screen, no Safari chrome, dark themed status bar, +splash). Shipped: `frontend/manifest.webmanifest` (`display:standalone`, `start_url:/`, +`theme_color`/`background_color` = the brand base `#0b1118` already reserved for this in +`design/tokens.tokens.json`); square icons generated from `ten31-app-icon.svg` (full-bleed +`#0b1118` + white "T31", maskable-safe) → `icon-192.png`/`icon-512.png`/`apple-touch-icon.png` +(180); `` gains `rel=manifest`, `theme-color`, the `apple-mobile-web-app-*` metas +(status bar `black` — opaque, so content never slides under the notch), `apple-touch-icon`, +and `viewport-fit=cover` (so the tab bar's existing `env(safe-area-inset-bottom)` clears the +home indicator). One pre-auth backend route serves `/manifest.webmanifest` as +`application/manifest+json` (`backend/server.py`); icons serve via the existing `/assets/` +handler. **No service worker** — on iOS the install prompt doesn't exist regardless (A2HS is +always manual via Share), standalone display needs none, and a cache-first SW would reintroduce +the stale-shell class the render-smoke gate guards against. Verified: render-smoke green + +live-curl (manifest + icons 200 pre-auth, correct content-types). **Deploy:** ships in the +next s9pk with the mobile phases. +- **Known minor:** the iOS status bar is fixed `black` at launch (can't follow the in-app + light/dark toggle); a barely-perceptible seam vs the `#0b1118` app. Acceptable; dark is default. +- **Deferred (not needed for iPhone):** a network-first service worker → Android's "Install" + prompt + faster relaunches; the JSX-precompile build-step (ROADMAP below) is the better lever + if relaunch speed is ever a felt problem. +- **Adjacent issue (not PWA, noted while here):** a phone in **landscape** can exceed the 768px + breakpoint and render the *desktop* layout; `orientation:portrait` in the manifest hints at + this but iOS ignores it for home-screen apps. Revisit if it bites during device-testing. + #### Phase 8 — conform to the FINAL Claude Design mockups (mobile) — **NEXT SESSION (scoped 2026-06-19)** *Phases 0–7 built the mobile surfaces + light theme. Phase 8 closes the gap to the **final** design + functional parity. Two independent agent passes ran 2026-06-19 (functional-parity + visual-conformance); their findings + the source-of-truth correction below drive this plan.* diff --git a/backend/server.py b/backend/server.py index ea09fc0..9d9bf3e 100644 --- a/backend/server.py +++ b/backend/server.py @@ -2226,6 +2226,11 @@ class CRMHandler(BaseHTTPRequestHandler): # Serve frontend if path == '/' or path == '/index.html': return self.send_file(os.path.join(FRONTEND_DIR, 'index.html')) + # PWA manifest — served at root (manifest scope = its own path's directory) and + # pre-auth, since the browser fetches it (and the icons under /assets/) before login + # to offer "Add to Home Screen". No service worker (iOS-first; see ROADMAP). + if path == '/manifest.webmanifest': + return self.send_file(os.path.join(FRONTEND_DIR, 'manifest.webmanifest'), 'application/manifest+json') if path.startswith('/assets/'): filepath = os.path.join(FRONTEND_DIR, path.lstrip('/')) # Containment check: get_path()/urlparse does NOT normalize '..', so without diff --git a/design/tokens.tokens.json b/design/tokens.tokens.json index 39f1bec..2c1c476 100644 --- a/design/tokens.tokens.json +++ b/design/tokens.tokens.json @@ -2,7 +2,7 @@ "$description": "Ten31 CRM design tokens (W3C DTCG). Extracted as-built from frontend/index.html :root + an inline-style census, 2026-06-18. The app currently inlines these values (CSS :root vars + ~1300 inline style objects); this file is the canonical source going forward. Some real values (composite shadows, the radial-gradient page background) do not map to DTCG primitives and are documented as strings.", "color": { "bg": { - "base": { "$type": "color", "$value": "#0b1118", "$description": "Page background (darkest layer). Also the de-facto theme color; use for a future PWA manifest theme_color." }, + "base": { "$type": "color", "$value": "#0b1118", "$description": "Page background (darkest layer). Also the de-facto theme color; used as the PWA manifest theme_color/background_color and the app-icon ground (manifest.webmanifest, ten31-app-icon.svg)." }, "panel": { "$type": "color", "$value": "#111a27", "$description": "Cards, sections, modals, sidebar, slide-over." }, "elevated": { "$type": "color", "$value": "#152233", "$description": "Elevated/hover panel state." }, "hover": { "$type": "color", "$value": "#1b2a3a", "$description": "Generic hover background." }, diff --git a/frontend/assets/apple-touch-icon.png b/frontend/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..142a1410deb2436d32c745f94dd8c5b3f8dd1dc2 GIT binary patch literal 4282 zcmeHL`8O2)*B?TLEK$gkiEP896=R9UJ~XmTMW$rz%Y?C2MlrHivJ7K4BaM>x$kq&xv%$$?O8Koi~H_w7k{=iYR zfaxlxR=>gfRoiZGb^j^Lb#)t775YmbNMdcYR>b49pfVk;;HthK)gpVj2m_tnrESP> z)Snu@zr- zaPU9&|9>4+?-iez8z@ou^h8E>&Wjfn!~9OfvYcGrk6t>^azgwLZ1}|0ushP^ zGMg~Q%+G}d%hN#8SbeBtKeY<1#lbEP5$(A((Dww zX^`5-A@sdYRbV(U%c3X|W0cj;Z7-G)avAo})-6H;Tbm??6 z=Y@5x_Z*?W+|=uU?7AZF36<3uh%043FK%Zx?GtgmT)wzayVcKL&MzDWLgQP)7iGg1 ze&pdei@L6p)d8R2M5EbO+8ch0YnEKN@bvrv{X2i2hS3icZ>>Kq!S1!i0|rvTaq%Wr zg3&vhKv}3u;728y2jtxl;-j=gF$+f@WfMU>`a8SwgXw%<8J#EOOPyc2Ehn)7%eG>6 zb!;Mgi$>ht-JMu_9Ca`_wp2-3Lm%w0lP2!LirEnnreD7L`bHU@pxv+XS9|R{13TAa zml-lCUODk5T~rauk~X+sNp_FByUH%sbwjSKFghvDjfhjN!t33|lG~qqNvod-D{+hE z*n4B;iUv4|%ekkKE}dDonDC?X@$V1=w9A@ZMaD&%hbi|C^4CjiUoNKnHXcbQQ5^L5 zq`+=09C7W}ku&vS1bRg%vx~WmQx6hork@GhKD52NF=6`aB{nZ~7D7Fr+IcQrq3~Wd zvg#UqVfZyU$jyLb=B2Z_$6=RUz1wP*lQ;ZcZ{$3`;GdnuOr0r&Cb?5@)fbZ{epGWIu z&;ov1crywm(O)gjcy?Xc3h|t$sL48#9<3EPH=!^S)ft6BKgavDlj^$sFYbVv$7&m= zjIU&CU2jV2uNd@yVg524#f&{6{^#O(#`}3a;Z9@HSj?K!yQOyle+`q5+MtcHFs-hp zMF!2f`EX7=?pj^p^XT&#zYCX4l*bipx0ab}PTdnNFOhGBAUPbMQE$^xx2DK>%*6Hj z$u&rt+=XJ4QKS9Z!3qs$_wrMg?}3&0RIfJWN21GcuW*We-1hl^X`j0B?)+Wg;`jQL zq1SLwuG(!2^Rc2Ce%FGA)z9ykYnUa%-7CGN<{w$@hPUmJIIucW5vhLjF}ry-sH%a@ z7n#{{iXTst0kT!GOR{QZ3a-t>sriL@>xMv%=x;)=w`*Ny#W~YNPQ941Q=CrF$g^Pj z;spF?SASwj+)o^4Ek#cEwX5@5BkIacq1~WpLCt`5wB-EIR{Uem*YPfHv-Yhmu%;*N z#VJa?irE#N)^poya)=061Q6!>u_e4RA?v&coUjaznJaI9JQ#!GFW@^~ad-i%_Ktwu zlN9KJVWC5wltm!7x+>3phrsSuqrK-($o|zY4{W3_zDr$nVg=a(n*L4h_%e?XfggmI>ZpF zkRaAI{3t7?yPuFSZ0Wvt+!@|5-Qz-(`Bv+Yd_*I_yjxp8oVdkb0(pd;eeDJ(PVB!y zz{h{y1!B55>t&IW^~x;=xnR`XpAGVbdQ79=T7<*$;dB-9bbh=<#8P-mm?bK%N21c^4i}+V{lk#l8OSxW z>I(}JO9h#b8to^!3$AbO1kcb7w$E8W?;qOlEV^$|gwwfdo1YnCdt&9JP!*50HPmX> ztW-Wr!tpuPqy9y*=#v{wx?cIo$-Olos$v^Gk9Ym#Qs~Y~S;NIZv#<9l)m~`bS7RHY??Bs1&r({8x5AG z1@@VI9#QatitG0X-lMbz*dD7ER(TjXm~UGjwW8X+z3J#TQj-><==L1<)3YZ*6vnq@ zenxr9w*>anHo!WTW$#UrOY?H3^{jh_Fe?4pDO84ji9K6*NsaMAn|wCjV8^ zp8RI^_LF_ds&Y=CN2c<7pB0y19!kwuXJVRq3Tvw>vgM&6iPbC}YEZXsCMY~l_r-pW z-_Z{G6i{JOd?j|bCd&pU2garL_0vyvO7n>V3=bL@Cll@9*3OIgiX3ux#J z8Gck#eIee(z3*T@B)5ms9$NSOJH#23sVAJ_7qy!1QUT`47>?~}k}5@ZCulwFscz=H zD$WKUitQU&V(JX&=Q%W8v*)%kG?-lbcqJ_EZdd^(s(B_KRFm?u2{}W0un6p?M3&pi zOz91MNUqvx`v?@Vv9NTt7fTZLUZFPlw4$+@DtS@B1$_12fzlt2*{j}&B3mO=;>wnE zo5NIYCU2plzN71LW3O)FU^-8RZjwcE0b^q_Uk|HRv$WY0)G+4ZZTVHMM|KJyM1^j`?h-urzyuOj7s*)f_KqY?EPv+ zYtCN%7DY5t(CsGI7tmh*J5gU8pH)2tf&iK!1@W z1SSx$9^0c8%GWhe6=oLo_o<)5M|8E0Y1yGjQc;;C zWmC`lBAAWP22qs`%%ns|gUvk$9u60*vIkAe?k4=xJK%ITrj+yL2vg_+5>Rq9PS=-p zz6{6G3I6@k_Lr%l+T)a~h59r0X&rX7lLiWQT2q)}w26;7IzmmQ|NV(}GF2i%&3X~0 zfohV0+^jKSi8H>6V1CjHWH0!hdYA>r)9gF818g(}B2VRugLKGdtc693aL-R%Ye>IfLq{-~DK~M4oH#NEvhKit}*?rn5 z^6yW{_WZ#^+*EV`UJ*F2yq^64wSxuCwR8 zuf-x21QqyRKo4ka5TaOy`rzcYcHv}q8l8Jz=I{KLxw?w|8xnzh8r8~N>%VlzG)2|K z2hzr;?0NY8n*uF|r~SM<;w~wtKUiJoa`bgFsGOe{M3r)nO|KDr&{Jb|2_ek&|Mvnw ziD!Cjz7*H*f(Kf3DFI+~C4MMG98mIBP%<^s9DvU@7YcC!0bu@zRb@0%0D_Q|Bhysw zOLR!d_Oyc(H<6UaXWxea!AkR-TVyS6qW^5*|5jj6jL6uHClQY={QHMsZH_Q|4Z9Wl EU!nENHUIzs literal 0 HcmV?d00001 diff --git a/frontend/assets/icon-192.png b/frontend/assets/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..d17f18f9a3fe738b935a12b5291b9b84b966e305 GIT binary patch literal 4564 zcmeHLS5y;Bw@pImHAoYsDSXlbq7(tC(jmY{mEHvDh=8G2r3eBF(nD2{kU|NeL)0h= z2-15ebdV+iX+OT)`+6Vm!~d}7oSAdhp0&=r>^(`whT4pD+;ji{fKgXR!{jm&{(^?; zay6OqroIfo026I>0H*i)+U4e+>qA{P7z`kKnbQD(2_69O-jHOdM1JRT`2Y~eG+o5# zzk^CI@PY5VSQZN$RVn?I{pX}Vj*TO0Dgl?7v9nT5J&KqzLbTVn0XuF3hDc&{yoF!i7Jzi_tTOcni_}H*3UyhkQ_{d4rc+Sals92fQ%j0G(A^IUX8cnmaQWQ0? z5+yKBUx4C%**fuVCT)UoCC+)NB0{Bdqrz^4{9#`Caw z`*vp`#wgc=Hmf6*xEqf2pzyX3dRTISt5_nZg#Gfa|96Wn^{d@9U<6>4*u9lLk9{ah zC}O{MqGPA?}+;4Z=1fD5eu;K6%kdM$F`2-W};zoK$BS zqUNX_xO{Iw_PsHe(#Ix~BhYDfNI<@?vG-Ky1f(Ow#H81BealSL4#{SYG>bthnULD? zQ*=!$pbORx#d(;h8E_tW%qnV+%IPenpkgjFuX35r3MqH+%ah*a*>%3Yj!&tge|2r7qH$R4 zgLzBuK&R9Zsns;$nY6L^6Xbr{1SSuj54U5P@fC3dSLe^44UzqL#uAsTL8nDHDO4&d3H~{Veg++*+%;M?pO)q(YI%o=U4W z?qQ619JideV_v$)%YB|cUs`bb=Iak@gG3ht-3KQ5az2~&Q?JW9SBGmBJ$@k5+OmQq zcHFn}4h9G-B2VLYYqEm8EKJ&(g9Sn&Vc z9G}2hazm3Y4BD}l@3(f%|5o)IEiHz!){s3ej5XfdjT@`UomW>TNDMPT3HxS^(|P5nx=O9z$oMLp z_vH5tb^b{0#60EU>K(@V4&)GMs;Jhei-WYCB^*~T&gTVbk z5n<$a3A}$UY>RK#@UdX!mLp<=)n4UpjWY}Jlwb7PsY>m0hD=!~Czem(Jy`g4F!Q=T z{dG5>4TaTS4P%xbh$CWuJCP!bv}z9l)0luCDuqS2$gcDtj5cJ$MLYji2tiW2o_S4P{bE3eZ01)ro+r2MI%_7I z})ZJ)snLeOxnZmsxs+EhY&}+f8dfn_6sO!FN$^Fz&$s zBXY?I+PV?CvJUFI9$_)$HSrD-^lLbcmAoi4&Td$cpv@2e29_^k-dsG>X*t}~Uz|PX z44H`UM<9jt*($JdL~CoUHA6S8ZfYRgjQMbHgubp68Y)@pAc}{s@NA?HT#CGR zoy08p({@6qu9dE%d^pNIt&2K&Nm5bd?bgg0<7mrVLYu<+r zYm0yPFmAxS4?Rb{69jr%Gd!BXOs#&cKL<~9j<*&Jc&__6ZD?8g$aqZsG{=v&W{hIaa8w>!bOv?N!enU)K_Ja9TS`P5Tqx25C_c} z%c2Ug9T#g_|4#XcL)4;K=Pc3c+nvMZ`I+m5ASan&)L=RjuihPV#Hi4Y3o~_8g9O50 zILJJwQ|QRC1HDlFdOGY-!LZBUT!JM8FG$#M(K11E5A)b0>~k!HpO&j!Avf(4YmbT| zpKX5etA}p;%wH|U9FDF;D20@CZLT(zGohZm@6NrF0KIqv4`0wt3xg<;4IGembyP2s ziI=QRa)M_AJDx&D`>EGs7)n1LUJ38ias&ziDp|MiiERH`m%# zY_wXi{6UK>j^9o8#q&nxn{qH=#g>o_CS~-nQelC+XWs=5f9#jd8$m?)4Ypa4&pQ0K zJa-dae5hZZfUQnHl`Zy~YKXtQV+L!210GmVHnPl?l)4%G<~~JnbVT)}9Y=?cSKWm_ z%yFg95K`H;vLOD$t_Gk@4UX6pHr40MPm)*gG3foJYnm}D*@NQHuj-5n347X3F)mN} zN7&rPFg^_BbXW027~%Vhd&nqz1sc~WIO2EOWm6M4s?R1_DADIm)v+0tZ#vX`c17!p zYvR*JilbGn!dlzjPfgZoW|iKRBrV4EzseIjnPI{XcI%(2v zL0!^VBg7}sFyy9%0bf?9>sCy}w}{mj(Z}+CWQbO67JVlZQFFzeGkknb#&`vqxw4Jz zsV0}o**uB?g9Xl6tJfJ&n9v-sG4Zuak8}mhibXZH$};k>$wTCXoPU#$t|rMk3q-wJ z%{e~U8u^7b`>_+`EzeTUy?)=EUm!An9%8D@cC*z?vpHxZOTaWa`N7&k$EensQiPOp9W$Yn(4 zOPnLmU1cFq+;fwF+W&!QYIbrVYoDEIVM)QDzxwT9xLM{>qBV~0^-`864ZBPeV}r{y z0x;NaxDC_uO1ksa(V5~!=aCg6d}imK^-1f)9M&7>tMHF zN$;4Kd+LXv8`hp*`zV2@OD^_ulP#ke(r!aE9$x--m7zviO1SRt&-={3peqaDnaWw zEVB6A;nM8N(o!;=r>7S<*N;ITQ2R`3_>hKGCDUQH-v-%H+s7_fu4I}~ECTeJ8TGo= z0pRo(f=j|lm!9|CWHCg&J(AKb(t{u=ponx!w{#38Do86G(uhchbiPUt9YYTY4Bg$| zo%dbe`qujW2j5z<)~uPsJ@=e@_TBO9XYWrh)D;QwY4AZH5TUY?ye0?)27ZD;cz1xW zBk!>b;0w!5Q}HRNbbxLh7(B7mRkl)7191Ys@jzIiwjc;*3h4QyZZuE&I}C#f$^XVSs(OK{)*(UGW<1zzn14Ceo^Ru}**m_J!1|F@0>r72Sug68FoBr~(z$jAum zHLQV7+PT&mcw#8AI@O&9KB!%!PM)B#n?Zy8o85 z{7JPt{A|x`s@6GhtwI2i^+T(ayTssUEHBcC=UBar7Oh?U5x}8!9Bm>XtkpTXHDmwW z*0EBQQiIG99_}rC`OS8NhoH+4iS2Y#)a-tlpXFg<$+=CG8t`UW)c>Jwtj~x@$ zlWsC4CyQ$7p145Ul+26lUuhB2-?XHUR@ZZ%ZKO@o(B;YkuY`#OhDGwQd-ib^yu-`Y zbCgoh;{q}3)O%ru?=aP%Htk;b`%>4^FxjA-1_O?xf!7DBH@+UFS$FB7sC2wM0r`3r z^&K6#ufvy04sJr3=V+efJ_8nxeh9;IxtFEX+UzvvTVFo9Qt5M;s3fZ9Cx$m0prex_ z=;2FbzekB#G!f8(nDhCTH=)(7XZQkCMHOdUdz)pgz2is}3jr0+N$ne7Ibg|n{1>3J zhmXt)_nZd7+bhq5;7@;Q*BJ)ii2N<^@}T_*B9C8k+=` z+sTTF5o7m`qp>2ePC988Xz$^;Y9eo(ag6k$oQ6ATbeoOyKl!zx_Xw6I$`;>sOk9897<>UY6 zpqYaTGX2^IdDjVmo$#J#y)c#P((OVE-Og)B)4s`db$$|pqUQCQ_pH(?(;jbb4;A~9 z^DIK;Df}+S%RjY>hK2&l8ZuBT*ZlbScvQg0jDp_9U!lJo62^067tWSqVw956_9I)g zq}d1^W^&N^pG&u;8)u>~$D5*8=itE{O(@_}d}a0{n?&7TXmw_0W@$)fhmZZxV?O$> zSJlA6&Z-QD!`EP$v3oP^((@8)RGj2DfwbTF)%U}LwRSfXXKI|27S>2ysKMECwEji=(+5bA>Z0|YaF0<=fqFs0{7q&4-s7ZD%5kN z!e65BWpnC}w-g+jEJvRs&L{JY(zKakWXKvGxJ^f<2wHC36Oa*goG#|HN_tnQSzp;C zvfv@MQ`c=IScAI!9>(g8g&zb*x1|d{)`_Y5o!A!2tk}mab<51IRY;bA&GjNHD1G#M zw);K$qZx&{C%p+g%(q_W`Pre$L1N58R$EV~1-GQ6?GE3@aHqsVbEO}!S$>IQp0>w# zbCMquDvNpvT7JEAx~&k+Kr8N_GH5kvm=eUXWgPIctm*7(w3n$*Ed6lMo|DRJ14>n6 z&y0rBa7COy>6Ap+uJvSUB*@Br?8zIHrix>ylmLGqph`Ub*vuo+923gaoV@u|_Z?N( ztk~CuE;1zu{Dcv&k?(72>tnB-=@nIz^X)vx%|t=J=>kEgBV5-vNt@;y3z<`$=^vfu zHo(*Z@bY5AgM?W(3X?B$SR9?|%F7arR4!SdIwXZCUEvM0A63wL>y3F*3AMjUJ*4Y^noT>Vg zN>67w(3@f0=T!Vii)80^L*KVuSE7k(1kW445=IX4y#g&`!@Gx04=O~`6hhi!vv+^o ztBN|+*+X#Lm&3ieI9Au8&NY!uaTbxBx-`>k@eZDKm$Mc?%9JmwwZZRO9EI+F#P4hR zNiwgE=1u2rCwKu`7QjW9{UVkgxkj2Xgmzk`KoE%N)PaNK!L$D0+uYO8Uh3mb%kcPu zo_y85K0ayoS2FZ`1>>bSO1wA-Ns{|r(bb;QGHy>e3gM90Ddz91B-F&%OuIQ3U z!^PNF4>Wc5=0k*D@a%K_yZ}>5Y~g~-Rm7Fym3UfwYVVvT&o`m1Z|9USN-Znx$_)=1 zi~WI2zU}ZY5x;ICldXLyyl2uF&f=6aSL)8SBMxEdexA(hx1D_lF>&e$fsaj1_2w&H z2lb-+(LV4^wU#O%WW5>awN}5cQ>6bIvCQ8;D5Ejpz<>HxJ zwVcW;8rqpK?pxYw#hV3RoMKXxatCufx@wUS}i z?eN68%SGIkdB2_TH5TssP3E1FY#J$v1#h=!YE4@7>K+|=FXrn!r9!3Hlle-zv^GrL zSHt4BzObl2`DlOvXY{jvRg9G>FL)hl=I5R($bd058D($;s-EtysKwz z&od<)_R?Gfk%7;S7M#V`MHhGkM5V&DUcTX7qY<%67q)Y4Nfw(Ays_Li=_EViTj@;8!ILKOKKaRz_abU9$q!gxg(RL=*AAOesUy$=*05{(zH%uCw4=+Ss zw48jc7a9OrOyHe};lRpoj-uO4Ae`F8&`~xG61RtGC9J}zmXgk9&wfQV;-fWIDMo+8 zaqDJuLr^>RrTA+|GS_9JSECN)KqgP71|1i=>^6&v7j#b?JMd&s1Qe*hcP|)*^Jhub z`>>QaDNbFo-Hr~O^`c5aM6cq&ujo3tFuq?D_oDTtGBpAcn6|hxKGa1HV2&E`n=Bf> z?YYkSS6u`vKe%`aj39-M#_zShVG-FO&kS10L4nMRIFKtUe%x=1a`RtCn`RC#DypBf zrW}LYBA0%Bev{{rB6u)68gnU7Tzek_pYT%-~xwIYifwqk8%;JcPc5Q=tz6u5F&i6}da!#!m7}VO{NNdvb9u>froY~r% z7$5)0!wz}kv~4Kzhw76)eKFG8_X2CiXeb~VOb#-8T0sM@fgsw?K5mx%9ET@vKemNO zClf3oR*wU?Yf*Er(ft`4^a!%6HQr+B)W_1I%Qq*nW@aVMZ}O*q+I3USBgy-zp*o4! zxQ{^9%c&8dpBC?)q?7ZD8m}*@nQWetQBkq4dh2|+{8?;JYs}zph$>0QT$}5=3}dQ1 zZ5WIyOlg_S4{5FV^l^&CE$)|%aS|AsTBW%4l5eQl06An`BYQ9J*`Za7r^MDAX{G(C zlgXu|wspQrVmxjmDzJFmTjm{jJsm47%jzaI2h^R(Xuuq6jU`05NnbmkzPf$bn4Mxi zIoL4^+iWIfJfE`E^*8bxPcSb{x87G=03ktV*z^i~fE=I|_~6G7abKa)l(HL~pMXz} zCg;{2Lhj%a26Q2B{#i$mo7c{{$~`HnREI#$@Sxx@QoDCx=+ju?5SSBAY0F0VtP|Pr zaer``c0UcNX~9(|D>Kf@5JpZdBZI$OSh zl#!Z!&0I5&oG*DV!s}>IJN7^u(GmYy7AuRjkQ}5hC`+e)SF6xaufv0*6RSgx!~iggKo&8bs~Qhp%ClE_JM z!4`xHOz>Zt3tN4Z*P5>3wxv zYHD)O;*;|VBAg(`-lO2QmtcK-kXu3BNPZl=m1E$7kT#lZc)tk6*q>aOTLYLF6DOzu$O8hL%j&QwZggq(w z0&@_aIl77ifG*%lF|ZHmWXT#RIa!yJPp%vJxbbwW2YFKSnCMR`p23^pXZqu23a$;m%tiM)+e5UA_8WSL`BPw9wvldV zj+$vFVk&3RZViANcb@9x$h&ySB5d1eMKwXo%fI|8SOJV~aBd;|lBwJc$(thPr@k;8 zAbAo8Gk^55$s2mjQ1og5M5h{d`UXfnpox_|x;ZKft$ZhUg^gdmk34xh(TE6n?RUpC zu7x99sBxbzSP952`oWu5zMy|VW7pjz(uj|?UN zi4OF$O(3t&lYUm(?2~O?4`G2*0m?+!GDJ6>%6o0GD{qNar}s;KACq|ndevn4=2nuW zgs({#A*=pNv&f2&(Cmu!V*vI%nUm%r62NI)V}e1EkdZKDId*NVjxJnjLCf^y7a-5Bnvb_likd@FeoQvy2gp) z?c29@=JWu5DZa6dO7+HrV95Xw`)cePAB2vo?&y!Y9Rx^AIp-QqweAxKLlx@g>~*Qjwsm9jB_W!szqOw-!2PK>391Nc?uXAzXI1ukSmLVJP4 zL5Gw;7D^Hi&>t@jjv|8Oz&Hey@s|xZsvTj0hSzb)Y5Wz?g$7`UO+V0*VjFI-f%68_ zPrEy}*SCbUk9${iJOH)SVpLG0!dnAKnxU_ZIdaeq97H;f6eAqE_cpopiqB@|Wy`JE z>xBvu5SRv|0~Wy#NW#p575$E5HCa{fi2q~Rr=x@+=8}GHf}=tPNQVG>$l{;{=(@pHtSl_LmpMk4znbm#yjRZbDQmr1SK=nL3Jv6Az8F<94 zyLeENWSE^@>g=Z4bd}|1O`Cbv8;BuA7RW{9Fp?Gv2aKk@Thwa6X=`g+IUsS}I+51w zT;kOweR!Dk)82Bua^;}Ji(STz6bkUG@&F|Ut<0zf)X0bAk$&~}bl={d?5^Rog1o$L zBmq^=I~-K^Rapy>R9zKnSmi1jwgcFst8*tM!13k63=PwT9pO%E`--*bt)P0Rh1{2K znj&zlBkB`_0Q$+5y=@NUw%Rd`2hD;lyytwKclcAMXYwWUa_CCL@`u%oFA9W%S|rpD zp(Ikk(T3x=n4W=xxgi)Lp%&$u`#eQhJ&`YiFUIeb^}BJ29i{yN8KA^~t#uc-y8)85 z!*a7)XessTdnY!UkFxjAGw;BEDaG(kvF#gRwrWPx3ZP&ZBwAvpZpghWf(rhynK1wW z(5etJ+^o4_1pc-`?c(|b>?m6J;2lMFq0^;t!d=~dvz9*+y_(Q82gks%j;`!zvk4sr&Z+p zquOce>2!5NTcd$#M_4uey2A5Cb6)bM{zmPEjRDc#@C1}%1Hm-lH2~L;!)pnN3?d5t z+UWVor}*Nn#~P78+mBJZ@%RbjheM@mTnRcWI#S2vyXNitu5(efz43*k9xyT-FxFic zLKtk%Jp#s!&wXgM?N>2aG?Jr+TfcTt^lWMN=I-5nLA&4l^(&pc70KYv_pnJo*k-RP zn80|;3JZrEqj_dahxt0Y>=vk&>-wp&iw5+?ha_Wfg^WP-T>+|lmC%J8kY7uLo;3o9 z)-HPsvg?Pvw3=mRZ&Hps=rVl?Thk<*8}+2&USaW%UP@j?>D~{1fMFFs%M0Fv2opF= zSL^XMALyqDd1W;4_lpqx!X?(CqQZJ;3P7s18vbp-a z!`->8ZK%V;!%71l_pyg9Ef(a4$+Q%w4mj2_?e0#%bfPh_G*oRsO3^^~cSw*3;OgzG zg(E>=y}MgeRXR>h$GieaTp&^X5FCPgk|5ZVFY@6TH=gc&fK^pc{e=gO{Th@ak+mCC zTj?iJj_ga~W5>R1($C=vO6%oSL>L<67rMN?} zc;@B202BN*nFy$Ya0So#opj&VkjWf32yUIIQWO zdf%%$-mXQp@7^O;V=s2>M*9Zs%r)2%aIyVaT%wS&%>xa^5!inw=QW(vpYd_512RsZ zx%h2!B@Erk<}a?d5>{HbMJBeg9QZwwr&iWhd@Yq$sA7!-h(Moli(cADPNqhb=#ub* zm)PMy$o@&k;jbl&{=izHr7ZI6yJ6v%R9!>j_u#f*sVu{($_)j*Y`Bky0O<>Ua9qLj zgzD;QQ*jxEG9atx>tYPN5||jd^<;g9JI9&80jk~-z$6i70@1E3M_-*}4kLX5eoP=D z_uIM+DZdazJxxH>vTlE$HP5Fi@JA$%sO;-fEdkLY1EX52s0}izu2%n|ZnMYgSR`y( z!eyR2V%9|jGQ7O%`n3+(yu4t11=!M2TcDErhRY(OM(%+%rWB;vfp2Fn`fS`X$uN(N zAqhJh6t=&W#1prr=KP}!#J`nfI400}Ol@2Twlas7s5aJ=RDYz@Hcaxp9SA4qAe(3i zC~<$4w0X4=9^eXys2hu&a(-j&`Z(t}V3o?tj&FNf{xq<(qCCdt!7pN&;t>*sM1gp9 zl`jCT@UZVRXR*U?r^U!9nQ-7cpN^qtMMIq-K^PeNQOJ0zakBHdO^%0viJET}s>t;~ zMty{a>Fqho-jas>^4{Vv>EpypCp+Yil*=e`hvCk~@%?p=%|~I&yAPXg4k9#b2W$fa zLv-MPHtt1DqoTtpc{q28>6hmwS*B3YZc_$5?;+G;=E=f#-d4P|7RS0|N@{8e0Qvo$ zUQ?J71OPv!k4@tyC582P-oN*1^R@do?A}Bkw><)x^Zx<}S~~GFq(rlmR%Gc$VL|um z{VtVTK-4YvE?Khd8v=2p^ts+QkTt{QA}jYOrzV3*c!sOVLjMmHU_u6A7EQWBtAqpM$W z*Va1kO8@*d>Q3RWt`ybmeD(Z+gr_-gf!)Kf(3;XJX-BNIe2x9HrE9+_%_ z=6!==T+J+RbDw6$rjCXHNT)T?1>pvgfPSy6<-oP@MK&CwKR*1s#s6{z;K(fI-<4Sc zfM~hMnKHaS3C_)jX8z0;HMoBx_=BZq-haJc@yY)F{+BObm?XS*zR)*%_5z8W<>Egb z2ixHZKV(D)9WxN?8>)O0=l!t&{_37mOI`I;(ng8~3Z&6ej-L)3iP*d_LBMgwi(dbn zE?|sGThFF~A4egQMGVjbr#EwcgBT)N5jk+&POleVFss!?(PZn!<%?P>S)-rrk2FS; z-+p4(x_kXbREOQZxmTvO8qN2>jjc@*;kq{T@Z4-XrgBzd&%Y7AvMZz0ZMQ-dOW&8R z)$_%5i4ggz-n3{~&swFf;A#Kd5!&xoHu~Bk2Lo3GGIJFVD+6x>elD_mql}2n1yCuX z9i|kA&MhhwV(WSLR~YAi(Vg|nE%{3yY+9V$R?&W?@Vy$InD*GTn5yM!3OwanZrs)J zxh3|FVGJNYbPi(O1*c7u_z%qT^ z!;F1hOVT|)*sQy$1R}e2+idl%o2|devCj28w`Ms)LhmIZF9>6K3~JB?g>5zdH!1AP z12axU#kc9=j{}3X#ij;Cu>g=yE&e@ezcf%_)H5xH_IbNWq4_gqrLaK`?p4$b0LzN? zf9g+^8Zb75xzAl3A9SSGn-5Hu(-{N_fw~?aI7ZQo7xf+^jkXN}D@H!or%oE~*)X7G z-rGC^*=d!rF*eHxo1^$NRGtG!exApuv{j*D9%ZBuMNQ?gJ`TUb-15Fz2s9`;C2@p= zkFh?NZ9_8)9zJ1z#eKVZn9A*URrtJYPgnwr7mQH5%;UAGgW-JPruIxGY3VFTj6IaAap z>~jCElkfa2N)T6OprTL9Gw}=>1FoI*Zce7nf>g}z8qwKU8IHEFc)LMujLz_2_EuqB zGQhK+55F;h{6oTDYu>zz@(Qi^_VqQQmLvHZ0V>chkb@c+)P%RUs*lwJri z=sIyMG4XzI=UH@-R`H96U8f=NQG`%C89n&2Sx-pspZh=`045g*mLzz7?cO9TStOfL z8#DG%6m`Sr+}RexG;SzABT%KcGkc{vSp`+7KIEsdcMY)DtM*oYU|)$39%gQ%4qEG> z_^gl}lrq!WPJOH;Ki^_xPx01*;(f|F?<_}b)$}~t9-ToBRDr9ea2%OQfL;U`bjy7s z99DIh`a`Vzb1$sPu_IKRDcQ)xrIDzy|Ch1h>3@1E_F-4yic z6)wppr2?Om0ilM9k(N2O3g+s%p02aEKL^IaS9;pi(%6?$H{js%F@|i>%W_Ax)n)k4 zo~WrBBu5E=;^AMOeII6zjgSA%xz>DnQW)L^8U4PPqB+q>QQEl@b>nPzFCv*F40A5| z2ey#lVjRtr&2#mG(z(VJpw_I1OS4dfq4-UAr@WfqF0oNx@1JeyEYL%841v{q?<<`( z;7nXqYO4j!)U2ls&Zpn(jx9bFQAok2-kUHzKSr7ewf=U4n5zJ@zm4>mR^Pq4I^?r^ zI^E!rma2P$w>%Od(W}g!vDZ~*7w|1VzbC6*Zv3|#6mJrUt>xJ&X?Rz>@w&of&q*Kmphs@qzWCG}t;%zdt`sVod zDuORU47=@J5GM2EwMY@T!#Q-@FB^Q~EN*hVWH;V3Vk&g_yy*sjB(yg$mF+qMFu#lDiL$20K=^+;cp<^}oweco%LHZRj&*J+({BA6>@XI+n z>C%@U0PqYwwk^Uj$kXSkI*BZYSWQ4}anK z6Uh%{h z(|1fWT4~tEP;^GOEgC=>JZ;l)3KXnMwQvtsY@NJcNXgHLTWvEZ3or%r`%gwkMwg54 z9G6_P2K>4EdbyJq*`s4JfcJF=>Aokbb50!S{C>=SGC3sh_bE3`Bn8$0=(h?RwW1aQ zP&QIBK1UsnoKF`qNk?PID-5B*wL61r5?G~_Sf-m&&^Y({ht|uRsl1AGWOaqGqibn0 z7`7znH;^=vYwZkp;ZL1=VRx}a04dpN%0ltN0F<8Hbo5V^X3g_T5u(GWAb|eq_ATSr zZ8ZyVK()weHpko@86D|J7kQ*x{x2PqvTUKO&dUSTmQ|_wGDeh@C(Og;e-#DlGdDB% zKkbapB>9B4!46KwkmKj%LTxtpm4Gn5n&D%3vwNUg^0g)Aaa{VJxn}Da$ob8_ zbduiu1+RUGu^@sN_T--h4uEI{AooLJk6kwUnc7^EZYBY0YU+cD1+V2oYvb5$2~UfP zJA4m=VCuj@#Mbv1!1$1j2{AQask5MJ`%x8H;r38dHc=WWD?=Ool`Q8fx~4m%&{!{k z$!eOZU><~0l5LDKWyt>V zQqOw%OK1gvL8Lid6uzfDECGa)*{(VL%{rRk^}$_Hm(eg>!#CU(F%h^gv7ZPN+H zG|6lx@;r^(t9G2zREdAIV}Dx%?1Q4+Uwm|Ky1X`2OW|-V*N9gKWS#lf-g*AglmXzLPuWhD$%P2a0;moXBoF=JhhoqsevT!8%dY~mK%>O&?59HCgxOHT0lt?AuFE7E^QLqI2GmYBqEOwS+Cz{m9ycZFS$}>-{$p1_(u)0iueB|8`QEVmidgxcf2fWB`E%aO*HP z&oILO4y6j5`0o+_zQw + + + + T31 + diff --git a/frontend/index.html b/frontend/index.html index 28cbfea..20f01e3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,10 +2,23 @@ - + + Ten31 database + + + + + + +