From 5f88731f536e9b704ef032fff8573afb849bf1a3 Mon Sep 17 00:00:00 2001 From: sommio Date: Tue, 21 Apr 2026 21:33:40 +0800 Subject: [PATCH 1/4] docs(best-practices): capture operator-owned compose contract Record the root-owned deployment boundary, the health-gated startup chain, and the HTTP-only Caddy contract for the first VPS compose path. --- ...ment-owns-operator-contracts-2026-04-21.md | 155 ++++++++++++++++++ ...ment-owns-operator-contracts-2026-04-21.md | 155 ++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 docs/en/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md create mode 100644 docs/zh-Hans/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md diff --git a/docs/en/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md b/docs/en/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md new file mode 100644 index 0000000..1294028 --- /dev/null +++ b/docs/en/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md @@ -0,0 +1,155 @@ +--- +title: Keep self-hosted Docker Compose deployment operator-owned at the repo root +date: 2026-04-21 +category: best-practices +module: vps deployment +problem_type: best_practice +component: tooling +severity: medium +applies_when: + - adding a self-hosted Docker Compose deployment path to this monorepo + - deciding whether deployment files belong at the repo root or inside an app + - wiring health checks and startup ordering across web, api, and postgres + - clarifying which runtime inputs are operator-owned versus app-local + - narrowing a first deployment path to HTTP-only reverse proxying +tags: + [ + docker, + compose, + caddy, + deployment, + vps, + health-checks, + monorepo, + operator-contract, + ] +--- + +# Keep self-hosted Docker Compose deployment operator-owned at the repo root + +## Context + +This repository added a VPS/self-hosted Docker Compose path that needed to respect two boundaries at the same time: Turborepo app ownership and operator-facing deployment ergonomics. + +The risky failure mode was to blur those boundaries. If deployment logic moved into one app, operators would need to discover app-specific entrypoints for a cross-app stack. If app-local development env files were reused as the production contract, the deployment path would silently inherit assumptions that only make sense on a developer machine. A follow-up correction in the same work made that drift visible again: the first Caddy contract had implied hostname/TLS flexibility, but the actual requirement was a simpler HTTP-only reverse proxy. + +## Guidance + +Treat the repo root as the single operator entrypoint for self-hosted deployment, and keep app-specific build logic inside the owning apps. + +For this repo, that means: + +- repo root owns `compose.yaml`, root `.env`, `.env.example`, and `Caddyfile` +- `apps/api` and `apps/web` keep their own Dockerfiles and package-local runtime details +- `compose.yaml` builds app images from repo-root context but points at app-local Dockerfiles +- only `caddy` publishes a host port; `postgres`, `api-migrate`, `api`, and `web` stay on the Compose network +- startup ordering is health-gated: `postgres` -> `api-migrate` -> `api` -> `web` -> `caddy` +- health surfaces are explicit and dependency-light: `api` exposes `/health/live` and `/health/ready`, `web` exposes `/api/health` +- the first deployment contract stays intentionally narrow: Caddy listens on `:80` and proxies to `web:3001` without domain or TLS configuration + +Keep operator-owned inputs separate from app-local development defaults. The root `.env.example` should describe only values that an operator truly owns at deployment time, such as `HTTP_PORT`, `POSTGRES_*`, `FEED_OPML_HOST_PATH`, `INGEST_ON_BOOT`, and `LLM_*`. App-local files like `apps/api/.env.local` and `apps/web/.env.local` remain development concerns and should not become the production contract. + +Lock the topology with a root contract test. In this change, `.github/scripts/compose-contract.test.mjs` verifies service topology, app-local Dockerfile ownership, host-port exposure, health-gated dependencies, exact image pinning, and the deliberate absence of `HTTPS_PORT` / `CADDY_SITE_ADDRESS` in the first deployment mode. + +## Why This Matters + +This pattern avoids two classes of drift. + +First, it preserves monorepo boundaries. The web and API packages still own how their images are built, while the repo root owns only the cross-app orchestration that genuinely spans the stack. That keeps deployment implementation from collapsing package responsibilities into the root. + +Second, it keeps the deployment contract truthful. The HTTP-only follow-up mattered because domain/TLS placeholders made the first rollout look more flexible than it really was. When deployment docs, env files, and contract tests all say the same narrower thing, operators get one stable mental model instead of several partially overlapping ones. + +The explicit health chain also prevents premature startup. Caddy should not route to a web container that cannot reach the API yet, and the API should not advertise readiness before migrations complete and the database responds. Dedicated probe routes make that state visible without depending on business pages. + +## When to Apply + +- When adding a repo-wide deployment path for multiple apps in this monorepo +- When deciding whether root config should describe operator inputs or developer inputs +- When introducing reverse proxy and health checks for container orchestration +- When a first deployment mode should stay intentionally narrower than future hosting ambitions +- When documenting or testing deployment invariants that must not drift + +## Examples + +Repo-root orchestration, app-local image ownership: + +```yaml +services: + api: + build: + context: . + dockerfile: ./apps/api/Dockerfile + web: + build: + context: . + dockerfile: ./apps/web/Dockerfile + caddy: + ports: + - "${HTTP_PORT:-80}:80" +``` + +Explicit health-gated startup chain: + +```yaml +api: + depends_on: + postgres: + condition: service_healthy + api-migrate: + condition: service_completed_successfully +web: + depends_on: + api: + condition: service_healthy +caddy: + depends_on: + web: + condition: service_healthy +``` + +Dedicated probe surfaces instead of probing business routes: + +```ts +// apps/api/src/health/health.controller.ts +@Get("ready") +async ready(@Res({ passthrough: true }) response: Response) { + await this.prisma.$queryRawUnsafe("SELECT 1"); + return { service: "api", status: "ok" }; +} +``` + +```ts +// apps/web/app/api/health/route.ts +export function GET() { + return NextResponse.json({ service: "web", status: "ok" }); +} +``` + +Narrow first deployment contract for Caddy: + +```caddyfile +:80 { + encode gzip zstd + reverse_proxy web:3001 +} +``` + +And the contract test locks that scope: + +```js +assert.doesNotMatch(caddyBlock, /HTTPS_PORT|CADDY_SITE_ADDRESS/); +``` + +## Related + +- `compose.yaml` +- `Caddyfile` +- `.env.example` +- `.github/scripts/compose-contract.test.mjs` +- `apps/api/src/health/health.controller.ts` +- `apps/web/app/api/health/route.ts` +- `README.md` +- `README.zh-Hans.md` +- `docs/en/plans/2026-04-21-001-feat-docker-images-vps-compose-plan.md` +- `docs/zh-Hans/plans/2026-04-21-001-feat-docker-images-vps-compose-plan.md` +- `docs/en/solutions/integration-issues/ci-e2e-runtime-inputs-must-match-seeded-targets-2026-04-16.md` diff --git a/docs/zh-Hans/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md b/docs/zh-Hans/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md new file mode 100644 index 0000000..58d5951 --- /dev/null +++ b/docs/zh-Hans/solutions/best-practices/repo-root-compose-deployment-owns-operator-contracts-2026-04-21.md @@ -0,0 +1,155 @@ +--- +title: 让自托管 Docker Compose 部署在 repo 根保持 operator-owned 契约 +date: 2026-04-21 +category: best-practices +module: vps deployment +problem_type: best_practice +component: tooling +severity: medium +applies_when: + - 在这个 monorepo 中新增自托管 Docker Compose 部署路径时 + - 判断部署文件应该放在 repo 根还是某个 app 内时 + - 为 web、api、postgres 串联健康检查与启动顺序时 + - 澄清哪些运行时输入属于 operator,哪些属于 app 本地开发时 + - 需要把首版部署路径明确收窄成 HTTP-only 反向代理时 +tags: + [ + docker, + compose, + caddy, + deployment, + vps, + health-checks, + monorepo, + operator-contract, + ] +--- + +# 让自托管 Docker Compose 部署在 repo 根保持 operator-owned 契约 + +## Context + +这个仓库新增了一条 VPS / 自托管 Docker Compose 部署路径,同时必须守住两层边界:Turborepo 的 app ownership,以及面向 operator 的部署入口清晰度。 + +真正危险的失败模式,是把这两层边界揉在一起。如果把部署主入口塞进某个 app,operator 就得先理解某个 app 的局部入口,才能拉起跨 app 的整栈。如果把 app 本地开发 env 直接复用成生产契约,部署路径又会悄悄继承只适合开发机的默认假设。同一轮工作里的后续修正再次暴露了这种漂移:最初的 Caddy 契约暗示了 hostname/TLS 可配置,但真实需求其实只是更简单的 HTTP-only 反向代理。 + +## Guidance + +把 repo 根视为自托管部署的唯一 operator 入口,同时让 app-specific build logic 继续留在各自 app 内。 + +在这个仓库里,具体做法是: + +- repo 根拥有 `compose.yaml`、根 `.env`、`.env.example` 与 `Caddyfile` +- `apps/api` 与 `apps/web` 继续拥有自己的 Dockerfile 和 package-local 运行时细节 +- `compose.yaml` 用 repo 根 build context 构建镜像,但 `dockerfile` 指向 app 内部文件 +- 只有 `caddy` 发布宿主机端口;`postgres`、`api-migrate`、`api`、`web` 都留在 Compose 内网 +- 启动顺序必须由健康状态串起来:`postgres` -> `api-migrate` -> `api` -> `web` -> `caddy` +- 健康探针面要显式且轻量:`api` 提供 `/health/live` 与 `/health/ready`,`web` 提供 `/api/health` +- 首版部署契约要刻意收窄:Caddy 监听 `:80`,只把流量反代到 `web:3001`,不提前引入 domain 或 TLS 配置 + +operator-owned 输入要和 app 本地开发默认值分开。根 `.env.example` 应只描述 operator 在部署时真正拥有的值,比如 `HTTP_PORT`、`POSTGRES_*`、`FEED_OPML_HOST_PATH`、`INGEST_ON_BOOT`、`LLM_*`。像 `apps/api/.env.local`、`apps/web/.env.local` 这样的文件,仍然只是本地开发资产,不应被提升成生产契约。 + +还要用 root contract test 锁住这套拓扑。在这次改动里,`.github/scripts/compose-contract.test.mjs` 会验证服务拓扑、app-local Dockerfile ownership、host port 暴露边界、health-gated 依赖、精确镜像 pin,以及首版部署模式里刻意不存在的 `HTTPS_PORT` / `CADDY_SITE_ADDRESS`。 + +## Why This Matters + +这个模式主要避免两类漂移。 + +第一类,是 monorepo 边界漂移。web 和 API package 仍然拥有自己的镜像构建方式,repo 根只拥有真正跨 app 的编排层。这样不会为了部署而把 package 的职责塌缩到根目录。 + +第二类,是部署契约漂移。后续把 Caddy 收窄成 HTTP-only 很关键,因为 domain/TLS 占位符会让首版 rollout 看起来比真实能力更“通用”。当部署文档、env 模板和 contract test 一起表达同一个更窄但更真实的契约时,operator 获得的是一个稳定心智模型,而不是几份彼此半重叠的说法。 + +显式健康链还能阻止过早启动。Caddy 不应该把流量转给一个还没等到 API 就绪的 web 容器;API 也不应该在 migration 完成前、数据库仍不可达时就宣称自己 ready。专门的 probe route 能把这些状态暴露出来,而不是拿业务页面充当探针。 + +## When to Apply + +- 在这个 monorepo 里新增跨多个 app 的 repo-wide 部署路径时 +- 判断根配置描述的是 operator 输入还是 developer 输入时 +- 给容器编排引入 reverse proxy 与 health checks 时 +- 首版部署模式需要刻意比未来托管能力更收敛时 +- 需要把部署不变量写进文档或测试,防止后续漂移时 + +## Examples + +repo 根负责编排,app 保留镜像实现 ownership: + +```yaml +services: + api: + build: + context: . + dockerfile: ./apps/api/Dockerfile + web: + build: + context: . + dockerfile: ./apps/web/Dockerfile + caddy: + ports: + - "${HTTP_PORT:-80}:80" +``` + +显式 health-gated 启动链: + +```yaml +api: + depends_on: + postgres: + condition: service_healthy + api-migrate: + condition: service_completed_successfully +web: + depends_on: + api: + condition: service_healthy +caddy: + depends_on: + web: + condition: service_healthy +``` + +不要探测业务页面,改用专用健康端点: + +```ts +// apps/api/src/health/health.controller.ts +@Get("ready") +async ready(@Res({ passthrough: true }) response: Response) { + await this.prisma.$queryRawUnsafe("SELECT 1"); + return { service: "api", status: "ok" }; +} +``` + +```ts +// apps/web/app/api/health/route.ts +export function GET() { + return NextResponse.json({ service: "web", status: "ok" }); +} +``` + +Caddy 的首版部署契约要收窄: + +```caddyfile +:80 { + encode gzip zstd + reverse_proxy web:3001 +} +``` + +而 contract test 要把这个边界锁住: + +```js +assert.doesNotMatch(caddyBlock, /HTTPS_PORT|CADDY_SITE_ADDRESS/); +``` + +## Related + +- `compose.yaml` +- `Caddyfile` +- `.env.example` +- `.github/scripts/compose-contract.test.mjs` +- `apps/api/src/health/health.controller.ts` +- `apps/web/app/api/health/route.ts` +- `README.md` +- `README.zh-Hans.md` +- `docs/en/plans/2026-04-21-001-feat-docker-images-vps-compose-plan.md` +- `docs/zh-Hans/plans/2026-04-21-001-feat-docker-images-vps-compose-plan.md` +- `docs/zh-Hans/solutions/integration-issues/ci-e2e-runtime-inputs-must-match-seeded-targets-2026-04-16.md` From cbec155c6783e1ca38eec8dd681431afef06784a Mon Sep 17 00:00:00 2001 From: sommio Date: Tue, 21 Apr 2026 22:52:25 +0800 Subject: [PATCH 2/4] docs(readme): refresh root documentation Consolidate the root README updates across both languages. Keep the sanitized screenshot under docs assets and tighten the product and contribution messaging around the current agent-first workflow. --- README.md | 298 +++++----------------- README.zh-Hans.md | 279 +++++--------------- docs/assets/readme/rssift-reader-v0-1.png | Bin 0 -> 199098 bytes docs/en/CONTRIBUTING.md | 128 ++++++++++ docs/zh-Hans/CONTRIBUTING.md | 128 ++++++++++ 5 files changed, 388 insertions(+), 445 deletions(-) create mode 100644 docs/assets/readme/rssift-reader-v0-1.png create mode 100644 docs/en/CONTRIBUTING.md create mode 100644 docs/zh-Hans/CONTRIBUTING.md diff --git a/README.md b/README.md index ade776b..99ebf27 100644 --- a/README.md +++ b/README.md @@ -4,264 +4,98 @@ English · 中文

-RSSift is a Turborepo-based monorepo for an AI-assisted RSS filtering tool. -It is intentionally scoped around a feed-triage workflow: pull feeds, generate -summaries, present a two-column desktop view, and jump to the original article -when needed. +[![AI-DECLARATION: copilot](https://img.shields.io/badge/䷼%20AI--DECLARATION-copilot-fee2e2?labelColor=fee2e2)](https://ai-declaration.md) -## Current status +RSSift is a minimalist RSS summary reader. -- The project is in agile, iterative development. -- The first end-to-end slice is implemented as the current working slice. -- `apps/api` now ingests feeds into PostgreSQL on boot and serves persisted - article list and detail endpoints. -- `apps/api` now also attempts best-effort article body extraction during - ingestion and stores markdown internally for later summarization work. -- `apps/web` renders the reader UI and consumes the API over HTTP. -- Shared UI primitives live in `packages/ui`. +Focuses on optimizing the "Open -> Scan Summary/Key Points -> Deep Read (on demand)" reading workflow. -## Project direction +By leveraging LLMs to rapidly extract the essence of content, it allows users to filter information efficiently and quickly decide whether to invest time in reading the full text. -- This project is a filter-first RSS workflow, not a full-featured reader. -- The current focus is article triage, summary generation, and jump-out to the - original source. -- The current implementation is being improved through agile, iterative - delivery. +RSSift works as follows: -## Requirements +- Precompute translated titles and layered summaries with an LLM +- Show a quick gist first, then ordered key points for deeper scanning +- Jump to the original article only when you actually want the full text -- Node.js 24.14.1 or later -- pnpm 10.33.0 or later +v0.1 delivers the complete core workflow. v0.2 plans to refine UI/UX interactions. -## Run locally +![screenshot](./docs/assets/readme/rssift-reader-v0-1.png) -1. Create the app-local environment files first: - -```bash -cp apps/api/.env.example apps/api/.env.local -cp apps/web/.env.example apps/web/.env.local -cp apps/api/feeds.opml.example apps/api/feeds.opml -``` - -2. Review and edit the local environment values before booting anything: - -- `apps/api/.env.local` - - `DATABASE_URL` is the development database used by `db:deploy`, - `db:reset`, `db:seed`, and normal API runtime reads/writes. - - `TEST_DATABASE_URL` is reserved for automated test flows only. - - `FEED_OPML_PATH` defaults to `./feeds.opml`. - - `INGEST_ON_BOOT=true` enables startup-time ingestion for local API runs. -- `apps/web/.env.local` - - `API_BASE_URL` should point to the local API, usually - `http://127.0.0.1:3000`. - -3. Install dependencies: - -```bash -pnpm install -``` - -4. Verify the local PostgreSQL 18 prerequisites with `psql-18`: - -```bash -psql-18 postgresql://rssift:rssift@127.0.0.1:5432/rssift -c 'select current_database();' -psql-18 postgresql://rssift:rssift@127.0.0.1:5432/rssift_test -c 'select current_database();' -``` - -Expected local databases: - -- `DATABASE_URL=postgresql://rssift:rssift@127.0.0.1:5432/rssift` -- `TEST_DATABASE_URL=postgresql://rssift:rssift@127.0.0.1:5432/rssift_test` - -5. Apply the checked-in Prisma migrations to the development database: - -```bash -pnpm --filter api db:deploy -``` - -6. Optionally load the deterministic development seed data: - -```bash -pnpm --filter api db:seed -``` - -7. Start the API in one terminal: - -```bash -pnpm --filter api dev -``` - -8. Start the web app in another terminal: - -```bash -pnpm --filter web dev -``` - -The default local URLs are: - -- API: `http://127.0.0.1:3000` -- Web: `http://127.0.0.1:3001` - -The repo-root `.env` and `compose.yaml` are not part of the local app-dev path. -They are the operator-facing deployment entry for self-hosted Docker Compose. - -## Self-host on a VPS - -Repo-root `compose.yaml`, repo-root `.env`, and repo-root `Caddyfile` are the -production deployment surface. This is intentionally a root-level entrypoint so -an operator can clone the repo and start the whole stack from one obvious place -without dropping into a secondary `deploy/` directory. - -### Prerequisites - -- Docker Engine with the Compose plugin installed on the VPS -- A host-level OPML file path that the operator owns and backs up -- Inbound network access for the published Caddy HTTP port - -### Prepare the operator inputs - -1. Copy the root deployment env file: +## Deployment ```bash +git clone https://github.com/sommio/RSSift +cd RSSift cp .env.example .env ``` -2. Edit the root `.env`: - -- `HTTP_PORT` controls the host port published by Caddy's HTTP listener. -- `POSTGRES_*` owns the Compose-managed PostgreSQL credentials and database. -- `FEED_OPML_HOST_PATH` must point to an existing host file path. Compose binds - it into the API container as a read-only file. -- `INGEST_ON_BOOT` and the `LLM_*` values remain explicit operator-owned - runtime inputs for the API service. - -### Build and start the stack +### Configuration + +Edit `.env` with your values: + +- `HTTP_PORT` + - Purpose: Host port exposed by Caddy + - Example: `80` +- `POSTGRES_DB` + - Purpose: PostgreSQL database name used by the stack + - Example: `rssift` +- `POSTGRES_USER` + - Purpose: PostgreSQL username used by the stack + - Example: `rssift` +- `POSTGRES_PASSWORD` + - Purpose: PostgreSQL password for that user + - Example: `replace-with-a-long-random-password` +- `FEED_OPML_HOST_PATH` + - Purpose: Absolute host path to the operator-owned OPML file mounted into the API container + - Example: `/srv/rssift/feeds.opml` +- `INGEST_ON_BOOT` + - Purpose: Whether the API should ingest feeds automatically on container start + - Example: `true` +- `FEED_AUTO_REFRESH_INTERVAL_HOURS` + - Purpose: Minimum interval between automatic refresh waves after wake/resume checks + - Example: `6` +- `FEED_MAX_ARTICLES_PER_FEED` + - Purpose: Number of newest articles to persist per feed during each ingestion run + - Example: `10` +- `LLM_API_KEY` + - Purpose: API key for the OpenAI-compatible LLM gateway + - Example: `sk-...` +- `LLM_BASE_URL` + - Purpose: Base URL for the OpenAI-compatible LLM gateway + - Example: `https://api.openai.com/v1` +- `LLM_MODEL` + - Purpose: Model name used for translated titles and summaries + - Example: `gpt-4.1-mini` +- `LLM_SUMMARY_CONCURRENCY` + - Purpose: Number of concurrent summary jobs the API may run + - Example: `2` +- `LLM_SUMMARY_LANGUAGE` + - Purpose: Target language for translated titles and summaries + - Example: `zh-CN` +- `LLM_TIMEOUT_MS` + - Purpose: Optional request timeout for LLM calls in milliseconds; leave empty to use the SDK default + - Example: `30000` + +### Start ```bash docker compose up -d --build ``` -This root stack starts: - -- `postgres` on the Compose network only -- `api-migrate` as the one-shot Prisma migration gate -- `api` as the single-process Nest runtime -- `web` as the Next.js runtime with `API_BASE_URL=http://api:3000` -- `caddy` as the only public entrypoint - -### Validate the deployment +### Verify ```bash docker compose ps docker compose logs --tail=100 api-migrate api web caddy curl http://127.0.0.1:${HTTP_PORT:-80}/ curl http://127.0.0.1:${HTTP_PORT:-80}/api/health -docker compose exec api node -e "fetch('http://127.0.0.1:3000/health/ready').then((response) => response.text().then((body) => { console.log(response.status, body); process.exit(response.ok ? 0 : 1); })).catch((error) => { console.error(error); process.exit(1); })" ``` -Healthy expectations: - -- `api-migrate` exits successfully once -- `api` reports `200` on `/health/live` and `/health/ready` -- `web` reports `200` on `/api/health` -- external traffic reaches `caddy`, which reverse-proxies only to `web` - -Operational rules for this first deployment path: - -- PostgreSQL stays private to the Compose network by default -- only Caddy publishes host ports -- the API remains a single-process deployment target -- `feeds.opml` stays operator-owned on the host and is never baked into images -- root `.env` is for deployment only; app-local `.env.local` files remain the - development entry for `apps/api` and `apps/web` - -## Local quality checks - -After `pnpm install`, Husky installs the repository's local Git hooks automatically. - -- `pre-commit` runs `pnpm lint:staged`: staged JS/TS files are formatted with Prettier and linted with ESLint, while staged CSS/HTML/JSON/Markdown files are formatted with Prettier only. -- `pre-push` runs `pnpm typecheck`, which reuses the existing workspace typecheck gate. -- Local hooks do not run `pnpm test:e2e`. -- `pre-push` can be slower than `pre-commit` because the current Turborepo task graph may run upstream `build` prerequisites before `typecheck`. - -If a hook fails, fix the reported issues and rerun the same Git command. You can also run the checks manually: - -```bash -pnpm lint:staged -pnpm typecheck -``` +## Contributing -## Useful commands - -```bash -pnpm build -pnpm lint -pnpm typecheck -pnpm test -pnpm test:e2e -``` - -Development-database Prisma helpers live in `apps/api`: - -```bash -pnpm --filter api db:generate -pnpm --filter api db:migrate -pnpm --filter api db:deploy -pnpm --filter api db:reset -pnpm --filter api db:seed -``` - -These commands target the development database behind `DATABASE_URL`. -Use `db:deploy` to apply checked-in migrations, `db:migrate` only when you are -authoring a new migration, `db:reset` to rebuild the development database from -migrations, and `db:seed` to load the deterministic sample data set. Automated -tests handle the test database internally through `TEST_DATABASE_URL`; those -test-database operations are intentionally not exposed as developer-facing -commands. - -Article body storage is now part of the normal ingestion lifecycle rather than a -manual script. If one persisted article needs a repair pass, use the narrow API -endpoint: - -```bash -curl -X POST http://127.0.0.1:3000/article-content//retry -``` - -That endpoint is a secondary repair path only. The public article read APIs -still do not expose stored markdown in this slice. - -`pnpm dev` at the repo root no longer applies Prisma migrations implicitly. Run -an explicit API migration command before starting the dev servers whenever your -local schema is behind: - -```bash -pnpm --filter api db:deploy -pnpm dev -``` +This repository uses an agent-first development workflow based on skills: -## PR Quality Workflow +- `ce:brainstorm -> ce:plan -> ce:work -> ce:review -> ce:compound`. -- GitHub Actions runs a PR-only quality workflow on `pull_request`. -- Configure branch protection against these stable job names: - - `pr-quality / format` - - `pr-quality / static` - - `pr-quality / test` - - `pr-quality / e2e` -- Docs-only pull requests still report those four jobs, but they complete as - explicit no-op successes so required checks never stay pending. -- Code pull requests always run full `pnpm format:check` and full - `pnpm test:e2e`. -- App-local pull requests can use `turbo run lint --affected`, - `turbo run typecheck --affected`, and `turbo run test --affected` for the - static and unit/integration gates. Any shared package, root config, workflow, - or lockfile change falls back to full-repo execution. -- Database-backed `pr-quality / test` and `pr-quality / e2e` provision their - own PostgreSQL 18 service containers inside the workflow and create temporary - CI databases before running validation. -- To enable Turbo remote cache on trusted same-repository pull requests, set - GitHub Actions secrets `TURBO_TOKEN` and `TURBO_TEAM`. Forks and automated - pull requests without those secrets intentionally run uncached instead of - weakening the quality gates. -- No additional operational monitoring required for this workflow rollout - because it only adds pull request validation and does not change runtime - production behavior. +Contributor docs: [`docs/en/CONTRIBUTING.md`](./docs/en/CONTRIBUTING.md) diff --git a/README.zh-Hans.md b/README.zh-Hans.md index acf5242..84d83a9 100644 --- a/README.zh-Hans.md +++ b/README.zh-Hans.md @@ -4,245 +4,98 @@ English · 中文

-RSSift 是一个基于 Turborepo 的 AI 辅助 RSS 筛选工具单仓库。 -它的目标是拉取 feed、生成摘要、提供双栏桌面视图,并在需要时跳转原文。 +[![AI-DECLARATION: copilot](https://img.shields.io/badge/䷼%20AI--DECLARATION-copilot-fee2e2?labelColor=fee2e2)](https://ai-declaration.md) -## 当前状态 +RSSift 是一款极简的 RSS 摘要阅读器。 -- 项目目前处于敏捷、迭代式开发中。 -- 已完成第一条端到端切片,作为当前可工作的切片。 -- `apps/api` 现在会在启动时把 feed 入库到 PostgreSQL,并提供持久化的文章列表与详情接口。 -- `apps/api` 现在也会在 ingestion 期间以 best-effort 方式尝试抽取并落库文章正文 Markdown,为后续摘要能力准备输入层。 -- `apps/web` 渲染阅读器界面,并通过 HTTP 消费 API。 -- 可复用的 UI 基础组件位于 `packages/ui`。 +专注于优化「打开 -> 扫视摘要与关键点 -> 按需精读」的阅读流程。 -## 项目方向 +通过 LLM 快速提取文章精髓,高效过滤信息,快速判断是否值得投入时间精读原文。 -- 这个项目是一个以筛选为核心的 RSS 工作流,不是一个完整的阅读器。 -- 当前重点是文章筛选、摘要生成,以及跳转到原文。 -- 当前实现会通过敏捷、迭代式交付持续演进。 +RSSift 通过以下方式工作: -## 环境要求 +- 用 LLM 预生成翻译标题和分层摘要 +- 先呈现一段速览,再给出有序的关键点,方便进一步判断 +- 真正想细读时,再跳转到原文继续 -- Node.js 24.14.1 或更高版本 -- pnpm 10.33.0 或更高版本 +v0.1 已实现基础流程,v0.2 计划进一步优化 UI/UX 交互。 -## 本地运行 +![截图](./docs/assets/readme/rssift-reader-v0-1.png) -1. 先创建 app 级别的本地环境文件: - -```bash -cp apps/api/.env.example apps/api/.env.local -cp apps/web/.env.example apps/web/.env.local -cp apps/api/feeds.opml.example apps/api/feeds.opml -``` - -2. 启动任何服务前,先检查并按需修改本地环境变量: - -- `apps/api/.env.local` - - `DATABASE_URL` 对应开发数据库,`db:deploy`、`db:reset`、`db:seed` - 以及 API 本地运行都使用它。 - - `TEST_DATABASE_URL` 仅供自动化测试流程使用。 - - `FEED_OPML_PATH` 默认指向 `./feeds.opml`。 - - `INGEST_ON_BOOT=true` 表示本地 API 启动时会执行 feed 入库。 -- `apps/web/.env.local` - - `API_BASE_URL` 应该指向本地 API,通常是 - `http://127.0.0.1:3000`。 - -3. 安装依赖: - -```bash -pnpm install -``` - -4. 用 `psql-18` 检查本地 PostgreSQL 18 前置条件: - -```bash -psql-18 postgresql://rssift:rssift@127.0.0.1:5432/rssift -c 'select current_database();' -psql-18 postgresql://rssift:rssift@127.0.0.1:5432/rssift_test -c 'select current_database();' -``` - -本地默认数据库: - -- `DATABASE_URL=postgresql://rssift:rssift@127.0.0.1:5432/rssift` -- `TEST_DATABASE_URL=postgresql://rssift:rssift@127.0.0.1:5432/rssift_test` - -5. 启动 API 前,先把仓库里已提交的 Prisma 迁移应用到开发数据库: - -```bash -pnpm --filter api db:deploy -``` - -6. 如需开发示例数据,可再导入一份固定 seed: - -```bash -pnpm --filter api db:seed -``` - -7. 在一个终端启动 API: - -```bash -pnpm --filter api dev -``` - -8. 在另一个终端启动 Web 应用: - -```bash -pnpm --filter web dev -``` - -本地默认地址如下: - -- API: `http://127.0.0.1:3000` -- Web: `http://127.0.0.1:3001` - -仓库根目录的 `.env` 与 `compose.yaml` 不属于本地 app 开发路径。 -它们是面向运维者的 Docker Compose 自托管部署入口。 - -## VPS 自托管部署 - -仓库根目录的 `compose.yaml`、根 `.env` 与根 `Caddyfile` 就是生产部署面。 -这次刻意把入口放在 repo 根,而不是 `deploy/` 子目录里,目的是让运维者在 -clone 仓库后,一眼就能看到入口,并直接从根目录拉起整栈。 - -### 前置条件 - -- VPS 已安装 Docker Engine 与 Compose plugin -- 运维者自己拥有并备份的宿主机 OPML 文件路径 -- 宿主机已放通 Caddy 对外发布的 HTTP 端口 - -### 准备运维输入 - -1. 先复制根部署环境文件: +## 部署 ```bash +git clone https://github.com/sommio/RSSift +cd RSSift cp .env.example .env ``` -2. 编辑根 `.env`: - -- `HTTP_PORT` 控制 Caddy 的 HTTP 监听对宿主机发布的端口 -- `POSTGRES_*` 拥有 Compose 管理的 PostgreSQL 凭据与数据库名 -- `FEED_OPML_HOST_PATH` 必须指向一个已存在的宿主机文件路径;Compose 会把它 - 以只读 bind mount 的方式挂进 API 容器 -- `INGEST_ON_BOOT` 与 `LLM_*` 仍然是显式的 operator-owned API 运行时输入 - -### 构建并启动整栈 +### 配置 + +编辑 `.env` 文件,填入你的配置: + +- `HTTP_PORT` + - 作用:Caddy 对宿主机暴露的端口 + - 示例:`80` +- `POSTGRES_DB` + - 作用:整栈使用的 PostgreSQL 数据库名 + - 示例:`rssift` +- `POSTGRES_USER` + - 作用:整栈使用的 PostgreSQL 用户名 + - 示例:`rssift` +- `POSTGRES_PASSWORD` + - 作用:上述数据库用户的密码 + - 示例:`replace-with-a-long-random-password` +- `FEED_OPML_HOST_PATH` + - 作用:宿主机上 OPML 文件的绝对路径,会只读挂载进 API 容器 + - 示例:`/srv/rssift/feeds.opml` +- `INGEST_ON_BOOT` + - 作用:容器启动时是否自动执行 feed 拉取 + - 示例:`true` +- `FEED_AUTO_REFRESH_INTERVAL_HOURS` + - 作用:自动刷新检查之间的最小间隔小时数 + - 示例:`6` +- `FEED_MAX_ARTICLES_PER_FEED` + - 作用:每次拉取时每个 feed 最多保留的最新文章数 + - 示例:`10` +- `LLM_API_KEY` + - 作用:OpenAI 兼容网关的 API Key + - 示例:`sk-...` +- `LLM_BASE_URL` + - 作用:OpenAI 兼容网关的基础地址 + - 示例:`https://api.openai.com/v1` +- `LLM_MODEL` + - 作用:生成翻译标题和摘要时使用的模型名 + - 示例:`gpt-4.1-mini` +- `LLM_SUMMARY_CONCURRENCY` + - 作用:API 同时运行的摘要任务数 + - 示例:`2` +- `LLM_SUMMARY_LANGUAGE` + - 作用:翻译标题和摘要的目标语言 + - 示例:`zh-CN` +- `LLM_TIMEOUT_MS` + - 作用:可选的 LLM 请求超时时间,单位毫秒;留空则使用 SDK 默认值 + - 示例:`30000` + +### 启动 ```bash docker compose up -d --build ``` -这个根栈会启动: - -- 仅在 Compose 内网可达的 `postgres` -- 负责一次性 Prisma 迁移门禁的 `api-migrate` -- 单进程运行的 Nest 服务 `api` -- 通过 `API_BASE_URL=http://api:3000` 访问 API 的 Next.js 服务 `web` -- 作为唯一公网入口的 `caddy` - -### 部署后验证 +### 验证 ```bash docker compose ps docker compose logs --tail=100 api-migrate api web caddy curl http://127.0.0.1:${HTTP_PORT:-80}/ curl http://127.0.0.1:${HTTP_PORT:-80}/api/health -docker compose exec api node -e "fetch('http://127.0.0.1:3000/health/ready').then((response) => response.text().then((body) => { console.log(response.status, body); process.exit(response.ok ? 0 : 1); })).catch((error) => { console.error(error); process.exit(1); })" ``` -健康预期: +## 贡献 -- `api-migrate` 一次性成功退出 -- `api` 的 `/health/live` 与 `/health/ready` 返回 `200` -- `web` 的 `/api/health` 返回 `200` -- 外部流量先到 `caddy`,再只反代到 `web` - -这条首版部署路径的显式运维规则: - -- PostgreSQL 默认保持 Compose 内网私有 -- 只有 Caddy 会向宿主机发布端口 -- API 仍然是单进程部署目标 -- `feeds.opml` 始终由运维者在宿主机持有,不会被烘焙进镜像 -- 根 `.env` 只服务部署;`apps/api` 与 `apps/web` 各自的 `.env.local` - 继续是本地开发入口 - -## 本地质量检查 - -执行 `pnpm install` 后,Husky 会自动安装仓库内的本地 Git hooks。 - -- `pre-commit` 会运行 `pnpm lint:staged`:已暂存的 JS/TS 文件会先经过 Prettier 再执行 ESLint,已暂存的 CSS/HTML/JSON/Markdown 文件只会执行 Prettier。 -- `pre-push` 会运行 `pnpm typecheck`,直接复用现有的 workspace typecheck 门禁。 -- 本地 hooks 不会运行 `pnpm test:e2e`。 -- `pre-push` 可能比 `pre-commit` 更慢,因为当前的 Turborepo 任务图可能会在 `typecheck` 前触发上游 `build` 前置步骤。 - -如果 hook 失败,请先修复报错,再重新执行同一条 Git 命令。你也可以手动运行这些检查: - -```bash -pnpm lint:staged -pnpm typecheck -``` +本仓库使用基于 skills 的 agent-first 开发工作流: -## 常用命令 - -```bash -pnpm build -pnpm lint -pnpm typecheck -pnpm test -pnpm test:e2e -``` - -`apps/api` 里的开发数据库 Prisma 命令: - -```bash -pnpm --filter api db:generate -pnpm --filter api db:migrate -pnpm --filter api db:deploy -pnpm --filter api db:reset -pnpm --filter api db:seed -``` - -这些命令都作用于 `DATABASE_URL` 指向的开发数据库。拉取仓库后做 schema -对齐,优先使用 `db:deploy`;只有在你确实要创建或迭代新的本地迁移时,才使 -用 `db:migrate`。如果要重建开发数据库,请使用 `db:reset`;如果要导入固定 -的开发示例数据,请使用 `db:seed`。测试数据库由测试程序通过 -`TEST_DATABASE_URL` 在内部处理,不再作为开发者操作命令暴露。 - -文章正文落库现在属于正常 ingestion 生命周期,而不是手工脚本。如果某一篇已 -持久化文章需要补救性重跑,可以调用这个狭窄接口: - -```bash -curl -X POST http://127.0.0.1:3000/article-content//retry -``` - -这个接口只是 repair path,不是默认工作流。本切片里公开的文章读取 API 仍 -然不会暴露已存储的 Markdown。 - -仓库根目录的 `pnpm dev` 不会再隐式执行 Prisma 迁移。只要本地 schema -落后于迁移历史,请先显式运行 API 迁移命令,再启动开发服务: - -```bash -pnpm --filter api db:deploy -pnpm dev -``` +- `ce:brainstorm -> ce:plan -> ce:work -> ce:review -> ce:compound`。 -## PR 质量门禁工作流 - -- GitHub Actions 会在 `pull_request` 上运行一条仅面向 PR 的质量工作流。 -- 请把 branch protection 配置到以下稳定 job 名称上: - - `pr-quality / format` - - `pr-quality / static` - - `pr-quality / test` - - `pr-quality / e2e` -- 仅文档 PR 仍然会上报这四个 job,但会以显式 no-op success 收敛,避免 required checks 长时间停留在 pending。 -- 代码 PR 会始终运行全量 `pnpm format:check`,并把 `pnpm test:e2e` 作为终局门禁。 -- 仅限 `apps/` 局部改动的 PR,会在 static 与单元/集成测试门禁中使用 - `turbo run lint --affected`、`turbo run typecheck --affected` 与 - `turbo run test --affected`。只要触及共享 package、根级配置、workflow - 或 lockfile,就会保守回退到全仓执行。 -- 数据库相关的 `pr-quality / test` 与 `pr-quality / e2e` 会在 workflow 内自行拉起 PostgreSQL 18 service container,并在校验前创建临时 CI 数据库。 -- 如果要在可信的同仓库 PR 上启用 Turbo remote cache,请在 GitHub Actions - secrets 中配置 `TURBO_TOKEN` 与 `TURBO_TEAM`。fork PR 和没有这些 secrets - 的自动化 PR 会有意以 uncached 方式运行,而不会削弱质量门禁。 -- 这次工作流 rollout 不需要额外的生产监控,因为它只新增 PR 校验,不改变线上运行时行为。 +贡献者文档:[`docs/zh-Hans/CONTRIBUTING.md`](./docs/zh-Hans/CONTRIBUTING.md) diff --git a/docs/assets/readme/rssift-reader-v0-1.png b/docs/assets/readme/rssift-reader-v0-1.png new file mode 100644 index 0000000000000000000000000000000000000000..02e281b930b50eb1046f25bdde7baa1483dfd32d GIT binary patch literal 199098 zcmZ^~bx>T*);)X(1h=5U-Q9z`yL)ig;O@cQNpK17?m>dP2MHeB-M{nP``owc`{P$k z4O26-`*iQ#vexP|5lRY@NbtDu001CKONprf0Msl1Kx)9g1K-i5(SHO0L~$!oQ6(!A z69ABkNYRARQq{y0a5oi!MGX&#u1Oz-%lBOKacI5ab#KtIz2LK*vq`(9m|!;*Z4sztgEbe>6hJie}WVSW3N zA&@`3XAx_t&KN9#j4@i&`po@i!Ts92yGt9oR}(7FFN=j+K8CY!@?|30kwPQvK8u{6 z72enmP|~Bvzt%K1PbFm+-i=#^-Oe4tgClj5K`E`qjcA5&9z%P;G?pX(l5Oh%TiW#!HbP#K&?^o%}U{uk=^hMdHi%45P ztp-}cXCNHVLxt56g3#qjho}o|=`7BJ`CRH11VJ<;M2QL!Mq=(EHe(nEMYt_Qa!)|| zAwDh|;o~u8;JYDz++VcZ7qG=VJOR;p0!1>&9`C*o!oswk>#&rN<3tdD;0ukuC;D(< zg#HuX$w>9~&D$^irPq-dou!{T)WUQ+j6XuSU&xr6CEYR#Z=&1*?fH;S2g)rB@#%OeqtM1Nltop-NlD-kzWGwX<_ti14e^jKmnPgKg~ zUTAUsyjoqx6tZ_doxS9~L~@MphSnayVS*I!wb3v}3(rk1LQ-ROy9bktPxsi6k6X#& z`rCt(MM_FC?>vtyudmw8Zj|{R9g)N-;sJifCpNmAUx5aQAEKx}e2O*U=H+gO%zb_> z`Rq__0^LmqTYrMkyu$_f=s-h5$NFvd>HzoCkX>l8KAW~n=d=0HKOe~6eXS7swK)O_ z(Z#)gelQC&@DqROr%z@e?>Y1FX6H7ZJC5Bo=7EWJHeW`r@wC}9@r_vJsmlpvad z2y$_cAz_3eEupD;v>iX_K`-_Yw0O1sM3_SO6W|9WECq{XMEn>IXB@FaHUW!wAs!SD zOO7QiF%vH*7DOHIIfPb-p&hJ7ypn*sBe?_Xgw`71Lfo7HBgJ(|bqVoH$yE)8L!@%* zf*Nl(chi!O2XkD^BLC~YipzVQSdBdIX|HQ?uMeHEZuv}80*HpzAJN%<;WKj%{K~Jv zpodH{La0HjKrHV)tfBPCsW#5iLb)ZR+Ct3y%KH-&Kay(WoeyOfU=XXhG4H|ehua+~ z_|yAh5(>8`-ca$J^c*$`E+a%c#8?E!2z!%x6VWHN4b~xCj5Nto{yJdNn3d)Gr?i&L zmW-AhkDw)CAF*Sy1gS@I??e=u$bPEbq)2HzX>VzULb^i68QwbF-yz3x$dtwL6g#9& zIG!jEpB~^JXj*gfrE3d4l=G=YM({^uMigYwchQQXW)$c1byR&zF6L|xjjg#GW4%JX z(jIl><;Ug5RT(7YE1IM#^-cnoJsV-H&*mHFPv%+=OAq01Fb~`36-(AN{N)R#D=c3d zFmwd$-dBdYMY`qODg@_kmh{Y`TGP*!%#|#*@#TA})+TME%P=gS_v8(B058- zkr>h&9h+tvxg4OnR=9fb{N^d*d2(5H^>S@=)ptdAwK&o^B%D1lHI}2JjLR6+8|EA8 z=y_%yutc~m^HZEFo{4eMje^gR_rw%xz^ zoy)ja`1*XZJj*?+zefBbkMhK7#Ol^jq5qw#or*_4som9B*CbQ-xKP$}n9-QcG1)fN zw(wYmG(IqC{zE=xoI^LCVTE(ht^UM}cWz>wakOgYG?Cx>%$})f-FBTNmU+TC(^PN1 z*Fr6@%Sh+#iLDE6!!L)6Lf#PTb{@2y}NdZRL(q0k=YQz ztdq?56DbpmdX|hmj0pO;`kz~u+avXwTmCqiw8%R>EmY2*B(;aNk9AITuJrYijhiG5 z$~PhNe(TKi{gbqTv(Y+L|NE*ltzENgQMg*LgP_B?KpPUnc ztS_I10iEWaOqKHaq4VXuE*Mc(vZ6}g)-Z6`iYi~_}g_Rr0LW<91;pVJ5-dS>z( z@)~|AUTTsO(tW=gZAWuQJ4c6b-J#WZ^t_O%^QrkfE>8(Qi0aTqyuTkf1&>`?ygc+g z#P9oH+hMiEX31StmI@n_P9=0CWJvMyg)o?`?dCcykM=XBB~8Lvrdd*06pg9+C!+YV z2gAP;HdCF-`v^Uc-1EY5!bOHGh7>`_#(vH90Z8 zj;_c-W-9qcwM}JBB}&ynt}&a(`|zt3^sG}c!FPq6i5x%bnE2G>CaM!M!AOH&XR>>s z!5OCGdpsznkk?+z+gPjU*A6qc8BZOv|8Bx%@jJ8lYtL7~v0r=ef14)QvYI{q1V3ax ztY8x(%wRV#vKzFx7%k?sqCmV{MfIike2H%S|+%1VPl9nHGa>YLd`18Qf6mA1e(;o7`v<41RBQ7ulCjFU(XDHAOV9Efdytc8!#k*}{C0?1`n~MR(7B z3VabAXAIUaY8N<3JwC07trXDTR#vSabetT~Y0yw!>Ev=f+w~26!ac%2Y&x?zW#R{r zsge2KSWX75oWDgS_xo#dx3S=LPG;#A|9_ywj^mS)^Vyz=)<{|-x5&g!0Je)Bn8K1iQEvFM%56vXqno#UQp zO}!G>`_AN{|LKzLX)iSD6#Hmpbs70>>vnY0?e6wbmM{&M$@{bYVfxaS=g9SxBu&Wb z?9PF9w+pYGn^-(JF8ejP&flK09!I}VR#&S1815V2Ci2@u+9~eX&r;U7It*^UpEx}6 zuJOk4!}?6!GM{c{Nd`$W5sni1AL~4Z+!h{}4$pqGn3(+59^B3(m~+{2^z~ZYU-9Bh zr`y5H>guvs@cGU1C~I})M&{OfV{*6p#^CIA^rm{DYrpHs;quRQH-l&KTh$}~(@QIg zG*R~Lg?Y6q&v(dG<2a4j{mtP069`A4-NRALIlBrr&?t#dv8kLiT>?#}1|kh~BzL66sJK)w~w@BMD>msugw*)H^5mJC^M zXHV0R;(_PE#(KK7;-mU%!K4t_pd@nM-p3OMzth&}-T`?9v5Az5JOFr60zg0r0Dv3= z2-pVzS7rb>Gz0*ibO6A1%xqKU13!Q^mX#C(-v0T^?JQ0L-+^(FmKTRPgu+AtP~VFC z9RUDY)k{swMa9US*wM+s+{)IB*u~S)jQBs4#WJum!^&xgr|RpQ4Q3})0+g^5cP8q^ zZFk@+F^!!?>XF-Ocp76WB{FenFyz5~H+S6Id6Mexrx%N-Ii(4wLq3xh`-2(%!hZZB zsc4I;)?LEM`r`?cGjjYHPs-&CWTbC0w8bGle^viJJPTC69avO&@zoo2r))hY9vi&I z`Y-ZBVUAN+wJ}{e8*JxoHM_2%gz8D+Bq8Ncq(144vJwAKnOc2lTi?+ZLZ0(1z3`hl zIton9xf^_Ldt30&5~M<-caAE1`ka@IP8Nbjq}yk`Vq0DI^vLR+SjU@pwMf7ot)M`P0c^yx`i-Vkf=j@?@9Ch!`fLgjqH=(In(&t z-TK>`SczNeb8E`IQ*LRaSSk0Pw9N(WrF>C&iP-80dqFsjTH|DfxVm0`=4|`lY100y zC?oGO-d|ociA2wfYRzmrT1-9peWUjKOMcSnAN!Fae8@xIMHWLn!IE^^C#U2wiSm(S zy8cR|Hcx8w^4q>!#Zoz*zLUZky8M;`QupK1rLmt&8ae(Mq}$EU`@AR%geI2$A?@B3 zWR>udnHkeA#pEqUeL8`cx`oPeauLghin^Sqr`GQ2H6izHlYBm3Qn59EISkne7C`{R zK^h%zq94WZ*}ie^l>aELn-Af-s{Hx9R#MU`+qSZ{(YHfTODMxZ{`2PR{<}fh^oJGL zyDQT@3g!E2vNYK8-AA)mha+Oj*Gt=z>DJ77suB8g#)ObVX4(rIo4XOT_Q5B24bL0w z#p&crOwtR_c;WMWtxXd%M+tj+-<3eMo3GM(m-ZZ1`n+xX)yL=iQlW2yt&myaFhm)w z>yo>IjeQ^1XM0w?W9#-_&i9CoK8UlstHB7}z%57;xew)(w~+ZEE#I~3menkMp5&)= z()2KxqKXuR_NMBDS$Y(2Bl0zPY0yu9q1i%#(|`B!@m{Kc+U}^n#bH*o|LS?3#9wxo z&i9anZK{5FzSvjbEq=dF#3Qz=jvwLo9aQ(7pdZn>%KC@U|l*-@fnt9o+T5BQw5EU^fVo)``15Y2iVr9S~qjdKY(>s`LZ+wAkdE>!2)< z^4c;c$h-5P7|8 z;>|SSH~N?yqVBLjbm(q3u_vOWA3Brl>9Br62n_=<(sY`8?9md%2X_mLFM>j6$UEB- zor0t1S@iAqI4px_@|4s(};x=b6$UKNIl%TXEUx%jI{eb*5 zN0#mQ{;{thBy* z1d>u*cn1Y9oJJL;f9SHeN3>lVAj{H>a32@0zB70QIZGJ3t00c!lsF6LoN}r-p?h~q zD19d5Z-)=gOfhT555`wzgq*|MsU{C-cweNcru-onIgX0bT5c*mmqpkjei1uMI9T|U z_cP9>1+yt`a0ltT<_Qja`sGZANzy8;f+VE56aTMGN<#S@-+FLZWA<#xa&rJ^VJFL^ ztvGKSzRLzh4v}pV)DNhbwi5E>ww|7!omi5l$XpYg2laI08(V4eFklWxkKj!E_yzE9egz`2(? zS#bvXhn9LD(0bVymD{xVc+Lgzmm(C-!khRB|GKqkZ>BEZ0GLLh>f@wv9>Y(d# zhBL)4?2Yu$IMc$BEKVs^=5Aj7>6*(MY(8PM)KYx+f&K@K-XgONZ^u7P8wPF#Fsaj7 zz*iqHdThP?8V?do%zj#8m|vG)urwHB!l>I&ZOoKq>aFugtEii{Br)Gg1GL1`FpMoK z^~mf_^iZm>e`>gM(@iV|SwzV}ADANZb_&?2RaQFv;g;OAbDAKHdCiJHu`MUlc4viu zA*XGLKyaa-n-({?I{q8_7ln~UDi*Q?CzpKR8~HJIbGq-tFOj zp0BQSx0E;Y zE>^wKQ%~5N96VYB5kocc+E^z^2fFOMJqn6Svd>x2;i-pKVXJyx)O}2eedsG9FP2!A+8chmZPBxT!O1rU65Z^7?O!%$C8#QZ-`Z#L_{?uRgEJSBUHCS zJrO<(xMi9$s;AyZHO|e>y`L7E02m~OOy0ZgPX9gQeCNv8c*_^0fxtuEfz4GJ_Kp(+ zE-;Zi89rv8cSAQ%3l0;%^j+_H#R8#qvl8~+LL8pclFIA{9mo;ru68V2xB(3B{gNMP z@9<9oY(ioM5_qH8dkwz_!wrT@ek_NPB-SGy8>@|Go}SEPlf&o`suahl-uk%k?gw(z zu-lYS6Ea@PF{R{^$J`%=kAG_#AXizmk_`KTP6(Ok#qXd9Ln4;a1E96SN`H2bWNUYZ zL4I&ERf}d;j9mXnSG;Dd7gab*_EX$xKK)xiyGaPryHqT3jbv7`Vdjy;FiFiZmkAj% zXI0BVThsG>3iiqwXgg6SNO>Lluhb?%b(FvD5|~fIF|o@Cgj0n&^Va1ew@hs4f+(R- z-u=L***0;2WaktjA4tGZv;Q)M68C8fYT4ZS3$0in+#apr-)Mx+iu^CNm&^AQpX<#e zpdfo?$xTV|phmnK2E+z5#)PWGz80f5qITM#!nV{I3aDW~OGXWwMSWATgkzWprneOm9oR#pMFV$DJho_V+?t*c$6m=CO zrR!rC#~(a&`$jmD)&oc2EnX0J&jvJ$oEYd6_3nveNo^ik+_(2{B8o7mlD5D?HA z-~{D~>S;)6rH*uQCezWSI)xQsSITvp9$U~d)g(r;UGo>|+BP&oA zBU$~BGtbCKH}P8N7Fl+Z{w02-&QyrIngO?y7MhZ!Pt=dL{ zVIWri%pWLHXTvnotQ6FcF@aq#y#9X3+6cj(5?0CL+}^;iCVf~oBcV{XYe94#Reb#+ znLVPq+4~U3XskoXbHSK9A{)f$88}o!wWtdLJT<8-WS)%{7^yU^$U7VreBpK9oQ(>S zEFm;g=E^$Fq#PvhT2ZAee>{o`) zw3v14p^1M%3*JOM@{E9R*9*suim_6J6GgR+)hCv$a9av~n>AHD^u~q83|<+tJ!o-i zo_=J4EOa&~2A=jFeY$fb_m7T#P9qC>-djkPtHkiog?$4LOL^158kO`7;2Um{R|wD+DUV)8DLDAV7Q&3Z7_n=&3`{C z;#3Hno1sbqHW?{@rN3H#MWt0R;DT+j;Dg@>f)kAKQYqe3Drc#vIjJEzLy%HQ!2sU@ zxFWQo!b$ZnH?-fiEaXnN0|;8($RhKsFZnqND3f&>A)wnih1xkx8};Evmo2I>J|`3D zgpKoH{!D)tUUrtS?)wn{P7OitO5`2x$G2_yVkxLPxr(VPpG zgjC$RqxJeTg9HwW^VSl%uoTG&aqX z*s^JNST(Dio*vuqfbY~VRHrG_c)w;PU;5cnJPz49kmS%v0cs(eH>yW{N@&pPylyR_ zOcPByYX(Bgi+yP9IIB=m;-FxsAZ|_SpkA?L1FZ#i*e8jX(`udIV?;+=@PN6NCec*< z2doE~p?maP#r>|cU5= zB3tTyvxUa}W~PHXjL`#U_zz-hXTE8afg ztt=+1O_-l>(D%fiy`T&FU;?e;y1Y*kDXZ9rS#aIMg|!^0tad+bk|dgz$l-e)obBFF zVf1YA4OKO4n~(>4Zx%g|kxOootb-UKeIYCHw%XIWm448!iZ&%Y*!C8%-Skgj@~-ynKGZ-7@_UI3`@?mi50;nEfQ9}o-W-XBpwCZcKs0-5uCXpc|N%x zEbeQbEHmJ8Fxos{!E)`Jx#vEuUMF7f8Wec)jAbh7)+g$A`DC#5!=d%n%l8?(ZqnaS z`u6-i$T%rn#I;;Zja11VL=>mxU z?-jAIqa#RzrR_}^>52cD?e=f)ub>UiKiB!k#?;K&#L3Fh#md1Rw6b+@B9;>s)B3u= z1yXT9T1;5YWBGW^RSUxg8#u2=kUgGRBOwkC$Sn!~8d$lswzihzdD7Ojh=tycDL(TP z87krQbeY2a?#aaQqZ_uCh%kTm;NG9C-@i+d{*FC$?M;39N};6rEb~@=4NDS&Dgr)D zoyAGK2v%0vBvka-FmH(c_x-%?zBtD?MY^04Ral+&p(GS}>W!U6W5$0hK(iNnOXGlt&0Mj~gH`ay4ctY_#3sue} z6T`Ms>nbZ_+~DrTJ)c0%EIUO(v5$s^hA0h(TUpjp=XOC= z`3+Ijcp#IHu)3wNqM@N7dNj#on3doI)sL8%+Tmr95aqU4g&|q$1DO;>X1{(>nCVpo@u_Mj zh!>j_M*PP;s3P55q(i17GR$UX;8<~kqr)a_;5Cbi3O|yHO36G~VD=Oj?qko-&1tLX zXsD^l6-=uo?|h7IBOem~*T-}H(+4g*SyeyshjuS6FMo@R|6*mGQ6J1vck4>Q_}ujP zLjfF^k^b+MM;R?VqIAT70Ls6nA$~k?Jo?(o*_Lngj5KD$GEs;Ax@OzOrbXh`ho5!z z^hOxz)il+UQ;peg1Ihloqo|KX`z~#fiF|3J;}hc+!y_`XQHY2S0N{h$c#-t^LQdX@ z>bt6%icVR1QKS*o|F*P|oSa-O88b>oW+^kjERA$)@)9-)**GN2M?kPTfQ(^$N=>sj zr19U0U8fbyT8>0sCL%_3yi=HOWTy*Kl=hRC&*CE#%o$f!PA<=*XzB zg2PN2YbYNTcI+{98jI|x`1lEYFUSx1ze$h?gIf;oH0I;R!H@&|EY?JKdnY8&t|YFE z4C6-n@AA+NB+s?A##yhevrU{M)9YrImjAa`CAgsd%uKX38nQZ$Cl+?vr3c@8?%x9f zQ2*MB$Y)hFxZtFLo}<0go=%{O8#KYjDl4UCzn%C$_wiHHjnpF`c=BbRtLBp_x*65B zmF0&(5&Rnvup}4%`9jp!y{K>oS((v$K0CRIc}@|XZ)y7 zGgKcLC0pW5E1nIeLG-8@GdZ5O;J--+`%*=|e?7y>$T4guzB3F%%}ArFp6GC#^>0p- zTzi8FgOQee;V+jene78?h)P?PlENT zTsZNqVA~ESjqc4tJlo7{wd2`GAg7v`>FsKU^5Wt79cOV%OZqS4k&$O*D%89I6)mm) zO-C@pwCO2hBy5h?ghB7Tk|JR`<*sW&{t3vHof?%HRb5KF_?R=gJ43O5qhZ<+H7a>DZ{|*^o4r?81=J`tnE+;);sO>{`xaxSq4aUKP*sxBl(4*Rx(S z<2Dy-hnX=Y!`kcXv1X#K!eWa==WjT84UNraBN}3%_KRnjn3yO;%LbogDLV2xo`ZDV z2@xi9KG~;-h>3^oBquj$ItcvT-9=A2JS>Yvwo^+^!QEjey~Z}K=dND&lao;(p)@l! z#a~{=`M95e0OgDDa7{6_T=rF;j?U?C@Ze-!y)*Q+^AEqLXM^sz@JI-N4SgFK`F7{I zmwv&;NAs*th-W3+{IcO3Fz`?PyvqJ(0;cP$GbzDM6F<@-w+pQ`r z3@IyPy@*RMsHl*VlaXc_nm_mONhp6K#1#(dqED@mW)me7^@SE?rR6!jO3T&3ABbx* zbW-#?RusRI6^2+@>*Hc#TAvbXPKhb1t26fUqHK;{IY0X-;*Z3XgDpKwQ${;dX{&R1 zV2urKG#2LKJ*@zR5m&_Bwk|#)j=sc$v4r^GLQ8MSeoN%dxkqnpM!?wE@pZHua8F43 z=pMI(je~<@$AE<7%E1|ij*s?t^I-6$b`Sco4SWFSf7hr4a!>#tfw%GYIKXgzCcmr( z9gzEu10iI(%O4%32n7(>$#W~aFN92@MGdQ|D^tD=56L7Upw^oC2}67zC;3bM+QK9x zKuxzzNm2YRAT?DYW)JJ1obyNF$$BNT-j+>`yM)@f@SH;JTow@_3y0z6_N1Y)Qr8dv zxPRnwJcR|w$jF=tUJeZpj}DE^%qc+v9O@~;FInJD?B#TOKmyRWN78my6%t-vf4?__ zb$6nvC`KKDCkq+CS1>YmDl1i*wT#iC*EUuZBduz}G#C)zm*6j*ZUn<% zU$UI8E}fmOJ|ar2|5=M*a?I>7!^u7c8L+j5MoL@$S;YyyxF(*44gnaZ zu_`Jmnr9-yz1?>i1Sk|3Ek!8#f)E8Fx1oVufFvSa^-U@iHg(8Nf%Le7b|k-xoRc#! zo&2-zxNKx(Bw4<(si|pryOoiV@$>2Q@Oo#YAAVgOEAojBVQiX97OZ0brFW zSL9|SBeOHjMFDER?DA%QPs_9Uc4<8FyoH7K;sN3l6QKb!GqVWkVR-na=tx%$%C^UGwK({XI2LO zD`z1kZ5%E}frxR&q+w%;6E!0%9Ap^Tc2x1pYl%C(`<)U+@v zGV)BoqpYat&uT+PZ7l>reIBuzrreK;*d`4Xr^xlFMQ}>ylmq5J#!GyUkA4a%Qql5o z3Xqh7-BLp08zNcxfwPURDJmub-UrOt-d+&yvGY*I_89IT9jM(Th7VZ+%v@|NQ;Xg= zkG^txK6tT6Q8ZibO#EbQ8%ND`toA;}Xy5x1(vnm)R4|rYWhk6+uE#S z1}&|PG0?Gza4>C5?BIaf+6!Q6wIU;3UmPIbuyOPdW9R<8@F(>#xT2sE6ZKh_V+F4_p<&ea#KVRh-`S2MW}&;+r9|MkOAfc(1@Xh z8Kobd!^OYu?(F!&!6H9meM?KLuBh0bD-sPphWtJb4d7TEcvY9`SzALtF!54_Jtj9a zsC5gh-MBOY#1s@S52MX-IUZ@7pAED?$&!Il=)Px)nPZn0;&w}Vz${bM|uyzC?Nd$Be;Y;(_vQy>gLgq-&WZMt zR?v^*omY@vYbF>oFaKWoy{z?XfWikEmX?^C$ZBX_$Wmw(A=2$<3WZt_+aND06-sB z>gd3}ogP@)G}bU*U3x+QcDl`I8C>BsbT%_%%;fC7y{iq6eob=(O_uhT|BSfgu_5Xw z92nY$%l%gsAFO6SZc|cHQ_-}#v#Z=(&BC$%yo5iZnx&!v8(@l{VMq(iA69}R=HNI&Z}*?@}vUTIC+}v zfIc`z5KsK%_$jI92gMWm!MMuj?m@h2mo&koYB?jBb zsG}hv;^Y*?)YL&!T0Co z_iF_ZNPy+Fj?3B`OC|>+EhQEprqNIa9ZVwqQb`6goCGc#*ce@O5EmB*`)GfSXa4ed zn4$qhZr(uhKX`awao#Y&!d$~m2i&}drh3x1gQFt=n8uVRmqlO$;Q%jhbJ>JdquKN- z6mWU<8-!w8PNTeC=4N{>-uI}U0k}%i(g*D*24HUt9|nsoV8~*I=SBznqTU4!y| zuP!S)3JB#gh9`osOKh-zcNZJgnY6%IMphOF5jr7K<712j8P95JYU-h-4a=|lM6_wj z>4S!EoHG^2nGM&7K#h%=_$%2!Mv`{dSMBugtRJI~k2AxE(nEP1oSf(h?8ZSnXWWjI zeJrUfiw=Z`8n6zL_bMCz;9OAs2k|O3;#N3gd}%eYHl7=4%LjynA$SD@XvoOu_{n7T z<;4mhf_p6OY;JB}(%gxdCH1+jx3*HCB-^Rja5?@ROFN zVQDJMOT;6&#(u=Ye+1*0$)KBqjT78pO4d8nc_pc`vJr59zFb7at@67zHpJp6?CsGFfjE;z45Dn|8so@i#=OiXh7)mwSPOr9lV{d-_ecap8 z;j=|FZKP;OHEa>#Z^}yxxEvc+mlvq3sXg{EqQd8y9zaG_witON5B9;5HS z)Kng;Y5IR!f2m>G}U)VK85C<3aLK$%_&aDn940-kk*#m%ZkD zE;jZxDM?i^yEQpJU2ihxN3liK@0#ty81ES0>-_V;*#AinB|W~+_#OlEG1S0?CI~aN zT5Q)6G4a3+>eqg~KZxSJ#TRhvZLD>F-Xk6KGzKF7@mvV%Bc$EEMe}+T7uT9XX?f{u z&^Y)!k;&OrQ6Z-)TH&<_(kOFNb0mV*>IB7E#eSGv=~s{cX%YUp(a{wk%VSKtv*DC1 zm|m#RB7VID$p|5TP3ISd@#?;}BPU?TT`g^$utD$S2FF)h}QU&%9+h$1!Sa zb9YU?Y;U2DkITbcpGYTf|A>rIbn!nVK*NFqu$k7Cl;Y23%kgTpj8#VZ1}8;&Nq4m9 z!J!!;y<}x&)f=xedwY5BR=eMaVPRr7GjloPEa{4Nb`bCG1?0M)38L)p;a@ZKuiTkY z4ct*nu=z$FnAX&dXeLEYw@NegJ zEwtwn5csn9hpd0wglZD!=TlM41}jZ{=87dp2%8quq>hKxXLs7kYvF~ z;s##hPwSrF`Tl|EQxn<>JmPbg^VtWG<~#iHeFymR69AuFu3ePBuzc`zYV9y9=Unr? z#{ksSlh62lyk2K+5@%*W#eoK0a{AypcOyQ_JC2%m2t8r%b}bD2QdDGA1Oa3KTa`Ui z$0^#)&Ofu=htmY6W@e_lla=%VCQh%2jSe}!S#2)iG4fh9E(B4(I$UlYrzCS~ex@P(Rfvg=(5-VK zOB=;ps!dExd@s9DR9N`pFrOdp@ESnNJP34R5?+@% zRNLEf?)^Op>#qmHQ1Lq-{Q1_G5 z)|Q5Y0}N}mz*itUzc0lG=)ZAw7MSn?hRh#jvBnx|o1XDeF$g~})W`;|i}aEew7Z{t z@*UBFgM-73Stz$yY-OXW{hev?*>A_VAQT0cuY^feBelm6wqkQ3OA@GYAB4_Nt1c_M z4D8g^qBp-OHh#@44Gr}IU717~4F%%!kH%e|&aGys3O`NxFqY#SyJA}N;OTv4f`*4Vlc&kWSqPG@Eo zln^^uP2-mh7_->fRfG?g(b57+DmpT9e$UUY8&eSkZO9*4-2V1kA-t^n00A>~bvgF~ z?5O`IIU>c4n?nXfN3(Mmw-ko>;A3IY)O18QG=zv1orhSKlS@g-z|!Y@*DQ^)a#LUg zsA*^xT1-G44v0_5Qa#xH%?;$1ln~d{M1uIv)LdQX6Bk#UPb4wLuK-%q)^Jv~`eANR z-4}O?lPrFVawx02?b4OY{+zx-BmA3gsq#kl{ZHqR#{ z#QR4iphfSG?AslXTTuBPV8@kL7w2XaNI?9V&39G0_g5jiGczqsk&jXZ6(S&}q)E~W z#|i+&H&V)#rQg6$oJG2wl8O6}!lPzFq1{yJceTB;_RXxUBL<)K^qg)x@PMV?IuZ3b zS-ed!0i7_fWTS0*9Zpdn_q27vShVFVc2_LMQ61#qvKuh|r-D^v7hk0Hl+nWG2}&AH zxr1?xiMjh&9ZAc%oIOay#2cfpdA0x(d*yMO8%QxX$79hV9(OhVsHtyZnaL@6FG7KhoDr}oOa zx!L;Py(W#Xyt*FFLPPe1e0voO~e*|Q^^(t3OtsU7n zcUN9hQD1$)NPm32B7SKb5@-L~I72B=4T2MK)D59|3E5yQYTko9zVzj0G=$ZKc}EDhym zbrLjAs%vZi-eVHpOqd467m!xERaTgqnGIQhZBx((9yTuguOs}tn)>ld11sws_8&1h zTOU)kF-3j`5Kr{a)qAIb1t6>2Cz$@}%jFW`V1zKUQyKYIJy2X=snVNfcr_{?IkQ{Lu&G}o-j<&KgAR_vvwI%{)VkH>Cb zZxnp@PLC6?=>fTA`5ms?GNhFWHJiFq&A~~79M4CP zB|#v8z;9-j?dl4Fo_mtT+rkN+z_razOj)JtJ&p&R!L1Kfw;kT zk6m*oCuEr#hwCGU)-d6Z`pc00bR%StiVMM02`g6Ccrsnrg_E5pf zezRAvqH!B%d3HK0QZlRp{%~_sN_cEe%olf!717=|18X56EEc?hE!yt=>;!{$+%oabKh#)4|~Y}!wYY(K1V`Qa`eK}D2e1u%>Lfy@-eg%p&O zM~Bw_uV_otwUuh}{(3faJd~#s{O;o7?`ddg0*l{uK~qr`#}e?*2^nAmwz`ei-g)mR}arrgaV*{!Mrb+Jpc?1bC29k6g*5ZKJZJ3Nx=A{5G-|LPp8{j8@v2I zSpZY%)=mfF=Usda=-f+fUR^rSEud^ohU(enaTD6Nd40l<-=`3QYRv@tJa|0@> zqMb%u#U;c{<^UxVlSjKA2==Cw6vvRJEgRb2kAcD!OkC#YTGLpNZGj#FR$FcWAl$ai zm6(CSY-X&)NMXIY2PPMSfca1imO~xvdNEls$;oOm*7_2gZ_)z3uVqzLoR&goVAtK) zsDU={m$MD(>Y50t?F)IMr6Q-av9?AvqUz~#r30j@I5|YLahgTFpZt8i(2S@6RPnK) zYs5UG1R0q~QzCO;{w(ux@EG;#pPW3NYb49~8z@nb@jYW_XD9V_h(_`FXILD>S|$%o zRKfr%NIIPlT)YR$T-ss1~pm%Oy0g^CJjjT&d$P-XTjqam~j*Vbr zpGrzHNst}*MU|SI{O#MvdADj%;5RYZuCH_6+1pvBdL)1*8 zlgu?>U}1rg)A9UVSU6WL+yfZ?qbemPsuJ;CTH2e7NMzT3f$Q>nof~3fB$@7;Rn*s4 zR~FvXb==>k`Wy^>4ax|5A|hKoWGG>`cA0I-D2Y2xty~U*wq~=5(R}sJPNg{ZfRL%f zOhEPeYK~WV3AL4t6{uYDU0vJQI4lnj zGj({)gf3?brA-MIKNk%F%V|({02Q$=uNSbh@nzVDP3{SL>=;?tyfA!H6BU&g{`?Bg zjLdzKgz+VOs;Z*m7@Qb_bA9}jqbMZ5sIsS5*+FGlUe)7^w-$)1em8u?1vw#jfY8VT z-@R82@pE-7WXS?KtRTNa?ZB3f4gv^M1;yW`v`=JYN882Kb|-ej+&PdzDyp+OIy(2A z9+f4zhQ`JrIH91!&!&qt-P<)5w)Ti^^$?*=2Iju7{fHeM9i1GRn4X(1@$vfc%7)#yR5u) zNlO=PB_<+U761X!J6*6kzd8tj#}46RYa_c(5xG$xSF9{^ ziwg_yH%$V5_U3d$#S(I-r)z5rxcVRZ?)};3W}~8^r9}-xMZ*rQ=L|!W`^p{;|GU_oet(@D#<`+XaLVev%_Hk=@%$ z2q2>W+I=r@u#Rm<7V@owCP@PFXO{OV=>BM%p2kMOU*p381o+3pY2f~Sy4$~G__VWf$1Dlsy#>`| zW24zDQw-jqph_8Y>HE68a)%k#M`G@Fup^Wmi2Km}aDF+z!kEPy-{}*v(OnTOs~^4J zC2QVAv9pV<*XD!{NW|fjzK}gBPc?%lV~Jh9UJZ3kPyt~2c%x+Vd0g90j7h(%T=+-S zu_oPL0%awzP*|7PSeM)#|DoXL58k)bp~y2}_T|?bAp#d=Pxophu51fes z3@psAA)(DpmF%d>pJT;$2FYF%w#iFXRM`*_uc3k%!8(6^jhK(sBc)>&~MK+lK>rnIjO`Dv09hG;!Ns|1Y(ZjA3s3UXSTPK_y+ctUxgOg+Ozn2c{zL(yo>y1%l9LMA(<>do|R4RMqIQHiJF7H0ye||msqj8+^dfoSRU)OV;ApsMK*NZU^Uq^c#5)JnBB&?*w zMF#r3y;t9;ct*eWh9%|*Aa{n*GMzMs$eax<{A`qdus@$s2iS>l~6tgP1#NgtC^ zE2$~JP*puRX1vS6k!rc&k1&EZG+MWcl7l zG!mq;N5_m|TAL481bA6l{cq_HEB5s8=pEE7&ko9~R_aw?#(j4;Jm*k-e^V0!JWQdR ztMbwd^Zo$zEC6>}+M3rfx{q*<3P)@ZxA;A>>{H2{@r!@xzYUUk-g;Vk<%%>SMJ;o! z@jsvww-r=?h&c4~;3_d_6hG=}un8BXr2O2q%E2`?%>c#9j|s1rrZyHSKXOy(eq?7) ztsWKnP9(vO1#C-Jo^(Yw_VZ_lg(S~~kkz?a&9=7er$kzmxtpz#u>Xroz2}!MA*T69 zmCkCxlH(*4Jr5z*UX&uTt!rz;J2o;>$Y@dAAr?9qPW>uV?%1cg*X8h?85x_x!q!W6 zU5ba*e0=KmFoubOC1fJonN*XaS=yG(28xSsjD;O zn)cI%wmABt^S*vvqA+6F|pNPSNA>F9;0Pe!m8x$R+UHY(} zCc`CvB*`n|Nz;m9BO-_@)aFi-$MPq-+S*oEN7>m$-VU1&itC5gh5ahFJ(y~UhVlZn zEQgMhj52!i6eX#yUZEx(dIleF=dRN)ZGNRId75;LYU&_qRzU{Nc5PXq{ zXwH}z=7DdrRC7~H3ofe!T$0+_j_qIn=H!s%*?>3*E?=>=p#)!*R>{jX?4h?~(Xgo= z{$;$22!{vhekC^67%?x3 zePur?Awke%*5LKib&zsAdNgrq+mTd5w8jq~0n`z{tw>6@-B8b)mvv?ZF%15MmCg&{ z%kOhRE5R*^N165QoAT-A@i~OsVW*@)anUygMKv{L6%}&c`ne5a&S|iiGBHU2l0YoB z^q&IFhf7OO^sh-BvdwsTIg5AqlQQA^QETq% z>QH&1n9`guchqS{E8r3(8>D3pGdVm8=%B&|b7T0Cialy-;jAC;%UBB3pNrkgK0c@P zw_w0-T))!5jMq+bhr6f{Ml7hvmR}lU9VF8WvH?kJ(CBC-Nahwe^<15-2Nvkd3aFaPL+?%>3AQy38wb1P7`_#^sTjv&|B#(n(a47WPH>LlpB`$<|^ z8IMV&q+l;&iqHl<%VqL~?CdF0nJ%9_$HZglrO@E606|9hLB$ht?O%2)y5hb}(z2Xq zkAf#Vhmf@n@}Xh#hF0#sCtqBsZzVo^75Lj9Un zmxJ!VSE?C91`7wiggqZlNO9zLM|t zQ8+D8ONb2Xb0mm(9Vw4$ZyZK)WiXRkY6FYwYL^2%5s&VTl-UHKZ-mjY6M8V#ZmrvRTpN;PS zn*NPSt6hBK*IHSdb)1ReNv=lzx^Ezd$rMU?OY!#_+5OXhELP10cFyD{!tMVp-#+(e zv#Qn_=gjx|VxnIIPMLm>=WEe78A)De#_VQM<#(rgO9G60dZwe^v2ig<7Z8cxzo>=q zS?0HVS6y%8U?WY^R8$0Pb6~K=|G%prm$ltV#fVG#;Ki)8UX9+K+?YKF5#sY4dGFCu z@M?D7pybEvIra_wJNs|neXk@!?3;gVlVXee#olkl=ocVwm6n8R&!q54(C zcj^J_cadhsv~-W99z=a=6jPD?9z0i~A0oJOH(aGrSFC_s(<`H3@1N@XBzAGfovXMw z^0Lq#vZ(eaaq2g_caig_?xYC&Ay(6g9S!>VJCX1HEi%+kG>yA*iwjYrH9Xk9P}O#Y z0k%L>n_BE;Q3a9bm22@I>;88q-P;+Eg8J4U80`Z-KelS&ZGfkq2#UFlZ=i z{dqy&7(?)HwVc0EXGT3D+;$N;rf!TdNJ+owQuu7q^+CO~Bm5b$Qcdx;E9u{oXU_Jt z)20aCU3YHosHGyo%~m!3L~$g3{ju{e%TM8j!ZW4@-3QzwI^i|6ZE+ zdYfS72Z?kNQb$;n7ZA}QmcibaFQ;Cd>`_`>92x04*d*d;FbG;uf;`S?Rir^3bqh>9|ojYwPqt&1vjpGik!ou5s;*}m~$N#Sa z;V(+gYIe*AZzm{&BrVK#`99MKi-9Z9;Lg#5eN|2{<4QiSyi zb=G?n%89ln8cOx7eRT6U*>*1pSv1u{uj59*&evPVW+rP!0uS&mHhhh};oNrKUtZW} z8uTBTg@qr#eutylYwuJAxI<@YN-G(FAsc7jz5B}8F>t=Np(5JZO$!VTadBvvH)#Z1 z%(gzL_Nzr9!s6m+lEYq3G)L)x8hmM<-E)wt`(z~gyOtIL5#1u>yF`Kbu2r2W_t1qS zUC>w?$-6yus(EA_&;lBh>ZHBa_t3gkI?Y`KGRMqr!r3pu&e*05{{EWxhX!AAfN+`4 z8X9YZc7m#Cf**l$C;5@GJ!f)^FyyzEdCk&iJK&ID;UV#7= zkBTwqF?mX@N}=7;8u~>wSmSDgB>eY*YZ50|Hjj&RYuvH*8Ow;t#XnILeJ17$VTPd( z0qM-sEM>p*AR!?#B4T$#rS^3La|rF&NN#2?=R;Ec8g*v*#7lmQi-SDOgv!bA(xS^A zxQONu8YU`D-Ab>S&N3O$&NSW$YNJ}|QBY2+v}XJJ>!r1|ly_V8NejZD|5Kpg@GvEI z$kfcT(q)GTaRxWBtz*`F@7!Tb@^BOLnSNG{{u8bO1Tyg8Vx~PB9MQR{sk;X z%DAeUnndRo3_Y+L{@7h*Kz>UD1#CXq*e5*vEL~}Z1i>0B%v5(GRd=!gefM)1@jiTt zkriH>7$}QZku+t@fc1XfU*Vj%nOXl20aALGa|qfRb*_R9i4TWH)qlkGqb~~`?K$)D z@r6Z3GHCk|QVHT9*q9uTqOeN(^qicR5P$!^!oit9(6I_FGH#xSz5#70HQ zOC(8Rg*^8MEkJ-x(O<1%mu5(eI2|ep4d{eRFJL--%iDVl=xlCV;C5&V!~uN>onS>Q zj^}Nmu?JUIyg=T0@Q^3BT*dpOS$7&IzQKL=)O!EV>zaA*e}yTlz+iUh#jnBw4-8h; zU_L0?E&xkPN~)yY{B(N5t;1)VbF9WSsH3*JamH3Y(PIzYJ#}+o@O;+Cir9k~lpXMDVx`b314GnG8M$#jrqR#ns*z)6~L>eFE>k3+rxKDmx{?c^KtgWq}@%Y6; z=g=*(U54U7KqZQU(9)tS=7URF&le?x6wI8tTmk@B`c@La+c3Owq*un}{ z`NnxiYau{&%ya$D17U~xUsueL`Zx%O7Er~BR2`dj>q=Dbf?pz>X3w$Un$h>D8i@(hedUPF7p1H^tiNoAz*k!>JuEbRGe+$0rwe2oHC?ry_Xap51w@5>XO0 zU$K{5VvB_?L)J{kYxGeKiAe> zjxd;L=Hub9>fo#6uRj6F2p%nWSb(r30qr)j#9KkQ61Uz5r%}@5c<=_n>S~v4FrX8< zoH|Yao4ihjC~A_-;q`uVHFPHmqP{t^a_Oh#W&qNEjP}M;{5Hl zb~m(zx!q2dLJa+7BApZCUGBWvI&bfS5juA!j=@D>`7wcoVI~tD{%bUiZzb1|txex# zX{^kxymxxq6zr-(Xot=tZl_M6G=7N&_+%ms?l%8^j<(P*Gjjxx_(=sHAx4h?-=oT{ zP8Ok2NV)SxKb{4S4L4z~(E3zw%4uCFsR=@!=q5)= zvd%=G!_f?WjOYi+rxjv5Uh9i28=N&MbV5Sre|w74TaYG>4_!+7f9U_ljF!+KU>|NQ zIo8hO=kgufk?-gabW>-IXcc?a?XYJ4&=(MSbu48R@-b(#tk=IkPqTbAxp?GMv%(h> z6)j50%iHHJOcUSmx2GqiI-xatq{7wJsL)s69P!Y#y1X!rRt)#LA^PX-^9Tb}z-`2O zV}sCbuIxvm=l|E0%LR*dpM556zP#){83N42(w}O41Y3-d$b$#R8^k{{X+)uL(()R$ zLctiK3W-Jr0dpwoGkxU@k5N=nJ=kaQ>x{|CPF)*JU0tmp#4&)Sp~4)B3u0HZrxiLb zE(8UGa4Q2dX~Z_}#3_63p`*^+Ai5O~5BIA)=9jJ^Khki!vth8(IE2C- zq>|UutA6w5c=FN7+PLq^(J_g?%H^}YrJ|%J=z03_#{?@yqP(?|t{e$sG}#Sgr_9By zEFiW2{Gyof^`kn#gaw^f?<(#n-EqL1g`=wRuG}kmp&{jRpI-UbrqeUTU9$0kzD_hq zufQilo~P}_l&*m)PrtYCOsjG+AfaQ+<{?v3P=Yd;C?qH)B?aw&2jgSj^WM7ba`>8> zu_`TlEzEal1Rc;BSZzN41|x1wStf+yfcVPAg_iEHfO$znPQ9wY;2^^N!_}Eg1Y&=8 zF5~<6&S~7DAdL_l-O0u-;T=-s7NBqsAihiKpl5yJWzoD$@vWBK+0^ zURqUfpfWjh%0+$GoL)(6{Zn0S3t1kwswgWzHX!(`PKt1Fc$Ud5hittwvK^t)gAms) z5}sIshGb2|ZL|(hjB$d${MUw*RUUGN*MSlEPCn za~ZsbzkF_wUy0&~1Lj9TH#I!`^}gh#GpXwx{f>O9`2&%?&`%$45H!Ej)YjB0xsTmm zvVxtJw`!f1W@K%X2S_jhEbnM3hDIZ9WRhT`5f_fQii)mE(Q(61#}SWk?-KKFO$usa zW?}4Noo-{EKXfNp5x0Z{%+j2ssBLe#slacbywv&FO~SS%zJ2%ewK_N?7dQJ~#=X5ZnB!tge3k>}(mb zCIFS^X1accAN$DeX@zU<&9^rwNM9{&;v?v0@P)LkSY?7zCaH*tr#cwQ+e~Kn-ddlog1Udhl-HGgrm(6HpjkbK7^DhGN+RI0=UUWmWpE8;44RkX zx`p^tVCnV?|yr?vDxaLE|nXB?aQeypi8C(C@BlpYnUmUVp6~`+$du z28jQzM3FD@ndYVoD`IY5r=u26-8kp};(FoJ$6X)D*BUJXJ<$m{c`QSV{3Sn`Xgh12 zn#T#DbsG$x(C92W9gca1g8KY2LY*`5f}foB?sXA61*(yO&Oycf_qo{847FtbX$ild zP~bn^nm4i>memVsGPr+1-sgprj?~NFvDq87r&a3tL$ck>4(ql@<~D9mUw~c62Pgiw z`J<%qEwcP`;@Il$j;PLzeEXF~O>lmi>v9_Z54AXbA;-G8GTJuPn7sw|LHTYDw525q z$L9sO%0M;M21JFdxS(gh$>vaYWDCcMQoqCEZ$CF88u=0xp;u{7ih$emR`-x-hR}1ziT${ttvY^yW3Q)xL72 z?)SjoRQ7NUULGE3y1mBYGN6+@*YVQA!g~DYJszI;%Oq@_U8A%*8BD09kxb@S2jgXt zZng;)4yvfCYFMd9Nqi!*QUyo9}fj8T&5SugRadz* zd+w>83W82s&2wi(LYQu=S}P{rX8h;JCKC{|H+3w$ho0-{wR^#OV-+dD-DD0-!ya*2 z`f`o=I~#izPjE0^2-Rqr<-xh|Oz+ODR8LnMWmVOXkU)m|ZfFgY4l(*0p&TL%TjOt; z5%+C7?^_N^)Agqf+Vhu>|F(AyuZI^w1QGGfzpB&G3v z$+Z6n3LlkUm0kq2i2bm5WdA^Rs!cF*Ve7z4FwvH)jTRxAjHc`6vkdB*Z)Cp1XJvLr z)jfs^#GIP{h*hUBKECenGc`oAes;6^Abb8nXT@bp!;KBk6vVfWbcv!H*I z$u4GLwC0E7rAwaDZTI3AndM_88$0`cyDq;e{=&H7Hq?&)-z8nkDk)JziZ%qqs&h~Q zJsMwOkWqWhTLeR~xQydB;fQ77A>G9+d+pZ}Yz{^AC|}DZ0LAuUs*=1`Lt*>|1>$kH z5CmgT1WQQNBWi(5(5p^KQB-=AU$94Ud%FcpCf6?Gl@u35gk>F_JQPjFcx=R_zD_Du zy++SCj%--inwciT$KUbUjRm>i0mXimoE0USke4L7gW)IySZ(mbovi3Su7BJ>f0N`U zbYVFMh=uvNmc~X4E32IB929yJ-1`0E`lWHX4B=3y11|w5rWJ=S_Q;cRcRz4Hz-=^k z=Iv`P|L|1qf+T8=K3*m3F7lif@v@DKK|u`fH?uFFKi?7hag{EI4OSG*Sh?S=n>T0K zNjjS}l^;KTJiA`66&@8u%KTUUq^-SO1B9b}GQWx+fE&)Yu3FU;&{b$!NlZ<>y>&%& zsi~>iK}9shBxwkPIc#jeJID^&h6-)$EVar%zi3iO&Cf3>2!9REM;_w_Xc8bMz;$&r zHLr>~SVG9h$?i`nMwo^crasLUg4KYZ#6Di!xa}D|2-wP??C`t(IP1~e{2WrhayJ7j z1n~~49ShE`XfSN3f?-4iJ5-(QHVU)mP6xBkxTu)dN}2tTAUk{Tskg`WVt?7&tec1A zz;$v7C*jD#(I@3=IgVG8e-V7ObS5!YMM!{f?y z33|XdcgP=vxLI49*dMp%&!-*Qz|8G56D86(qttL|b@Wi|m0D6{{6IQJ3Ske5Z8$9p zpA|K_!@XP{HNIaaGp=A8;QR340JrY8q0Nf*1*0)A$gBuyoF>NcTWxMKI{J*UNYuoC zyXD?L1V~GFLdDb1GO;3#+~ZCMt62~;tyNP+q&yXz$;}X85t9Q>(qK|!bBMQDM{44tix~o((b9s z$PUPCY{pAM6INyi*A`ZJAWdLU?fLUza`jTsu~X-8jH@XCe|yOfG)-ke{V=gQ^p=~7 zVSX!)c@YNF(Mq;7E~#@}SUKnzm^I#r{}xzpcb3wS39=VxgAaetx>)Viw7|V-2_@(A zT10?Gamw`L#~`a3qiwQ-bb4fC;~n}HC-On_`RyygRYE|XE}({FASdKTsHW@?IA$K~ z(%gP`0X!VU<{UJlE@}qV5fRf}LWJ+52T+Qb(E@K(Du3E!emMj-GtX^Y|o)^+=hDY-50%_s+zj=WDFtP6M*g(Se7% zuvJ=xIKA9C$h1g+kj8yQ#6E$dCcIm}oMTfJM4&H7+|SI#nA?9}ef!DHL54V<@G>Y8 z;N|FC*uo9ry7S--MQ?YUXlhxkw58VJeBRNhh!>(3)cJOpwR6iX_c#cG0zL)=5L^qr zN^s?9TOdr>g*50)TZ2}-(KB|5gxX1y&1|4sj1eOdOcbg7FyZlZL19E%oEH88H%LAQ zS2pKNO}_5r%AeP)f?;K|AU}jOl100#9%7xqcR-(<)@yO^9tiQS_Ic?n7a{G$yqIWR zUD0vo@ED`_wGu~iMk}!I4y$e*G6+l~ zpNMRo`+5t{*|`rru~$BIlT?|L2xf4fUBgqG)2vpNM~_a;Ggkq#-d17PEZbN+*~~?Q zc_2?iu+(Qcm(y-SJ-&36K@fFWXa;=|;qB?HU1@K&mBV9)b~~xPbQY2{%DneBcC-m8*6&v1`iCT!PYxs$93Q{Y++vjo0^2PzSp*j$ zpH%krT_WPA(P~t`OFzo`7*T8H=s|dk!6DI_uLaXYxj^$qn$=A>X=JBjcDB~18-y7( z^Y^nKCe)9gZwdPw5L`T0MY)jU+xEeCS8-&ox^nA#gKm6iat#43&)b9q9>?kSjIA6^ zQDJV6KR>1We)PhLVeDT*+)Q9+csqtYt@{XW`H9{Or}RAZd;m87vWO~Ki`*MQ2AscD$pyGt7L9*f7BNlZIQ~$W{V)xF7!ENm$8Y1}*i3t0Z*#ofR z4Ge~DM&4n{upX+$W@p+3%anWWgU7rxhTCQ5Bd&9|*SHh-rHO9vE*)nq7k|71zH&>Q z4;^zK-bsLNY=8H}9Q=RN4|vp`na~T;gz{S)(xVejy*(jPx>LfX;`X-W?QN=V@{(2=DQ}@y5qH zrMhl1(Ej|Gcm4P1uRa6^fuwxKulFx<*^xD)fN$yUWPv!$`w^GJ=1A zzcnB8-G>kEK6Dz%e;e5xs#9ZD@KAt7Y34sAilWL5VoJ{Y3nP1q;UZUfc#0qu;`7b3 zrW&ncmwwM&XRKsRSTQ#`LC}?vRmgRq1beckE~b!taMj6W$NLnN>)#BCi)e+Ww7kVM zGC8QtkTG{#jIM|kyN}eszPQc2(0TK3+Oy2wOj(L5SFCU2?Ch2F041>C3u_jp3A2oW z!w7sRxn{K8NuvtX@#faI%ZJdYp$-_h!XrWcZDVZyL^vwam|gRJW2+B(xR%W#A4MTkhqb~e}hJ(zGL*a?q6 z!z~l3dn3mEbb3eYS&}oT@i$w?A_e`s=7rrBxh&twy#0 zRvWHTzty(Vej)miw+Z7NC~>VvbZ2QXOh04%#zhjLkpjh2cVf*|BLgWegr9ci<>baW z2SRG0Cy86fbMXtD69GDd9Sf#A_+Ox3`r%DkXmx%HmNp|J;|Sl6d~Gq`?GA&p`7I)Zeas96fAMxJ* z?bmfM=I@e3kGj-v`laqs}SG}fbw9?DcbKs4D3Px-@3p=8u4G20J z4UaF}0$x_K%TCO;8qs66*O0<9Xg-I1x{W6Ug{@_+QM-#Vzu(IYB#L>jFHqwy+0O;! zOEhwMc+{kfQwlnIC6{7yJJdUno%1`a!==THH2hNI;GEliuNbk-M`~%|5pI_N1rzKx zTa6tv?f`s~0rm+T6g2~rj zC3(ZCgARJM!Usu-8lNMj&g1-ErcljN53Y?E4F>-GA@XiO ztMkVy_5I0|Rcrw5&8|?%tWCM@5R&meR=mmc!%gGS#neXQ*N^HOy|^t&T-NIQg4AtK zP+N50$dX)J>mX_p5`}tO_P&Vo6)y8*jAw@$|C57cU0GTj%m-8NR3n5knAEv^t+l&t zl&6K~CF(mjoOf>7b?vX|LR4{muo9ekxzWf{JDVc6rsii`-{g|H*9aFEong!7B$azk zc=zmw7i((bCzL;g*np%47Ko7W23H!08fk?+_?%w2?+t*@UiI^rR=O!i@jp?kM`D*Q zgmbFHAmF<9E&1<&mKV5x!K;8u$+*Wx8hNavIzfE{QR$`cgO4{3XF7}s#WKvTBjmKA z7Zu;WYa^C;E+zF{v%p|Hfn~N6+#hW`r0PYRNBp{}o0n8WdgAVc1hn3oSorZw)EPZG zr`gMV{PmM0A&tiiUYfz3>0-ebq^W;jLgo+nsa;oB?%Qq(TXKGY-g_rhI8&ZN&L(VP z9huaQ>vKuZ8pkq{@XUg69Il$2*4`1g z$e0N*A;6e+fi%6O4$zHXMWuOW<~K=5(C%uGrj-d={7@Q!-!kU@j~bXFwL^mxIy*bn zR8{jQ(0>))ooNqKnKI+9%d6f!ce9!(hX;IOxXqk!d+#?I*>ii%tt~-CUGKNhSpZN+` zvMt@y$FfD93!mbT)kJ8sDxl-|&Z-dp=FF?g%S$4M&kI16t_{15O)#WTRdk;Wo*|<; z#n$7{<6P&2?vqr{+{nmXIzDAm(PC-S3_Cp?@dZ2bT=!FeI37N0MnjqXff>H1Y`SLY zuPo$k{YqsE;u#4-#wTQa@iV6=&j6W;i(*J&8cbuX%uOacwvA|sq3HkQdxxs(U@ru`RN zPi-Ki;>503UXz@>g+Pq8#ailR`1B!=Fv;1fBy2Ah@`H`Q@2$-tMLmd z>4#$uB{At&Yo|)`^N}156sgp2i~wqhFhshsv9cP!Olg_f010(9_EuQTXQP33%guL6 z5Y;rh3C_QI*Kg3~b8~m(UTbfa0vJoZsA+Zp2xM68I{Nan2r#k<-@EgGo{j$bGtF$%Yg=%7qHJJ7 zZ)#==>(GvgKPNs}dYuzP2IGw~UUR<9a;y1)mk((2HQZ+R{se$uo|lBg(UI7ZC-Uq^ zWiFx1&LNB$&Tl@C20^lfobFx5rt9(*$q=!YknmjI(lRT2EDii?$_|zQ#lmk5w-;cv zOilG+T_y!r%+n9}fzm86F{23mYR`dmoF_9_>|loCbCVID!IK30p3lKpZ8c+=Z{qiN zg{tM=u?}DqnKR{RWHNi(o-+e{>%)2Mi&jni^l2MbTeEzU*Rj*`4(K#<+HlI zx@;+H?%{^*Z~XdkrmLd^;uMITU%MAEmF5depjs6%vqLh9lwhx<1W`X+sJBXEyd-aL z3;6Edgvdil0_$j_`=I9Un)d={wS|Q|$H9~_1d%~nV8oSN`Go4b@&eN`V)c(zNlY!T zQ4TF0wvH8vhMUXVj^jR$9;mJ=?}AD-{L*E&as(M5tY32=E^^Gp;NJ=nFbqCFbSfC^ z8Pe6V@BqniO`p6@)_Lu^?7jSW{8J<){O74Ll8B!WeuZ)4TRtcbA-CF-M(!IIbcCZjsJQ|)GzCyf@JGa z;2Ng(!*z)>Iri6=Sh+I8R2~{F`c-qxw9ZAd#jqqKI;^Gf&XNS`vgVEqJT-rH_pX86 zn7jC_RQzoYZ>(m`n`4z@fn$(PS;-E$HyXcsJQtSOS9-sQ#2@wibMQ2R{Gq7(V%g z1Z9xowc*(5aM&Hhb)CCLB{GHPPT4DLD$4bU9prj6x|c zY`CEW&VP{qb+{oxMi#Ajv<6nn1h?xq$e-}Bs?NyN@^8+aKiYGs($MpJ*JMPnvf#DT zEQ0v$el+!w(Gi{S`T&wYP;KE2m&g~F1%Fw|g}6dU(#bkYZMAc5d0x#ub5?Ul>cI)k zoXypm4s|=u1fgKnj--aKKr5>-FAuTlFi_-8yYxi`vo?7Py+k0!eSVO#YwP#Xo1o3k z1vTLvE`gviFqulrmk|5y&wk&G33@{ zc5^*IT|x{54TP#(4g28ZeLIYM61q2IX4RWP)%Np>s<`USo|pylN)`N-u@v;A0$9pk_eVYyI?+9 zx|eJREjB#*&9SC#2A29Ahbh=&7(Fq{W0CJRE!7L0x||#wHyl}YI9zwi^H9nKUp)zv zU6ur9&sxM1x*iBmgw9amG}rP3U>ikBC&97ia%B(gG&Vy;`>fh9r6ax)@nfR=38wBi zyYcMH*F0TvokIO8JHo)jxF6`6=Mv+=gj966mJJzX1Iy_j)bEikY3;n6Di8b^lHpT- zY1a|VEG7jps8z@mvb@*w;w=)gR-JSWzNZ%}z8d&YY&RfbGznaC^|a*D4z0AFhQ?FT ze9`9+8@95xQ}i@C7;+!*X}ME!7-xthT3gWQn*3u2FoRY;v%=k6gYGC`ON)z>s9U#^ z@73_*aF*Xg*4?w@L8TZ$sqgKnRU*X~l|d3DspNZr@uep~&X z>Y-D;f5fxIhAHc@Q;eq401o0hIl1GRRGIFEt2QxFgv+Xk7v|9yfo0Hnw6;|2(u`MN zHAJlL-rkP(T*BttsuLOIh^lzOYs5%92IdQSd+w#2hVS}rz1C8TU*8>LM!!FL$JLN! zA&|S-j!m?GPHC7l&Y6|Se#MG^IT`0`= z65IMDfzsZZ&mS)tpyIrHE%Il+Rl>n3WAh8cx=q`i%K|(1yjB)(KQ-0`w-~C4rp#0M z$ax^SHZuh8A#QzY??OJxO$R=?h}ZGT)Qq_+rUI6S_j337kgFufx1QO(ehK<Lz?t=I5S1wd$R_SXgLK@ZbuS;Vc**JT8}i{k9M;f=K-B&H17cq_;5RvvM84;y?;J|bli(y$jSOiNgU++HtxLPgR%j=?uVW#U zVk+%x7J!eGRv->&fGiYe|&Y($AlA zakW+<(EFla$!pa`p4nvehvs(8;0H2ct%>j(lG5fO{y%dc_#gXtO4 zm8~ZrGStwqkFTyYa2r5+^xVwZ>)?>k*AX6|7)!M~NRhT#9YQQD*_ypAU~bizEvoVq zbY4S^X9uO1L^b!CSF`rprjDm8sVMRN>CH2ngJ7!ZFYy(38D%dY?OD?Z|3sch(!lnk zQ@;f@+-S5j^i)CBDktj-f&y)+>dgoJJ}w0%&g8_CH`Jy=uGYT2t0*9Ko-{CkT{v|o zH;$h_@qy1Bu1THes4NG=&}5JBQ05TIM?Wh4p_>O>mbsh`=5x=Z>Xo6Ypoh5`f+`33 zA_uH3ChbsvVzlyS+IK3zMwzpE_^iFFBlFYuV(tV&eP>03k>0eerNw{+glrszX*y!fY|if*$i%*zI_wZpQo^PcZv1sO4QZR;IvC zDL>XBnVhf3xpswI#4sdA#-mK1j3!7AKiv64Hf(MyZ4zlO=;7@q`sF1gnC>qgWfw(f z+s#2T+{GPCMqZwPn__aX_Flx+kFm-c+Z`kH`rpQ{H__&HP=k0cVttdCZn$r{Rl?B) z(qU)vK}ovtE#Iy2kp*0VY&y;#nVDlnp6c&ueQ$seHRRJr3)5Z)1ME1x`sw`YCB)+J zPWIiuh1uU<*Z+tMJUo=Yi;DJhaP^_`14VtT-T});rjlZzeMI=5dML0d3AO5#XWuQk6tRmNe7ij>*R>I_*jr} z(4U^tJ+Rq~DP|>$Ap7I)j84$}0ufO*8Wr!Prh1#Z&DLv0p)_V^V7q^p2h(s3q8eYK zE|Xc#x=(EeL-0^owkf!|vwZ6z2JNoA;}Dobl(2r}l;jCw-eHvkqL%MEfQJwk_TF0^ zexXX;1ol(~Y-#kdv`&pXzw>*3j^tlz6%~+{KuajS6B+Y~D)GND>LG@t@kUkZV|4)O&{+pAZvW<|!u;YV?oved|3Kz|WIe1iN%FQ^-RZ3SbHM>U%Onrr!F`ssd~ zF}NHM1p>Hj*C%{9Jys_T?2P+>j(&q&INGm@6E%-*rMViUZhbwe%%B_PVzVkNd=iyE zd}lP?VELQsQ)V>0-{OLzSP@^w@V_uFmh0{-Xlb=$G`|$%HyKV0o7L&mg)cC7>!hto z!^G*INs9n}*!_6Wkk_STWl>|CbNFwao`QP3;ARv$Lc1v`l!8mj?KOlBMZTRNpb;3$ zvue=_-o?90kfFz4YGW)TH_m4* zPyMROKJT!#NFp<2)ncN=`NTDz?z+uYn2yUGRyhua28*yY@M&@mgs-PR0bxMvJ(hQG zCFLXa!-jg9BWjp0MBh+J;Y0V+jQ@rTqd+yj!?R`p*krOm^^Y%dGDbar>&_sH-UYVI zkY^KB*g;5=BDp!s984=M_iK4=O2p`8lid<==<^wB2|Q|qZ!&DC!^0|it;^}@#>fO= zDerp^AHFat9c(yq-Nr27A(HfbM16jI_`OXWB8)88bDzUTwQ@Twwz61QWOF1^Gt1qY z+vp@ifKXhe*O*8F_aZKFX|RfNPwP<&t8br{a}sSNX@UtH6}d zBvprwiNI(9(H>k^aX_Pt*Dk2G%C`jfdaqyFUfT-_PVNq|9Gi zFgCfLIJ_1ufDph#u=xvqTRv$)LIx)2Bp`${l06N)2=Gk}bU!Xbc3z5}W(cX>`iE${ zj6xs()3JMhnyhAjzDC^=hc66hKH#~OJ>Zh`*;4~B(;L4}5q^N=WY1lK5P;p)u`1xy zP)2W|J8L~!OZsg8z8${v;zkef>`Fnz1kOt`U56DuT}0uWnEt--@}_sRymu&~#hbbZ z2lvbKvm?G?`2cDW-pbK<3*Sv3boBhmXGsFer@_L=1lM@(S1%G(P?r^7gzb5G$w8F9 zj7nm8_h*z-4cQMs3RF}}Zd>Bx-7|WZ1Nw?q`4yTZpQk8EI#z|kAu7vWTUFBT`2KGW zf;u>7y<lYPf2&^FGU`29gA58W~Q-&v3omGWA2D~ZQQER zx^j@4BvP)Wdg0lJ(edgZ0n6GzDf^Zf7{n$*kiS&2JzNayxmio z2Z61Tc;#L>r&WW$ZE>SJ02?cFIpK(J(EMp4S5e}E?qiC&d@XeQfQx1f1ZW6I6W;;% z##avbrirfvz4V0^#I6h=YWwx;<>`dCHEfT zs^iw5Tj-2PG=4O}g%f-;S!#9cmlvjUJ!SO%M%}SoffVi^K75(ThZy7*Pk2r z*4Qb$?s`>LIT`j7ss9Fu1)|7^A!~Was!}r))5i z3ag$%cl+{=An6v1a;R-$Zm2BaqPQ3F`*ET>@K0$Z(tFz`&VYRM^3q+_KpI;>SPN~p zY#khw`}V${Veop4cB6%v>d5?aBwuqPqLOakS1VDU`>y_g!ltve4prR+Jm?4fHGL$< zGA!4uiD>zd)eE*rh_iQwJRNTgN7-1;yiZ0{)Q4w|#M4|sO+EdnKYtW^`~ISV_{hm; zbT%`zeJ)Z(f5fziez`hUVAx&X*Jxs&x6hSvoSP$)AZ@y~Tw3K(*Z-l!Cy?CL+ws{Je`RDMc{L@*f zKUS{GmQ$4$kf#bJqRdU|G$0YeeFG>Hysaq1PhR2S@4yF9n7?UKB(Ovt3n^7lB2fx6 z3w|lUV$d_&TU(#o;fGIsTg)Ok{&#bzFeLp2{tRm}DHKQm7ua`ghJDw^s;aSggOIr+ zX0ZMcaT)J$yh?k4VxR_GAFC_cHQ0Uk)k7OoowfWb|1(Rb^z8ZvLfV>@VeN5bOR8>s zd?^MQBRI7cEe_$P%5?O~V7aQ`=kz+hdEcn>LG){H_w!@2YhQ{Ja>1A4L`ySCS%SZ;Xq+h#82;K!D+$T?tYKc z(WP@U?vU;%>k>UyXt9&Zka;S!bjW+(?c^J^r|XsZ?|Yt8VvcLvkRiic?KSl-=EVO7 zy=F<4?tL{@_-=q_-TV(w>3Zg1^oxhZH?-amT=cx2$r2V#Ex zhWhlJHa{XFEtbSU?`5rtmt=$3^>(Fv%+>%sr&CtFkJHJmL1ZkR1!8h;dGGDvp2re4 zDxnwpA`ptVhA{z2&&VhzBl9+{Spw4Cs*Bm3PoTb2magS-Nk1VWQ|~)iXQ5*gh9ID+ z(R8aPx}4~1_!P2IvN?`69?HbN@ta%ME;u||9Rl*@6VBs2mjwI)DR z=u3FzN3~U2>x+bXvg%p7QaIEgwRLf7slsyT*p*fkb0b~4NCIDQv-BKPO4;Xup$d2e zS3I|NiNjm^>PJy0dI!3JG^+aR_GOiGx%`m}bFUPlYKC13wdS@1P z(r^4U=zc*jdGr6V_TAB3$N#_YD3LUXijbA;kR4jc&iIg>z4r=*3T5w=6%syH_8vv{ z-djR8*_-?P==;6*ckVg&zuWnoPJ_4i`}KZ}=Xi`nmxQa-OnKY$eda#~we?6+&Icxd zB6vr-FU|eu4FiG#jQhU{Zt=_gif{j7O@l!k95R9k=|I(Gv{wFs!YaVna62Z-tY>~b zB2e$KU*8RWs$gX;-jtY39Nyi3l0VyY)Pg0VKq}F22~}SJ_#*lIAu-XPSBUjN1V*It zvJL2{qFVl(aW5B3p|zsM&p#pn@P5UT0};Z{SF}2uQVXOT%uZeLQi*Fu9TnEHD()LC zqah~NQdf7Ef$tnZtSV&1#W_0%H1dqN`7{%rJ(Ijxrz|gTx6;)&vh%atF#-7n#K5<2 zC(gWFn9hZgVoHiWa$94MRYt}lKDC$t)$ zHOKE8K$KM-XPfRg7TBY4f^O%K<6>vLLd$NT-?EmENl8esbDDzWBBjpYEaap~x;k5S z_KkiL;12{$`*ZHzQ(|&zQB5Q*psN?;_&GzC2Jk$UIzxxhj)y1?iiE>e_T*ewe?Ywc zRZ0uw-M-cFQ;xyK5Wj-^PIQq_U2RX`cDh)2;|S!>@A#1) zL+G$z`hj4Dt^E>ioi3x!G}qPJSFGu1@ANqk7=zZZI-uf3vIJJAc1)h1XvT*fN564# z1jHZoexGpX>14K{MG(P957PV%Z z3*^MMydOa1Sap^05^cM~>ZqH1MnIjvMO(9{D}dS(3b@D|UI|vU>orG&qc-29*`~`A z#?ImWtX+2aYC`!lOZq^Ua2je@L(I$l8o~S@CJGky75=( z@%{Vg!xm~QFfQS%d*4n@o;KFbrde8Q*2OF?-ZbJ!8H#_IqBj+NjPF(8r?^5_qUJ0T)v2;rhorqs zv&4Do&l__V>4i%#ii~8IHuj|j#QdWFI4nDSeNKEnc>lO0KBTdK}#Phb$1ErB(Ig8RY_FCQK*E~2s=RqSZ| zCO?$BA7~G%VI)ztI_w!IVjw#Bh*GA>;V+amKPWsC%wCa+`)u>L*HE+L2^3}NeTF|M z@GceP-3ME|x}vs>%gZCq3xo0eS|ZjL@YDU3VXpNxu>4k#SG;n!$a;87;>A-%a%E-D zJ6qg%VTPIE04cuTh!-uSpV{d8M}wblX{27XuXvez^DWFPt^@ZGm$ zs8YXC>$AqRF(foG-0vJnq39YW5U4);I>VJ|MYhr;^N@DmlR{`~Pah);Jf``oeoMli zSmM()Fef2b>kkQqzHD6wY2lKd|BVVp$WBe|A6}+}9-6hTMrGO|1(hccs>Sabn z*#Qx}F}H_DpPj4>q;oVuE;5smflEHz<(iS>D^T zCtvE$Vd#~7=yvg~+>;-V47BBfqs*@fRk4X|<2r9_)7W<(HqhTffXHqjLoT~*a1qb{ z)y^^J)B2ia+&s6=0B%TiMkz4Tp}K<=N1AeKy3jXoe2oX16}_(>qC}id;@f&XLcw&?;(8tu zq*S_a2dS&?%N9qIW<+$52^rwnw*%BzU6wrUj^nw64##rWfiKaCQRV{Dv*uU`WV@y# zpT%s-T^)ah@NJzqMFs^8=IcJuinK;v>Kl)D8xh5y5GHjvYKZyioM6Z3d4hg zMaV6+n4nw%Bu?x7EQvNgP>g(X9vWE`0?v045I^2r)qU2-^ZpVSu?i$5jhVV10^)88 zcBd$jD?t|?w7wt8KsQ)mXD%rtS%f=v72>w|`1a4om9F)rq@3L|ev8Bl*KHRAu=_U2 zI@+6vOAd%`5;L`Xf#NNj)&n(p`KKkcK?DA(RH7vnJ_IQx*^qubDaVnFKO8Y`ZOkty z2>aMC>VHGZPMe&46c|h*(0hX5;OqqtKrR5LDJ>lh;%5YN6C+)TSy5_oDAb*@EJ7Yo zBc=7aM1djiR~`h~&o3GEfh3&=Ej81*bEUcs9YvNCU!!xcA}@;WRlA(9$0!(qf*~3$ zRayC{fPk>_9kB^#-7~#Q6!Nn&m%dLt@g@M~etUbr>gX005J!O(OC*pFyzredAwZ!W z>{i8cnl^t~O9s3;?9AJn=~D^{J>J68wNLe9P4#tWrmP1VR%dF(WI@UX%Nsjw`}op@O@W4dNU1-ZbSH zcM-RZ@6|rtC0uB0sl3PzNfT0SYoOGd#(i0eF@-ikj_T`{Y0Hx^TI%;4P00Ce zH7iw&_0j>kp5)Yn24|pj-?|xsNdP_o`vMO!Rc3v4vevTx)3$LZDJtYwYCN3@nBAoZ zAENa)fF#=$%?DhI_9J>I>Ji$a05NMzlKs`eMKjNe1gvh2jfJ3>P`=X=YRalwr0Mg0 zt|vq7eZpOw_qopS=C3G#o!}ZWB=X}>EXDPNS2GK7-c#Vn zb0D;nI=&4#^=Hu{C}@V#`%w!_J=k!Tk)~jc{jr?W9VG?=(vi5i?eWz*<^w;6vs;#& zTD()$*)&sFLGpiT(Wzr`e%Vx6#&RN0<`4h@+bhHHkE;`7#Hdp8;bJP$2dt`PMI|LA zerh;K-3M^l*ae{R)vLtS!kWxE7Ju2?t2Ps4XF*ZqY7cwHP3e3+FSigp%c<*Lo4LsqPA{XR8U|?R;~p7 zX<}?-sv!VzhloBf0SkHpXKLz)yMK&IO*_L;u)n$BI{hmf^clSIS4??U6szaqt3IA* zQBxZ24UW`}7vJi_b?9|CsT}6D}n%WeJpqOThwAxYcmCxD@kGG zB?xE?CmBstZha-%QjT|pG*#kNtTTh}N_(K0fnLypExW<* zhLpxJV9|TW-M6|x#oL*zxYRgbP@q3F6xagmem{r!N?|O>ALy+6aH*wfr=oq<_j=AR z-jgr8MEamM2>uGzIrM=mkSU5*3kgCC2z-TrM=Y*)B!8xt3wQt!U^eI z@0XJE0kMSn`a<+PmhJEQdJ`@|CB{OdfzhqRtRbgZB^wvPCG7s2yEOIYX zE7Z)PWZXe6#F#n*AC9@O%#i|xSSmgbXz9u0ry%aD7(OMF|5uV-?X&LC+EOa~k;r=F zT93DM9{d97wM&$PRIX#3fISg(vD?~QA1Rxf2z}h_ zj3Dla_|r#<$49YTT#QBXhsix6{ei2OQR?bg_8bfh=UnxAxY*ejfaeXVF`UG>1iok8 zbCW-Kk2-5>Umar1NKWPgt}LYaz~}~a|5FEU#2qkBfEn1$yTAhFDS1JaSTlsN_A*pN?o~#fh0GM{&8OK*27_ckDx;*2i*mZ9{9xjKgr} zw?DKQ5TQXMB<6t!7;@h#yNFRDcCroiYt6h&iQU-Zspd%O=_R7h>k{Q`2N`uL_beft z5$SsxE)J{Aw?@dwZazO)#rx8KXn5eto%#)$8Gz`SyxIkzBa)Mxs%JDG2+St>SJbT1 zl9C<^xQuwQOxpHNpYp}0LINMp|4QnjC6QCiSsW86D?gvQ z7a>QZo<{HkPKS<)1cl@wocpgE-JySx_gWuV_yCCVls)+N{b(RdMg0v&#Y8*whM_}? zL9s9P=&l{!L}?@$nVMR>D4$MPK4_EjI~gh=#CUqPN;MxCg;|&ODlymyAy<}#M^03Z zCKcR4$h1A{Bm*63^ZIc}P2fVKHdldPdV2bENlNS*Z_Y^3OO%Qk>x{DTd-85{FPOgq za=x$ni`qay@1YuE)wJCI)xL#vNh7_I*t95!pv68^`MF#eN@;22le&cToyU+)ysb@b zY?7Sq#z8!8!cW3XD; zmb2&0`G7cn7rBysd}L|NZD~5%7tE;EVwJd+2C^rdAYp+_$^0af_+;1!beQVCb;p%o zUYRH-MhdvNytDI@MK1y{wDNHiS{74^SakL@YOi!zWe=wHt~ie2H(r966KDyNQrn9( zzgG;vAFsqaA1bmHI`gkVCgmL#_u5W<{cs4W1EB~mveUqG;+BRxS`wcDoG+>aG5MQSB62 zqnt-NR_>sFx0k{0Z)3VnRV+wSUP2%dX^_=;@xV=#OH$^B0Jz(Cv}c)mXGaMX9|yi> z6t1rMYQD#N9emo>=Fl~!(72JkHJ0@i{UfuC-TzsVC zlHVast_mEDbf<4=>?V8WyOM7EyfBYHkIVN18xb^A96yH zq5W37mVa0^vYq^O&{uA8C{*yc1Rb{*j1h-Pnk(C;=Bo zFZ~Jdrjg&_a$k+k~7g+X@EE*P<-PN5hPKJDQr_H&@QKl&D|&fQ^g` zv8``hK-_C50SbtP;9Oqdu~@P~co&^##AgoALb;=eBv`eLun*=r_=R) zJ+nsX8O%}Rax^E|QyPjrSwEVowX?Y4fep)C;noxisP(5#+`0Gl3Ty_M`BOPH>vEXIDY z#?L>Pz?)ZIpL7)*yI5oSXk%?`(|lhx(@lQ$dW3z}#b*0@S+u>?YJ|z9=In5k|A*0% z;o%XXH@~>u0!q%j&kg>*B}u>UE%>>zYvRt^Q5N9hEU_t9;^{IGX8>S4!z#|J3E#J) z081v#e-hj^?+`@=!$fpg78w3+{Ml;IX=VC2Ab^@t6PnSQ)`Ak|qq_Ngl1df}Jg-1K za0TRUIv<`58O5sUCqMu3JZEY^##idD=9In^Hmc=`%Jfg+-(Iu=23^s9(f@7^^|uOz z$V&wFOV#P3rW+MIzwi)0GES-8%L@P>d~>LIrslnv?4q0DQxr<&5h&3JYu!Gw2C{{L zfs(a#6U1hSyXr=5PfMVY4XuN`sFL$oeH@tBK_CeF|AzN&fNI@f;m+UDb4=bV$&j`K z41~&UOo=&;$56>>z)6EVE5^d`I3FA&e9%(xrz+qbnf)2`2c0Q79RyC^ZITD8LD_&}^)woQwU; zV-W7Owk~-q{28x)ldfbO-Kmwb-$7^$s+~AE&Y*8bG*`@LW_1Gm5od^3t(ADevI9jt zQ+YlBf3p(Q=%P4vyTO7AT`DkAth__V#4SI2G1HV#_=AIayimz+=e1$zoq4d40>6ek zM@xoQCP?q2y^ZtM(l9jbCQsTg6kH^_xM^=FMb2X|eI=Oh@&5Ko?wcL$g&#lmTw-19 z61kaQsC>fTIjf|gQ0lx^4VI>O)8D`u^Zohly@>^pjq6!-vI1M<310?viCi&m?Eqrl zS{!?`dVGRrkTTJ>;MbX}ffzbOMcdW4c)osF!v5NwKuQLKhFG9==D(0yIKKs8SRI3y zRGXc@RgNdd7Mhwe7MPE#>)WW>)-pSrC_ekx#zhVGv9g0XB@OHlQqGIP@;%+4+zXf? zt-WfZS3WX1yV~0MeE1RB`9K$lLMhd!tf)!KZ5g=U#K6@pn?Ika{OLsHFB{H0WH^3GaJgR;RC8zG#nw(`-4-&t{o{I;7gw*zEv76&iJ3m?d+a7N(JqxZ_ zS_u@eT7+DW2P{U3_7)94=eU45AJ+U%8f=hbs~Vc64)|MXPae}mWR4ct&wh{YsmEYZ zUo5A5<~1A#1Rd}edxLg?Y!1{Wa8(1EMsZG@O0{uuO{4}GOSgCtLp{df#KkXGrO*By z@LKzP8wM-}sU8G1q)UFticwPD=%I(2K9BQV``%h;I+8k=HLiY)i0qieMksAIhW7cn zHf#QXIF3+)OsbmH`g!+`)K7N5s`lN~vL`AWS2h!~^yt)NJx+-zkK5g5fKfrsZNzv;$S6A&k|p$eZD$g@zBrU&)O?ce)_w) zPR406_jQY@Y}<~itO9yEmY{q5r|p~@rLV8#HsxX9oTsUo-mkS?uiaXqbPn)j$hU1p zxBYIMw9=UZDH^+IsqaC@e{*{dE>rMubNqF7F8{>!_f^I2ZXF;orKRlw0(2O1h*~nJ zz;O@S2zWpdD4Hxc#p;mwapCs4bDlmIY<}lde_z`>EGel2gvWH)eRFey#R!(Py~Vo7 z@bIGwLi$}HDt(87R} z2h0fb^KA0+Z1wcMz_;}Fq0G%yNl{&6PhLw;n(uFcJ$<=f6!r1C`N-fU>-nkCN>i?% z!f5Ly52nJ4W^qUP+MW+H*bpf92-#{no%cx(70uDgKZ~L9?{{n;VM#)AvM*RpfM~>h z@ae%en*R}v|3_(|yxgLPb)Emd)V~aJ5DS|6?-0H8W431CBm83<(1b+oA4y%hW&z4A@X8Sr0BIlGa#m&6j_O zE+_EeD=W;Nb^SZ;^6M3f1=?=F0Wk(mowi_Latpg0GZr3SFdj-$`R&MmuHs3K1G0PGdCx<)k3#-Ns)rG0>PpB ze9Qxz5`1K|a;M}=L6{VV=D)KtTCkR&0lPE!$+Mr|{b2YQ$fZ z^L{T8{g%uYczc7_qFk_S{+abgN`p(V8JtpUT`ii^|TC$XZU2gN=Gz)*tHj<)yuvpyeQVYN_nV1KnuV&yJOb+l-uY6q< zd&_j&&l~sv*NiqDZg#qCt?&JwZ3Nmd;1B0PvveH$M9!8SO0-gO_eER7dx?C17Fdoo z11W1i^XcQqNuHk3jrdDw3gjWA#Gn&tKI)<4OGB{#L+6AMIHKW4C|zrW8FG)im*-w5z#PDd4nF-|UGj%`PdIOpFPWlr{|+{7 zU^PAX*#-6FhsAojH19=(yU(@7aj9r%Z2is6N6k2Kb920?dgZ`Xc~h>Zw=0(=I=899 zM&c*wswFz@Ew8?lzgE($HHk0Dj@)eUY(lF0fr8pNw&pE zfsaZxHM{f#L}a}=CxdeD$y@?Ic?k2ZKlP}nc%38}2-eZx-;7wh(>>m*o87p4OGunuBRn$$YBfS))gi18s}tXiS2&^S*tX+ZtJa z+BTe|>H;QNjl)rGZO!iq!0&oBTl#6Y9L{^~w44!)#oWbKy=ECuU;-pOAD`1x4QoPw zQSjtRW6TW#HL<*SD(p8-CY#-30$6LSW=ZpxXgQF8s}Y=MTZMGAr6wH7sVY;MY%J|LY2Dq2d z(Qrz>G5`$6hJmcblExFb{&N~h60X~a(_MmvXuLziT8z|lT}V9D4JX!E=+=HG-=aKv z2m1Vp6LAmCt)R+D7AYHYGwbS0u>HJ3*yCO$2n!dO(ouPNW&EmMIL`6Jywu#$ck~lNT=CQa|o{ zhrngzpBAgfmHpEHr$>f0h6?W6GP5tVbD6*waHoF$lAwLf!ra;_6~hH)dHTlfFYl${ z8_rG}=;?W@Uxq26pdbg>0Jk3TCI&MHsFxR`+&DqAS0}(n;?vN zfW)s@;8yfpp>Fcpc*VwCwZg0dqjCea>3t{#nrT*7fZWBj0?18R4&_$w_d!dN?8Y33 zdD^Xv{~Sh(a7kM18U{j>$Y#AoKvv|OQ?Mk3;A9y{xnOZpj#qVdcUR0U(ZAfEzt^YZML;GXODP6tr#dS0pfOEjs zN6C}P76n({F8K0`wW? z0n>0WZVI2SfTo~{0&PqixWbqNakFDk33e<1w2Q{sF1FJEM7VDq09~=X=2Vy=s+e(T z1_D@c2kGhD3;lq34r6PhflvJN%R6Y@;zusWg4-;-VI8wPOGP-F_FrWT4D{*gQa`G% zA2qnM6V1xcLV`Uq&>qFp@a@!-E6w2aMk0W1rBf6x#g^E7ii?x%NRY+;ZGVo$-cKY!9=jN8Q~ zf;S=$*|=E~A91JVq&|sjZEU?ZivkK?I zXlK37mJKj6F!-pOEjOPF*9;FAbp9O+3#VR4_H>F>pT~GwNr!y<~N+&teVQoYHE_9`#(H@O%_&Q(B`KY7JjxL z8v$A(q-IbCwBsiS#p(@e>LjE4p9%|8uDySxA^ZBZ4(Lnfg@kx}2iHhE1BF_UI)SJX zyUJCUL!U)WQKgWLjZOFtH`pT!(9=i$)B!~>*mLIQl`c{-v9jV_yzsSW9;Bhu*ubQr zxvh;zwS@QHTscI{{)PH9Rp5hk&ga59R2abFZngMVCx~K$Cd{1#oqRs%kJx9Mu8XEM z0p&6o?K#^P560lHm;b!60VnRw-e^hIQVQ+nX6|iq%zM0I(oM>_E+r{>LP5xS4}|Bk zi*0#s-n@wbUGfP2iG~K;xIsZ45V;^sbB9_scu`JHZegf015g|9D%72w4MYt{0vjw% zEhSl>zj(3PePRxl*s`lDsI0TdeXd?&Vq##fLrsy6?p$y%eP9FrF~zpq_6mP?`U(NE z%js;F8upQz4vTTnXY4<}{IfoK-o8NHJvOGHOH*`o%bLU8KB9Kye1oLJ3 zBb6O|9ZoV<&v;9j*kn7}+Tary;LX!HU_Q|P=#eG`0`D^Cib(k&0kL2N_K}sfyslM>NN8uk4S!P$??0pPBw>&8e$Hl;#N?Rs!D#(8u&-Dt;Bo zJ3Ioq__QRJ(a&qG8uiyC>rg(Tfb(!UsO{n(q z_O~=K$xK|^$_uG;iC~G@xHG_4Xxlk3Ff*13qCT=({L~zG?lTi2!@R@MpKd_Y&x$wp z@ZIAHSuKuyn{np~%Fdj~FJEYp8)Rhl^>hZa({jOv+S=CUEMy1(ub~&8775>U#{G`4 z(%4g6hP#b{x45kUlinNR@g>a5++_9bR0TUNcvW<3Z7P7cdjRDypUoljc;~=D@_BtUnY+SR_xKi_U=3p++B98aMGIYKw8Yf zNZZIxXu_#+A{A}g8jY;xmz~vt;6>r__EvZi85tR17Supbn>%F2QIhfAHYNclLO47s z3=1)kmbNP>+@%1w5{r>~3PeYz`p;U3J?4%aKSji!LE2>kkf7jb5fYN?k>AkhzCP5v zU8%5R!PR|%ndxZ)a;~fa_uPspK89PDX17LmDkRM^nd>HaLBR0WhSNeO9boIHT#P2N z{h{0T+w7^B+G2lSC?G&8@GZxkUj1#1t`~as?+{L}?&mUUK%d_crG8+9% zfuWOfe`uSu0t04H?}X_A2EA!wAg9jH3l6Q@{P*q_q?LS0de_zcLtX8*5;GGU8zbwa zM$$10OA)Ky8ycEPYz}+-!jvzxrlv#0#1}`c1@9U zx)X_^pK#ivP5pwG{Xn<5WJN>ZV|7AX zTU&bqKSri%+>^dT6ZWNeKJ(YQlp?3GWVOtcSp2cpJEzr8H+ZN@^R|d@61TVv3^%`H z-FfmM5smhNoFb^Rad(&2$;p{Xjp@7pSsc$bmS|qrFLMJd%*p3Hhh~ z(jLd5U~de?gbU|HU*UWZJ^MivH`~%wx72#MTgypAjjV-O@{ip@XSTL1B}ba2fx`;4 zsGY6@ELlTEJY6}E^<*v=lM`LpFXuYum8E%t-4ek9M>%qvkw2pN>G|lO{0NPfov=^f zFem5ZRraZ>DjT+JCbbNTv2x_0fs)KxV0u0zjI`#ma&o&ytZ)TDO7>Qhw6(dGoxrz& zP|*w8O}RV1D7o(lLOhcbm*nGfp}E;dU!NF=3?RM?$RxdXVhs&*?%i$%NhGM`pUWPo6w%y^nP`3ZIM1odKZvp1{LrQOe4jxJX_e2Q4bR zN#|%@kba~6gNGOX^7cgn|J#et*26eBILxq+M^xItAl)nsjJ$Y;g@q~5Ulw#~N(Ha( zpHRuBQVJAItyN00j`8;w1_UR5pM0Y>UNT0t%yrG!SWf&F$PA8^+eNYKj|Q-0XjsO( zyk3!30<&@-5sz24l^oQ7r`C@efY{Lz|u>q@dvXAUP9SAeWk zdt2Mi>yumsG5YSec*w0w^l)SeQQZXTjl$VCK2=o~PxYikMY$2ccgnR85D>t@)o4>C zY2tC5Q;=6!X0`uPpRPyvHZKDoOZDe(c_Hbk8RllAb!py@eE%4LRfuc(od7a+>(P%B zg_r3Amk4bkd1z{x4YnNJt)_J`kgxdd>9)JUQwl9r^C=(pxerP9k!0c*x*jL0ITPTt4*V!YVfo zYs%}In;(m*WmGDVU6x&i2T$$LTv%L0r=yiPqCxx}CKlLOJQ1kcf1>({^vkcT*i1jK z`kn8w`x6ouz~;OOm(0tj^Mn}o9L($3k^1NS>E>B|dwu$5>)cDrnBZVIQ*Wy zGF22{o7Jjj6K~R@e;gDVZtY+}6Xo|QLF<|5GZ=puK#=P;F}ka#M2JVa+%S-7Yv>{L zuuPr-b_e94oSgQ^xqqbrAKm0#C{)g9b!vF9( z{{Qy_z-X0xmyw#|Ye~tMCkGCMgayKzgkT?_eYLAUf&_}P6)mPiOP@b?hV^d3p$(l4 zCQ1Aa18K5@SEu&Mka4*+Xm!nkiNe;xmcR+yBXEm%o1+7fSJ+O8u3b}ASN~BSTRRCQ z?zvhqVRG^;c*y|3OAvC|>)Nr^r6m0CAgt~(?LWZ804XWq;l%IX)xth`vb}uZhPVH> ztE02?wxD3>YU1CIbFr}%ax^$(q@;*@mj8@cVMl`SdDSAb#O$xbgXeKD;Vx_%$M~ex z9ig?8XU}>K4!+!Z#T!;Tx8faGEgciDk8xXz;gk@4JJTb z!v5z&dFAoRRsV_E=-5~#x$j2$rYT98v@9&-q$ER?YV-?o5LTF&lsV;prAyH_HonQM z6dv)_D?L3JVEihHN$-Lq;CN>%-@w~ZA#?j zLxi*0&LL*p<++zn%Y2xbA$H$&`sR&WCSb}buSQP3B((Pi{JOF-Gnt(VO490%QpB`l zbYP@mB^ePCzI~LqzP?HI{=nEI4f~<%qr?NrK*$QGr|&}t3_jlh`Ja|=stlfXcety* ze1VhEli6GL85RP<3H3tvPB&OM8YsIOgAH9DX*)ar`StcqqlPJyudJ&U^q6ifsjX#` zkVW2mGkg|Gv`AXo%Tc%8P0xcEyeW*b=QD8$tcNNpdrQ%w;B~%Gj-6$eh4udNYMIbV zZ1lUeDt8h|IBtYP*bm*de9OtZ$bI(CsW(aS+1v=Y z?(nd8(o2HvI%x$N9K~-AHWBw>)osXNArxm;jpF&O{m-itH}NE@erX6l@FNxiLh0ak zu|OHeN)I#R7GZn20Sd*nI<~Mtiu_uh4hz{Qpei_t{0x|M3RVr$wW$BSW@MAMoNRbw zYd&}WX=}sTUTxcX{_=U+fJ?%UAJphp*{6C13Ibv#o`51kI=lM!{SQL5p6@oT`sOQ; z#n%7V|J*f4ba6xK*|Y3SJunr*7t+%o!b09Xp-H0?sycQ}z;14CW@5YnLkvR%iB8&4 z)s^7d$qLu0^rR%Pe|V!ye&xR}BgdpbsN-|cJuWU(RVfc1A0HnXSxaZ<$M(3`rjV{= zuZo@Rt+g$B#}R>IEnMr(6C|G4u3q#Z% zuI2rlV6@iSw|(o<2hrp;*MS?YjNp~W7{%D^<6k*&I&IU|P#4P8^%OZ!Vb|RI2REGh zePc1d)OAuWYQw{ zUMvY$x&f*kPE5kzVc8j*nek~_-;2x%Q?o>#Lqvyd)LDyjcg`WV>FL?%SOl4vB3ao6 zG67HIofy_zoqp?|*>uwVd+P=FX`m>`6k=NcVrWl**Bkn|R{lCj2V;{$>HYnH~ zzP<$X50GjW#|`yVRGg+(s7^->o#VXY)NOR1HF9KJ;v7O9(y}gzE>zSfJS7@oM(Y>M&pB`8`FG!>q#R4#NmvvsX)om*YI+dO77?2%S5M7Y!ZY+;xmm^GMJ%ugQ4yV zyG3Q&&4DLyDcr|tlj@Z=ocJ$DL@%&zF971{>dsS%^+mHJPm!M&MN{5Aj_b>3Vv7Er zcol(SUF`6%B z3Ki7OYml=b;9PQvz<2X9E-MpLae-Oa_fr3euQ*#aw=Okw2!HI=v@ux>0(p$lBXd!M zA{7OqREO<924C-!`qxf^gq$nhMQE@XL#HrHchSJ$&O4X*uvk)}m@8xnQ3+Z`gHI>= z%Tg2s&Wks#_N3(vErFs$b!EgGr*pLj$yyIzetcr8CM`{XfWS^(Zr-mk1DFi2^e&c~ zPYmPu-ow1UIm@0P>v*JQ(hB5F{akUF+Uqo41+$kVN4&e$E)>{VP5Az&&;~O$Vz9}1 zdwvsgAOF5(V{?7$lVCFB-i>ggRF=^Ki)a9pCSrng>8Sle{QI3dDP?7(rExLJ4_S6x z4;mq+M_zAV{!|HKcu?E|0-B)X8ji~3u(UG5>!OpOpmwmP@JHe^U%SwsRq(+tfR5NHVJ$J;v;ImEf;L-T) z9LRYCDMn=+h!F|Nmb8JgH_N?);v0?2gC(>l?k{Zws5?V>nB6veK^^sd?-T#^;?wAz zF!61nx%lESV%QeIjs7{qd)yg1{bb}qAs!G$fQCuxpkk_+E zNgwE3!2A(}9xmetQWt~wF(mD0Yh~7D=sRAt8swj&{KW2f&c57qcDvVnXJGY}vdeB= z+~^GflaC(*bTCUJvA(;;I$p737R`PK`P<6V(T9KjHTCA|)I#_{E6-&_$)BCdsmLFt z_OY|4htEmxy5&+>Y>30|E?;C-@*g%8Af&IUY3eU}5f89ZkPYV07t#u`tAN(?$%_Au zMJ)M)&paxPFBOGsU0Ce!!4&}-N4>Gpp=orqRvKki{YN9S%FJ<+EY)^fmr5TccLh)k zUP9oshOB6P?e+S?5R1vQiH*dkgAB)aY;`Xc!EUu9hHLLveA|(;V{wt$qlk$aZ<`*@ zytG}1>A!DJY7$P1<7ri-yzN9)eWm8(PhCe8^Q9w+9LJ=(<-_MqQu_y2p~1wO6ZBhy zClq-J|NBRJ8uAHfLxPg-Q0>updcHE{0|y$;^eb$d&frDcwA^K{n|!A_?}%JYDUg!$ zBEoNPbp0xEOl*i~L;`O`s2w9r!o9ggtrv<;3c5!C8L)Di z8Mlj~a$NKg0rGOb=>|M%y{i~5RqiX#Yixoy!A$gk%x?kxL|%}rxHdE?Ijhiq$_Gh^ zyxwsO^!Lvf#(qyzFL*u0W)aI%w%C`9Uz+&&_C!i3XDJHvRVxHhMX|x79>Qh&)$QD}Nof_UgI@^1_(| zr^n*z{z0kpOvpFe3g4A=x%$Gw!oSO8M?2uvzC2zyH!S@{BGJ;Uu^+HMwei+l(TpftXWVI;k|Wf ze(!#7#E2c=TC`=n?9-;$x6+~xeS6`{G#;)wYL>RF6^(aUbI!P%E3}E)_a!nhU zt}36ijHz@}bRINqC?i&@#|r3$)xqHRWCtv#schf8a_C}XA7i+K06L?+C(Z%(L*HgJ zEKt&K+`I48lw8HP*s3nTVLu(IsAbi%P<`js$=LMo^-r*!$~q1o#c9t9i>m7`8C)YB}hpEyx;x$99~m6Qz2bt{BtPzLk4`1fc1=xyqDuM`C$gmQkUAz2hFw>iU$;fNAPq% zgjR+}rmCqG-L{R6<#S`9=aJP?Za6BWM`dhV>YD36y0h_mXM7fjp4|N`ocT5~v8AK# z(uEfK(v^-nhjWP`A#MZrG)|)*&1$Okv+-A@6%U7U;`R|k>A=rPsa1OrT6xijf1p2- zm%HN8nKL&gstduHO`d`G?Zlmn^Mp zaxq^fcD-QEBtu<6q1GTjeRW=o7<|fsN&>3$(8RJd(V^Y#91h3Ni4~@%+X7mE%fKZ# zM1E^z=^hjT+u`fVaiYb;@i$izs^VAPkIcpm4{xyE5;hpD(jaRzH(_S-Pi?f)$muIq zc0{W}sj(d({cgYGPABXUYeC++1S?{dSP%;cqP;~mm>AP;?`kKXA2Ci#K>Ww?_=K(T zdeA<{y1h;RPD|rUB|O&5w3pBq?Kc(BL8rZ}tEaznCk1-LH@Mi1hI^x+#AMetJUr}C z7Y0k0j0qn~dCpZbG*DS+X>3*sinN3RTCt3LWmrV`NZ}EG&-}cu3ouFh2Q5##-zTe# zo*JwijM0n|7akr+4;59UwWxq`BCA6>{>1RGDq$qG$MG4(AaSn!!#0J~zjBI-Rz~KF zox|IyrV`@fDg{RUg9mDtq0HL5`GsJ6H(lU#ldm-WQ1AM$S1r7DKqLEZr#oV_K8h$& zlxkQ1>4_$+2FP=a=c5lUC)|;QA|@54d3bP&+1Mm@j_##Pip2XvKIEc>$J3a*3XUFtEG(3(wPy@XoC#EKRKU3)BLqaY99p!=zT*<%E z^gkD9r^LfD9g6K z*>_uup{e~5;MUsTdv#XU{N_Sp{l8uKIC7?{&*z7Zw5vDu?i;6YTmQ*#~9VqOLAk>Qca zw2G=GsM&s?^SrXZ6A~N|5)s18l4G_+{;x~}_6OIT`g`+Qn4)+q;35Jd=bW9+<$sMB z8x+9c*q}lfJ$GNqTZbNoc7RA zQHkpxMh!|pU%kZQQGHy6X53KTJ>qG6CJA_JG}`q4Jm&LS8d)V2!PjBxP0CvIe+#)s zuGO8h-pXmXN5yv;JJ($N|J{lQT#l-YB zIB_zVKCE2Sv443#MFn+wHOyBg{Pzb!f%ZR@CH)AvNe#QD)9IgLOs z4-pDsdv-?t>|DnU%h{Rba@*Cepl{D?cvu8`Acw3hg50s{M{htjR|N1F&pi*zF)-TeX5(%m85-T5E>8D@5N z2b6_--}js+XXnpVPE%cP-9#<#m#3wDwl4Ep@PfZJPc2ixZTGzY_HAESzX)9zKeslw z@w@zUmM|+1UvWX5tn)ZA11Fg0cp5hKQW_v;iMI;3z3SMkjUdiyJ4Z=Nq1)&C(CzkA`si7GieDaX{!;VQ9K7vN~{dW zr-&A`~JpuS#?oHTDW1T^w#7b(4P0s z%{pwTQB%`lA+@Bqac7^L4;Cf%ZFlys0>BQD;)+!MKS9~EpbT@Mfa`8XVF~`R2`Dfu zEwkYIlPyuy(ZRj@@ORcPn)!;1G`+{1Hjym`>Kd%7s;aD(4ZMM_#`)Np0V<9Bc?37_Et!F>kJB4OCwn&$f>BbSl8L}5-%dwGk- z-6n;Q(1yuFQJ5Gey`P|y#4p@Whko(`}tEk zmQ!Le39yeT8)zU}zPb}u18ULTIGg3w(8JHOmn+Y{|0!Lb`EXg;kzILoy0?7HO73vp zd+)yzK6~R%sO#ixYp{*R4ij@scr@Lo%Rl-5c#K6p1M3}3tIn7&LQ6J14x4bvfbzC=YBLD9fR`zhdTUf*<&Rds}_)%U- z>k2}~FR*6_9;<3=9FBt$x&e!EG)mdlwm^5%2%JO!3frr~Bc?1Zyt&5u(%!y(#INEp zu?7@iXTH&VMnj`rjgobX>;Dz90|mEEVFrc^Muu~s4N=&jW9P?3q@@1Cc&VUZbGF<7 zDqz>`Rb?xkS_t?^PCoSb^0$@lUa*4ukdX@ZWtza&9PyAwi9x|IJnhmlAYHWddxLj% zLKJe$_GX7OX1pA3H#WX4M=g+sv@B1Sh_bNY=~jC}JHYx-rN%q+2FhRQrjMXoRsO{m z7};}+^W&dvpFCEZB*;q7!rf|({u$fiq8cj$FW3X#uWyf&QZ4WeDS0$A4oLX3v`X9t zi`ksZxeX2HKD>G(;6~=FEG!%=^*}N@b(8jF%j*m1UuXJ;=}6qpTC~9$PTth17|>%< z2(th!E$t@XO4$if4~oZj{WjWbe^o6mB1ULhjUV&C>S&a3n&{%k#bLV3I}-ztyhe|3 zf4j&eVC18XXoD2%mgeTZ1N=I2eVJJFSkE8Vt&fi`+FYK~(yAya#XGRE(50rO4K_6ww5tufrDg}mC$4bqmDYv62ydi*8O|8|Ckf1RRG13P#=!FFw)R08Y^`W*A9RpvVg}N1v)%( zS_;m`W6HHyPr0?NK>2bOntilIwhQioG^^o9{c;&&=MzK|Wj8oJDEj z*9})Z-OSI|gQfy+B6r74)j=jV6%_RiUkB|>ZCCcGiryQvW)3E5O4`+=cI8@NJco_I z@4q|ONJ5iZHHs`qcfE5a(beuN=&1qcgH?7XJ?lI6MY`O_)tYt|rwK{MwzfMcf?^Y^ zVxu`hGu0JmQINcW_1bP}Kz>tSJeKV6R!e*9WUisLm6f|WPZUJDpJsQmm_BD>H@iGc zAW^__yl`99xzZbwM!tLz-J?rEJ@#SjgP9-3y4B!v|Z4)+43>7+dQ|B{{DW@Zk%Ap@m6|7wUy9)Aa?Peou6Zgn6$M5TxUxp7I~T4 z8Y5uu>DV%7?#Rb0kf{S*=NfkX$HWwk*;q%9?rTlJv)ko4K^#5Gx*Bs%3MU|xFaR!L z=eB9lUrx-!of<%dEqZ|3j$j>^khr}WpjR-gXv|2*n8Rsr zS0e0dgz+kYys5pXzu*0!CzK)Z=~fG)-p>A+|9)|HNyiS~?%{AN$CC-{EK3hUREn{| zcZ(ukR4^N3($*XB_WtILLWzV|e`AM`hmeb92aSB~goJ|iEKxezN z;hh&luY@k{{3k-mRWdg_FP}<@3wDoAoivuRG+>)`a@J>7JXZUJsY3c8O=!5K8x%Rk zKSkcW$;;8L>0QWS&_d%A#TB}4Sxj`KiEItjMU6Qv?`xRM()hy_3}!$|%G4`0EG4P{ zz#;L%wgvB#yll$Gpr0FPN1hGj!-L&!@*CUxCPgMz?DoN>0s+uU0*-;Cb%6LRhu^>4 zRbxGwrB%m^S$0MnDTTg-m8KrvxI^8GH89OBd&aRsUKq6=bN`|`Qy;1&a>j*Nm#x| zm$uS9Tw02Ndjjq)e7vw+n-@ zDXc9wJ}C3Xc&YO$)}zcxdK3oM`uF6BXZt(=0uA+4uU|KU<)bt5MbN?F=^YbR!eM0? zsTF!z4jC#lGR(0b^N){o%%@I1zmDRb%nJ?S|8PtZj^aC@8_9+wm{=~lV&7YRBUup)Nr zi?0pQT9r1Er?|-%$(Pa9bxBCg*s6=}CZKRR<92hJeg|eJy$dykyHa=T*(ik@05coH`|ZTz9c1OemZm%G|ra9ng`3>U#tyD zbT2Svbv}Ot6#tdJ1Z_7Q8eiq>h@i`J8emrpoX!^iO@4}od;JOD`$0KXePw0sN}CGV zPAh-x)AuJwk{lR%pk;JFe?^gc+ID3Aaa6a$h7{i)PQ)vo3^CEwBqg%CNW86RY8;vX z#v(@@lJp9n+qaL8%R#mF^yoM-Z+KPFI0t@LjNv@eP$~df)EG1wu}NI1*Mb;0&A1rRuhlIr z>9-(_jV+m$jt&d^8rj;MAm+_2vIQF45zfNtJFQ^GFEPH@@~r7HhIC!4i0TguE+kYT zY!16!d6VT0tZe;jYZkB!67U8%zO$+u>;mtC2i(?yUEd&lXKe!A@i+g=w?PRB8SRSq z1R*Vi?8%dW6P@O!biSxUbo;3;MX?D2jOf=PEHvu%GarO&I>m--c}@l#Ro*mu((c4bA*OYVg(C} zG_4PNTtOh#9rj)lK6{X6ceH|1==l{-NRE;lR7wrl0K-#KtQswI=Z>SiqT(D6Kk1tw z=yB$8{O25CMJyb^V|gD8cGfFHPfxI+T z=TfGW+;=o+z0fD5-~ziADVG_V5&%9NoB)WV6y3Dr32Zp|`c|_i=^SX;nl}jvuDQc(Trq9}`i&gTcgs z@!f|VY5{RoTIDt@Wiipg`^#CXu5X#Gv92NY^;D|@u7pUjg<5t7gvrBnqREjR&TYO_ zOK$uXXFWAFRVb@XPYKXL#1qiZZ4@jRP5ykM%}M`&{C>B0%!kv7gLEa9JxbSp;n7kK z@9DM4=86OUlm`N@vp!+&^4sVUmAUP72qBkDW+Wi-85si`(43Kqqr^;0Eu~rd?&vFz zs_YIn^-XNQr{s?t1D=$_WIiFirOs&`32XDO3`bYjvla|q=k1TtnHCE&GQdr`p6&BN zLGfBdjqUY;vXGD|&(-F%A-GDhtUN|jLX&YNKgUmP3N>|Lcmjyb=#cHB$nMEFETyV? z5;8bQ<}_$EFc?ZclK{D@a)}Yn;&b}pVm7uSQJHASNZv_3Se;ORcO6!3Ut?@J_9=RUD8w+8(Iw>c3U48Y^}EIID|~AJ!3Bas^rv_d{V!(`)D{b6v88MgXVn z)n9rwkc?Zt(Ul&DRtZ>-X^)E7WB_yHL;hreNwb4>f$K=^T(y{phBNfxVthz|HN4uf zwVI5Vj1Vb5e~2qCDe3o^UoJXzW!c1XymUHZbOq9p*SOwIFMe3FzjOZG-;X*b1CCoF z*7ySwH$~T@km|6Ml;gC2S7qZWA_K#pyU&^qy{@ip_n4Y|aMU;4z1by|GC08Y5>!s8n!~-PES3*&En8 z>{W$?+{VJW6_ImxzXzxX_+gtO&y^0y+2-Ft5<&8-FO05`xK8n50DivW&#A>UC0QbZ z=KG4-yjNh3Qqok!LcqOpKvIVBa#Cm9De9Zk@Q`a2)y;tK5!|-7ZhgNFke=Aka85w$ zpR(TAvMPT2_2r12LD+GnDQ}22)yqZ|{D)|j+t1z!DR;2i=*dd*T4;hoZe3oNZ^!@8 zHZxB?$q$z$^ud zdmlXTu!Uz0m-+(541LdKf-wRve_IMvk8z@$_WsCw4HP3aMg_1{-3DDyKnvqD*X>h$ z^xTbp81Us4{|`SXf>2yl2ZHtF*7r>vDt=p+@OMBg-NgY@Tdp|b+u}FhYKChAl3alm%t@t zkD@0f-C8iK=>K-wuE{dXED-Gj@bKE;KV(y9V3UlzB9oqe zgon#wv{FLc0O>TyXJvJdrR_l^e0&f&9i1)T(Idafn>L%MmBl^+KM{C=9i0r?j4$E= z=h(#0B^9H_K{D02Rip369&8p5wea)X&gOpeorYyz9u_ z{p^RTq9Pq7aQht9kP2 z-ao#CO3x@Miwx_w4Ks?hs_k_(M9NcA_K!h!#AI%kZ?#88FMX$W0Z6`V?Xtk{L$mM7 zsAY8DGOG(=9+;nJ*#$zTi1@HBx9#_M5ntXYHa0GMivvGR9M`ie@TNWHFRX5=2!Np& z+8Dh}xDE5wT2-tu5TLZd-T^0tt73?~$)pO?m(XP|iq4{9i}$CSGN2Xi$=|NRlxc45 zy^hH$^11<=kpB?`ITjfv|2n#j!l)@A$Z7G5(Hw+ELkLpT22HoQ~2I|B=%aS9@Xl$7(fUfEejnaGvPTqb5kTQ z2(xkAt$reQ(eC3T0ot+dEJ-g2HwuU?4|%g2u&zx@LqF=cX#h5B%)O0{L2l!wMghfm1~8rjbikwL zV>#j=;VB;eCgV+Fpn<+1%?U=0W8p7b@j0o61#sIe`y1m8M4Kd>)JF3iks-kF1m(mr*6eM~N=84LwZZim+ps>VrQjguox z^w!~Bf;crGk5XH?itHb97(Lr?Y96$nba8T5tJF(D$8Hkq7YxhBRy)t{r{v~>ZN%Kr z99m^{U+~luMt_)uQP_H8fQOL;0f`z4a@VGLwH1Y`K-MQn@;g0i??}bvD|`)gmw*Vr z1pxDq$+n>eZd+FN+{@QCf(%(v+U%^ z-|0`C#>hL{gLh`;vPs=AMFPu@)HKV9iSB+kok4B5A1=aXG?eOiKy^dFuCA2dX&`r) zv9*=%^z;*a-lF-vrEIsRMH?GQg7#@`dT#dfzeN?Wuv-tNuuVRA9i87Y0L;ojj!xTP zhEz5N^ka$Q|2RI^c(GNv@nu5{U^|R=;wSfg8zefLuOYaoKVQEdSGhRM0Is@!6;?~s z^s@;Ek_W!_GXqy~F(J0Dyu3U`HfKE`wz71WKGnK89u4{BnJX-eq@@2&*Zyn(yx22; zxY**mrSFCFq|0}8C(3aG35*UVe7I}>gArfi)DqStB?X1m(TtOH7F9&=Fc1V(_Bhzs zn25g6RotM31ae~(<~X2pAvoN^ucH_q)!#&195SiHJliDZ;TVN%x9jAcKj|aO>{gl$ z@}X>_2zKsTXu~6Vw=@v+-3L6Ri{nY=aOhIQpi=6-at^ZypiTJ=MUqX|7w90>$Ianl zdAF<;daW9zqFr{U_EiRcCTBPZ$QpCEoW-}c6gk@T_4ayy^C>DeOQ*14zIy8x;gLJD z`t#s)$*_>5>>3IRwC?`g{2U1n3Kz*9x1*dfJy=z%zUZR4dbJ9Ysq#YRx2%f&K53(b zg>83IV9h7yLw$G-^%PI}A`DL5?4{e!M6JM&JIay6%*?ttgtm-})kbk7=A@3cwNda; z6Ckdq=-JuXM~?`fYjcK}2~n2Oy;Wh0@147UfA-OLiEX^jfA7X1hgZPe$6Hzs#3oh? zocbf2!DZTtiUkWbx3IlF^Id0Zao;s(LGn4rB_#0J{Gq81v{y2g%D)Y0da zEL$z?!L?sa{X_@I)JTIqPnVuaeR#=MbzHX!oRV-Udw|DXPV5l=lN-O=aH#{_ z8Q;sx&48SPx8v)7%h2r!38pS59iv@xx^_cHeQSGP06(g!ohSz7k?E2;3IDO%;bMvV z-ZXTPt~g^Mn}@@aHYrO3zdpfUVk0qOiB7QknCdrvC3Ra2!1iSH4D zscL!d%$pJGhn<|*Y0LUkX7=YCj2^1v?K@ZtGK%-~Q=)H+fWMjsVN%ch_>pWQz&)9_ zcFMXe1$A^Cz9S`{JCw9k*`=kQt>0K^kZ-6}}phA0C{FJdHnqVxc0Fz_o?#rP#AR_5r&!}l2!^)nFhOrBK=4w4Cc65G?0FD)<- zd{%xTz%baOq{}JmZpuZ%5Nw*8c5I1-gn(_Y1+G0 zHVUV49y#XVUc&F}Z19j57w6}KjA1A2vSx5%yZXBmX;`VjZN+pFO>tV~iy7tGWQ(lc+k8O14FlJ#BgEN4 zH+lm#g@E(2j+z?GWO!`KrY361dgA_xiTBW+fp^{h-az=YiQ&1?A>rRb<1&P;9B-oD zypi7t6{LDx?zDf`tNn(j@`}UF*z^r8PhmXbk( zKSUF^h34k)1<5-h3O0j73*b^GEUVrn8vuWnCI`QWB&`kEQh>}P`|t8+kC#j|*c^86 zs==;cHQ~N_+7N{=xuB5suybsJ*Vg5#M@eHQ(+^!aF^SF){>8o;wrqeDZmkYsUdAauZ_u4u%NzM{NqSGZ?as9UvX_GY4QsI;Xul>>Mq9AZAayY~mm zLyI71K4E-4Q9kZaxRryIjqRRm0UUY2b%G2`ovRF#5=m8Si@2XOM%7oAOm^&ff$N4) zwv0U|PBDS7wk2(zr({owQ;8!Qo#ks85D zye2Oh(aj+(Z~Y$fgqX?Qym`CAp|^i!?`rKaodr7)@;k#E#)X883_kmw#bSR4zz{D% zw2i&g&JW1Tm@FE|rLZ0Yc&XBDV+(|!FlXfhA(KDrzk+9WEey1B{aB)^S)*ICM;Soc z@>RNQoq5Sptd3Q?=tE>bi0Z@QbRpp)tFEadhA(f!Ix2GM00diCJcml_;qVM&jOd+G zpNH3QrV34yqxYEKf&D4d?)n)ufOpqCUtM*Ej+k|T+HH*^k!KLaK8aD38D)7Mi|=$6}P0;AJ&sBSV#*q6 z%hS|({_4_-aripa@nV-Nu-st_=O%_{v40kZr%00D6E04H>RjuM_2eU?^Jp5nK`We_ z|6OXjiG~%z04&Y@w)mCU429xKuzjn{9ARK4x5aoYZmXatH}v^^ohUYu*cnx6S@qr? zmdVC!sZ*_#jUR-EW>36x+nsIr)Wj%uPx~r1vAmYncE?K6*RSQ;894%4kS$?Q6Fj+4 z^A*3W3akZ3@AdQ0GWK168}mc`<|6O#Bqh%L{o9Jl(QC%h)0Vcro4c*C^aEo>>P{_w)I9SBj+_beZKk<`6AYtbw^cfhN*b3|}h2-cY;TemdsLW=vRT z(2OpYI?vE(ERX$D*5|=zS=kBD;&Am|7SU4<4iB&sxMH2`uJc>=EPl1zOif)ySC#WQ zZ%;Q!27EYkR`BA6%Pu!J4U)sm{%Fj?s{TrY5FMj__&l0JI@ecTR#mfJEJQaU>zA;g z6}R>!Q<7yb3G-50Bq1bld=-67!ejq}g9B#v#O#%Y;NTu}j*SqhsW(E$(h;1_P`nOQ zCyeLC?^`DzR98c@SZ!Kp{oYJ3~m$#KexHB5!yU8j95DDOnAH+L@h_Qka%4 zj3J+0RmFh;dA6f;pWD-27&7Gumb8?^|X26_$^+X%y)a z$)4?y330tdoFNEqDd^)y4(~k_Osa)KFQ`Ap{4dJD%IJ8u7C+y})wO3#*xbVMIjVK& z3ZlE}=5#RtC6E&yv&C5jD9Y+GznQN=rzxor1XYgZMdO2>wzXrX zAn-``D)W1Zm0kFq)xI_>E$t>!88+ogedE!ih^sE_re?nbi1_Rd&Sw@FCJ@ht9b*cr zU)iIDE<49_8LH2lo7)^b)`lH759RAHv$cyBQz@TaWT9i^p@8*}E+ncm_C-*My0*Cd zo25VMEc2IT)klFJ+@S6NvT(R;&o8}FA=Lh4F|**KO^)$pcqV-#tuu}#l*2gF5Nl_{ zJJY`Mvb^L0|y8(pbu651_ zRVJ$fp|T_|?2x@5AbJPl0G!s08cd&-BLf3LF5ysmAX;6KE!*h#L?Wr1&|wkryw6L- zo6BLnI#?3WDyn;NLpEpo^vkBo0$j}W8pZ$q{E7m#MVgA4P5(1e8s2T7MaYx-+^2Qx zX@m`}mBOdlDw^`K@ZDJ!cC=6IM%aQNdum~+GH zGn{6lBWeoT&)$+9-FMBKTz!NWSLK8UP{itNLj#r(>+$E8ce+4_bN%{JW@dB%`Wz5{ zXpCr&B;0YA-Ltb#L-8PRli08H`~w`krN#6u`dU~kJw1I!_D|rYgCsBJ=Z(nbS4}OA z-8Sacm#tg{x52Uq3ME~JuB%*Trxx7W>&st~`tEsi^H&ALx}U9gE!jXJ&4NqcSw9a| z2<7f_UTzxaQ>MO70jABSv0u5H&uXuffiVaEH2pEp7e0n_b4U#jJ;kr`l&_AP^!?%2 z5!h|8Z~n(+>uOzBzUzTYv-ybd!6TwD6G+F=TW75;ds%;Jp|2nFulPR$fNPn1T zIOZov`|+c17|#W8#Odh}cB^s8V2jbBB5TmNCGT*Kl)zx??=20xpih{%aziN!4~0b$)!MWpE-h{O>J9F#=-5&nDpFAsr@rYq zB3SVui6TurFf40AP+5cf-Qg|-0fjL{Uq@gCz3K&B_tKc2aIg%7o;AExrq?Y9txfBb zQ{8b|DtDR81L=c5p}@)TRnnOv|JX`!s{!YC4WQydug>G$)q$*%K!rT;sO5~WBQ=`| zXVkKfiwqhLR1}yGbFJex1m?isa_8?yY1E%);eFlEAT4HUE>mG(^2 zC0iz3Y4+ZM#|88q=&tQsaCr5-UmPhrD~ien*^oHK3*cASqogn8FwIdreX*1u>>vP3 zIgIleXEg5@je8F|CaWo+lEhW)A^@!`HdOYeJs8f>!VUIk)YR5{KG$jUC7_uLGV_-r z{VK+EoHKQ+9-j9Ck}M>Pfx%P4;`B0(Cp$pk_%LKg zMM_K1$_g!6B9lBTWM+0FA~HfwUOwLpLJY*Pj^Wa}=ZZD!{ZV{ymVlgvuIc}&MZra( z!jRDeF&E&Deq=WeHfbN9o=>7y@C$>1JNbG{PNKNDxT%wYl#PT23MCABYWTe)O3V>Y zpAcvI)EvC$mjneDIQ zbj@S%!*3rOSax1Pj#i3D9kw=rDxhjY;$6Z>s5J8-cqAu>h!`&}Fr182_DnX0Gv8P1 zH3+-2)OOtV#en1uZJl2eurnK+NHUgLC12YuDzZQVGs&Ivit@CIEMmV6-hUm+q}ciS8*FVbGbWfCyOTl-lpbp*qG_*+aNFn z;^|w$3voyUuD*neS703d0X-2OBqS2>@bTk&?~hOW0Vo2^%H|zjJbEg245ZE7qDEO+ zexl62-G^lcOWx@)!d|qCGHqF8mz|=crut8FXzx5*I$B$XJANSSSED{;*7Gu7DZ<9@3 zZ~ogsS*N>X#|4tN>?}TO+c%fmuqH-$S}Z*>+16GFHmAC6C(;A#>tQCYXOnMsEyz zL^GPTPtypJeng|!p_&4t=9gyE@Q&&&zs8lh)KrVh&ZdPh&nZ>w%I0y9w8KAHS^J3C zySUjI-mi(ACs4|M(Pj!6w&M4=HO=#t!rI|q&z|dE+_JMU1e7p@X(}lN{|tKc@ctz% zVwb$Kva-!Ku{C#tgN==u?L}~CC`g2N6Ag*@FF4!oB9Na=GaFcLmrZxQ>OGs$>~NV- z1^9#EhQTI$@x@LMo8&m;;|2uAUBk*jIk_3w4yC6*5{y%jQ+s`#!k`5mL)0t+*@zq) zoCuQY?{4`S&*O>-AMqNT^b=bZ!(1pNJ|m;urTz64rk;;)ErbUI~1|UvQ#=K-C51K`3BfTRAL{EPu(M zOZ@??i_P1X+y31>?rTANmZx9!ba!DoM{0hVi<;d6CXvf^3M3yB&aUCCZzzgRK|%{9 z${w3XQgcR}MfTzRhN}&9B?R=No6}o&m@!hm8R>&CID^3d#-FfIORwSKD^`u2m(wYJpwr;POU>sr1Z{k0B#A0$d3pQqOfG|+gWn}#l zCsRY7J$vSQ@el8g$>_mjbtcD1ztDzB7X1?v1=S_fSAFu+zmk$D3-LrGg&?&t-?jyn zq1wp+pqhZk*4Nju8&v`rvMWoNlbr`nDOgsQY@)tzPreORh;XC9zzRJKY9TjTVcJZb^J`up zT(X5!Ra9g~oL5{Hk(z^Ef^EkLF6--)MlCjAM5s)+nnqu@r0>b4r)=?%SpvnJl3g?; zO5&w5hNPRL*{j5FRg;0Io%PS z{J!(D1T1Yb5;Bzb0oN|B0hR!ao_dj0)2QQwLXQHJZXXX`hb%6*Ya9n=q1!69!l`be z<7>$QBY*&lEm*UE)r5q!AvP8xgta4rOI%s`2==We*>Ms9`_$gK#_x~+gz3tK{rF*b zHv;oOv1Eh)iYFvQsk2%aTMi}&iFU4!76q{$D5)?5Tm7GXpMbBd*g%(@BgoKIMNfuW zu-exVosF@2cTBx51WHAh!2Ful5}$$2a`m5 zIJb zGcZVB)5&u{B9`mX8OxxZR{Qid$RGX|jfP7Nj41nxr9!V>AM8)RAmFS!sdvt42ltwx z;ljD|r~gk>3xTiPvYyjl!gCBwY<>Opal$^T+ATP*0Tl(>V`F4^_}9j>dwudT_&|+D zIX;$?3nXZ(J*0s&I^+=W5eW)%zrPQzlv#o@;{MdyWt>3Z{rJJ2;{;>ZPX9glGhm1B z*dO;2r!oW1F$A0f-~W>#8D(+oCdBCsfDnn^J~`b=S90Cuec;BM3nSnuSQ>1)4hlI+ z=x-k>m|TP%(rjMq4|t)AhyVxXEU}GO;J{1b4Gz2bZl$B&uv%h)U}LtRr{ zN!jCqc7LsZZ7PVRQ>Sv?55^X>K?f1e#_W&6q0(#H!e>kN0kEvbGQ>Y+WGi#xN-b6ro_KY!SajLJeb+n{3d3WNIG18UOi6gzS^hh1ivmAcg{`ZSP%Sy=x zC}trQr>@Z2I0qg|t+MmFaJ=AhBbc?JnAo2|h(W9DSLzAd$@}5uELcXMm(qxdwrb+! zX3CSZ1F^xN$|i%Yu>9s$#=Bbd92{}&Mp!yp!QZLO=9QtcgE^X!pLBSrxdwoAG@3y< zO&`R|+T3m%kI4%#gO=lgx7}oMwf<1}65!P)oQ?bC{Qa-T8@n$x?dKSzU@?GfG>4U* zS0WlCmFkN4_=aFy2O&AAqEzMOQ_6=!f)#TnN>dXPd2L6WQj7OzU*qEwQy1bYJx-Qy z$v3n`&rMueTZeaN<+rJLVQ~Q<*S33KGi-T8x2_T`k!T`pY^+GUw&qEMz<^P5TKaGH z)h>DKf%+l2#h3AipAA6z07b#4RfbhmvXAla-an>%%E`u72llx~%C#IcB$Q0gEV}dW zBytQ@X^Pf%xTh}(MiWLErXC3^ceh=9(0qv>X{@Az;LoZ z+HEbtRXF~J6G0tQGHX;(yblS#jJnlC0!AhhW+HH8UZDAwiEe`09}FrW4G0g7spIgf zkb}pYLaiu6-$wx6V~7RG(I`0s3K)#M08?FX+=$Dvc+OL3y#fmUmG!!aUMjf_w~K#@ zQdH*CrMO@hqONvW$d&-*E8IX&@WQ4}mRd(0NkDgBcRibvTv(h({u37G zbuSSxM~Djxqw2fP!Ng}!xS6l4>0qG_IarmblXD>EMZHvHuG}$O8!6;3wDtw%D7e8T zG$7tEq@FV2ny;yMC;axjfsru{)o7>@32i>8;BkkEL? zz;Ex`gI9wv=OR;m9Un^SIb1Uq3&5L$zAfbY)ADZW1L7P&n#!u33GO~TZegrBYWnZn zUx^2kf0ZcOB-<3(;pS(~?SvE+-YPe*|5j)I?cpNjj%&!?D{hx`T6NfG#N}T4)M2?v z$XM|sZGdMSFh4;c9V#d)%Hw<&1OA?w)SI0#tN(h21)kQYX9+>REnVw8>@G0>>aJ)S zsqDiF3?~|(b||cGn>R`zAv>||ConGGc~=Z3+UzExX$;=B{YjiwUV$Fdly>u)|2NJ%wUDlI337?KHqZPo2(ozs6bYsu&yANeQ?)8Nwv~ykPZnNLnzz1Z~ zxyM7}k_cGdE1_jemKT+f`2Fh(S5Y9d@%(Eyz8KO^$D1lwRTY&lmt_&7pTB?K_73`n zDxd;8W$}9x!->pv2*-xT68l%OY{cIDpMn{B)qANo+1Mi3*t+}sv2WgPZEsCATgj{b zryasT?YKV6k3ym9sE#RA?ih*gfC_>*wuws&5b7kxjgV3&}PfOsTdJcmDn z+Y|@K2WxpI=eqzGaXS*-0YC?91#_vP#|z; zb&%ybFD*5PuBs{z87(_Atx7bl`o)V`-k`7pp=fnQ#VFWs?9Sq+-l30wft1yiZ4yII z??MNzCEe=1{E~t<%<3~u<#KVZ%F6h)PD0SB^Xm)BPRbQNBz>KweoFJ@rKEh#;VCRH z*U-?2bk+tH42WF;V-QbXSe-=>R>CZBp#bIYv~rYCSZIOBdS6#yHvU^LUn!m}PBZP_ zFpY;#Eb1N;c}r<^DyA+noWsRuvAIB!dI1jC&tF8r8g!{767*1p3_SQjRybJc4q9W= zXom!bC;tBZhkysXDLXd`_xS9(^R~RjX{Z{%Hes*bJ?@#EUiI_hh}bxB3or2; z;tM1#BfYkhDIya9G)R+pNVqKfkfSoY|GQtL#bl*(Lu3{(UQm23YiWYQe-Xs>!NF4V z#ATu2(vBFAa{+EFaMe-#at4?bylr4SLUfzxQPROw9)O2;k`ep9T42Laf>ZN!<4B#!6xHLy6m9EZiK}y?06rBuXiq zSK7pu#Kh9I^ABx8dj!8{e!dYB5*ou}z8!nkBpXv09k_bcME@heY^ldo}}L@EPraU(wT_UsoWhZ-!gyyBhlJ4<715EkLEN^n>QR1 zYGRYEzghvcf%zrg$TB-){+;iD=4g1L55gA*J!5ljw;exo?CydG>ueR{sun$R8- zo1af+9i-U}Wtt9Kf7igK=CC#vOW5Bv-Sm4GnOd5=ida^k5ytWtt4d3M;hqaA{)#Jf zvhyJ6cUBhVD|A-#2TWJPMZU=s2ycU+IapCuGKX&y&YAeU_0SLoi-~j zWS8^1o_jn*{Z3B@gpxD?_flNBVH<*{QRz250ro7a8@P^qj54M->*}xfyr9pkY zU3>^m5RMn@u6vEXvw4ndv!}g$kC=01?W9hjce>bGBP%;w;xe)mq>~Ujq*;%D3dPqB zjo?{4Jq1sC`jZYINFa?0qB}Ka0>xLi6Hei zYEbp%DH34lfqu@Fn5J?Cn0dCS7XvwxlZ7YUAp~#; z;5E7Bga?NI0E;d@P>brlR5xW9<#hq!uGoJaz`Q4 zUA_Io)h;2M)$7!)?C$A{cV85vAuKq?QOOD0mjSuv zPZ+=*wc_N~7^y_0A?nzb|45OK3^XZdcZ^M}Q~=}s+diXa>rsd7O&`MHWoR_W4`=_0 z#Q#cv)%acu?GScft>y0n{+iHsqmXot*IIcTkXNf&?YyRP&_{!S#m@qtR)1m?0Df;-yE#tav$~M37M=A^? z_(~XY6V93z+9(5j%J&=~X-tRH&68FsBqXHd&5F)Fto8`5bdSkP;VRiJCvX%49&`nv zlRS4V{`>1*(g=chBI2)KDM?Try%%s>CsYTVo_xG3|3i>`o_9++JkJz)A!h$YL9Gzm zBmxji^NQ=qu#L&<Xfz#qjCU8Qr6Z;pZyKX5u1B7LPZQsQ3Uwi?I^ za7}tKgBOf-+ZUvNS|q%mto2kCz#rP!TgV0FQM0p!jvH=byJ-p8#V#k@Xj4@PUK7`_feZddKs0Ts^(+VMmBw0v z%m`@2AYgNKVcpu+W*cN~#MOIh+h=aDLCS=Mkawr&_YA6nvWE-#{+OTXDHAIht;(xc zXw`sh=q^YuQjnG|GB-A6+>o99>~_(;(@!_wyCN_b@frspy$I^qLE5!j%xYVST zVS`y+e0^C$OF#ImV;uZxgno@K<1MM$nl1{~`s;~oS5FR< zl;y=`#7`itP(x!L=6eo1iD%Dfk-A2dt z^!9TVTDSh2Svcj#{Q6b6*j#@yGa~(Kq?wJGGf3;x%UN9-Uw5-PH3d9+aR1)={5d-V ze;(k(tb_nUe`t&7;CQBVDx@kW_xiW9&$9=HZ!JuQ^HG2bgH%Hxi>AT14h&I`~kQ>7*c34EBsMXwxFJm>wb=;h$!;{${N%$kTNC`fm%t3v12?8;Rnk)<8t z$Kl8cY0)S)D*$Q&Rw^iqaRW7sy7jund5b=b2PcP1SV*o~=I8qRX4DmSHnB`rhqBa- z*AOB)Uq5p|irj|wYFSK8X=pjN*D7$fZneM%^qM%~QLS8*Ts!<}#tQ7tS7|-+5#V>n zbWx4e6xGf%{Uwf$J4Pu$BZ9yha`;zN*o7M?fdUAQ7TQY6OiTC3-2^3t! z;x;bY!>vESMVFX3vuMYKOT1_QgqGo;uPg(MUa;>LODADqF!^iND zVH1z3t{Io@LdCPI?dG;Kc*wrJk2N@T0RK6s-*p3amvIu0-q&rv$y_~RtKa7R7K0@r zY~yGG6N&U8W_zK&_bn0C9{b|W`LsR!2p0!_@u3RfBHDsc%Wl^~#UVRA?~R#rBM=qM zG@7~9@~4UpaXQ*0d?;KH6ecE0%@f|uN=f+{sbU6R+>#Bx8;G*iGr#FHx!E&s*8 z>~LyERwJ|id2y%arZ_7ksO@DCFakLYp+!I*Vbs7=a5T*&(9Se?8D^oSl&S$-!|Cj zTtj^A)>fY%ZYBP{52vdCWA=F6jddvBz0V~1LJn2*XuV8ZDC*f|;{Pw4H zG%RvJ3yz>v-$1iMKp~=D*gxL5XGz}?ZnO| zsGuu*Fg+|J^#DveF`y!U8&5ryAg`by5!slf!A|fZFdTFj0l^Gkplb#R7jW_EdMF|z zvkyjXC0v>wAsu)(`DsCF)z#CZ#_GY1tPZ&yo|AJteP0p$7R+EUv%+~z{hfHCu{*og z_TlLn+9(vOm6C`%O%@JyAmtVqv@kDY<_&!p?h}N;9Vwcd($`^W-WgpXlKu7;@JA`K zkLZTRCi2{(3J9`aL_FW3D_~UTEC|i&hXtS+m_QJ(8n=gY##`fbTfgZ(T~hf;4+2fm z_KJ+093JDw*XW$@K?xtAuhYtk-@0Fp&~M;>)V*a?m+ktl`yh%4VxWYik^%zKA*HmW zbV!3BDcxXzQj!AFAl=dpN=SEicXz|Oc>infvG@1&WxZpLG3RtT6@EO=eP3~&$5~Xc z3+36!i=yfC(qdDcYW?Xf__H<6*vBoQ1prYX4}>1Vp*PX)BTcupb3n;73ifj1H8bno z9k>H8jwQObo}jMu|6)u7K0wg<=qA6@#mTxeqlMPQ!`l@~1A3x&6ELLR%vi7i-feHR zIRJG&Xn;Ova18^!11b`iU1Klzr6&9YP2O^^>R9}tTtfvLqRk&9Gx%M()XUQ8S^7RX z-K0bWLBWyW0(+}0`qoaGWKzIY)4Q4GRXBtWgLpHjHkK~DBQw(?6_)pVR=e|Mw}cm5 z6K?5KOUH7yw2mGo=DNcOON{$pk8pv1OyxcNhX+M9$?44?aJzEqN5x-*+03d@U;Wfr zGGWpHF|xE|)E>+C#)w4}_&H*8!*AvkI3Vl=TvF~^P3!)vdQeZ!(IDEorkt)ToD(Z4 zthWdd7$3+6p~f&64z`R8HO0%{q-GW^>H6}R^P4E4Xa&eINjP0j24Y0Pb4H2?JbEQJA} z3tsKFWo{*q(gk`7V)X$c9;XGUgecy!f&VcgDujKx&D9^n zKt0Hg3<2YX5intdDu+i8NkV$e0h*F-;c3kf?tD1VwnUJeh`5$ zR_VXzrn=AdWhY^0(Wz0HXELgW7`@l22CqA8Cnm}c@e%k6Vf0pHVp8KYe&;U@Y6HK) z>hcN*XwlF-_cvF`q(>Yf59PhxTS!s1w1n&xRX8aqDJUfUuso$Ho6^oSAH>#82NqO% z_5IB4)K#$JB)WC$*3K8fg`U}l=?}0m^=o;JmdIQa{}l55Aok`!eIpNE8+a6M9;{Nb zp3KX?eM>fjdJCb!A~xUcjQ{_^`?upFf`f81Db4MQWMOau`Z`0YxIZP8DR7LphlK_` z!TTVI>x;%pdmf)3%%wd9BmD;IobcmVPQ$+|J3bhQth_uimnkxt73_L5AU8{2lg}Qe zqp#{U-y5Z!hoLoq*KIzt1S|-?(cS>N&afR7S4WrnJWwUk=;m6EdEVh6`tNB|?~N}# zK%aEmI(C6xx_Wxb`EKml(yrs~*LnYgv(y#v{D&9<8vnb9$*R#JD4DtLCZ`xItuAjh z6hd!u1hNaPJKykM{(*;MM5W|9gl_(pPzJv_h9*Yw{mx7sQ^A0;P_w`pH`^Ogo76j#GaMan5Fur*$M?r(VSR3<xA)x0lmEw#&u2g@DI$ zmRJY0v(m9;XSQNz2evNF$Iv2nbr+bjtG|9t3)sTKLP=54)wb=b8vCWk2<%}&ssJN3 zpODwGYD%$DQPd}dK+-FRWuPW+#m+osKM10(7V!MkPx~yLZZTls&|Z8i8M5E-{aPS(tt0 zCcn9>ISiIVm`t1;jN(*+Z~>yXk3{Z4t{lB)_5J!kFXE_kcXoUpS%>>hIvDDjI_yo^ z?Ysp0UKE=08ch%%3m+NXuXtug4zrDM}h_yG&V{))9++JGB8tTB_BMvtKXYmIXBB5*!KK!x| zCyy;6Ej3^|0WL#o8T|M9Mgp;=?uS%=pAkYM1d%_Xk^`;En{bFlDN93h9yCb=r)!9) z-K~wg>CbI82e`OYhc!<9k6rI1kwkJhV_0Cef~!`Cv}|j*|Y)Hx~UQ zl^VCMw1NzU|4+0>+APm1LBd$f@}UaUOfX=T(kjZyCB2^O_XY0a zX3VKNppKNkxiJeH@UOO&2TEwe@t6zEpN z3jrCRblG0~^5%__-0^TrF7E`Y0AqNEuDLuQ41Wvl@~1;Z;#Z0rlzA-AN?-J29O>I38qTVVn%;IBIs98r1B?yZ`S`g zI=_guUqYf4qz~V)HU=iWtQPbP735=btEDJ6YZzvtJy(mY zQoKn@yo-C zON?_sZ~zCLoL|+IaYI@G(9g3*jrPLuT^7W-2j&O=0{LT(e^_x?N^x;YaoE@DSaVbc z9w>N)Wp<(?!(JK$oq0ln5ui0oj!#c{+;icBfFl4Jbd9PHdxu{?Uvh_rN`gpql9RsP zUC~xb%EcuZt{*RUPah1CcS`;s)X>t@1?8iTPEZ$}^c64;IM4n4Y@oW(y`yPtBi@`Y zp=65v#_NdDX0QIn)k2oakLW7TakI4sHL4vv$NcTrkGXH;;I~u4Zw7@O|G6p{T9cFK zKWo&3RS&%08MG~A)nA~UPBxeadvZ9Kn(BHIjSV0t|70pMtbp2T?3^ zAZ=4nB*U40QHU``N><+a03z19>@WF)42>KVq-?*RnshRv`~`0`A+Oa3z0Lctyhx+k zA%aCtL&N&;B4lqb3uc%XzWMxMh>OHuVjE~6eRW9*Up2&cfzb#0SU_C@vbufp!96lQ zcZ*_r2(AwUnM{xz1|%MTy>onAxPYGEu4yU0oKvNR*$uOvDXoWg*JP%7J-QgJ+}Fil zr!21mvYh2cU57j4RnpsK0B&0hyxsuytE`@X0Kb3__d84o^0`da2_+nz>U_r@dtCIN z>~ENZb_*_{%^2ke=et*+>vMi%l^d8IUHsxoN+QvZv>=Vl4E9wua>94dc`xMa{Quuf zBhUY1T_oA`=JX;uZ7wDP&t5cP(i|5^ktuPgVG0rW%mUjg(i`m46a`*3e zSYY6fAN5~v|A)l(2)y^5)DhAg%B{|GVq^U|gBD=}0%>LSlShDb`;SBiL(&AjMM)_? zvAJU~Eu|NPVdP9sG;c42hPYsMYLr`^4}9U+t!leN z5M@+if)A=dyF+0cVu$`WGF%k$R6oP2&`AV$@)dAsX=u^|_%3oMoV;t&@7JN>6F!3J z{VfC>zVIjmVXS+wH`!^?0aXkwQSJjKjQks=HLri%&&0iD_f1LRISHR-9H%+wcaf0L zpn?AW1@$M?)B$7nT*M$0?u-$?KA89ZNH#5{;$mD|C;t`f7U!4VjAJ+q!g3$CZFoRGpMlMXr zWc(sIwxVX=;V{9zGso4*HgoeTeJqsl8f5K_o)@M5`s@O8L&9Pf7Y8YN1A;tzN5>nppu>Wi>2Q6V7@2txtU=1RI@s9I z5ytRwmLMpEftw@chvi&ubKmKMzP>)%-L-SyTscN?Fv02>7+j8* zUB4{L#;oxyjEvLn5_GAnMP5o0VB>-ue>h$6#r?_1=x82#daS>6C@rx2gN+*If)v!> z!o-~`E11gWQTS{ip7OMJl#*J9^$Gx}zTVk5l_8OY)Y?W?wK)i6jEMvp0N5N5P^RPI z#i1itoG9)Q4QH%H^~>Qp6SyB?!p?47|F~N{me24J0w7042*8bqpo!9W;oi)>(rs38 z2lY>SVg*V(EC^2REg)u+su0D{>(Jz|`S!>ZWEMfK3RB%Iq zo>)UlYCmLabqioU9UGn%EdAwi_%is+53ABP-|{4S-hDyu^MyOF
b@)Ex`Q*1}MJRw+*gG||}T*2!B>uu?hAa@!k}^k5a=!K;`SOH>^pfzo>FAoOOg6EAEdm6dus*!} zF!$*GBkRU}Z%z|`5+<*f@p~%;?puyOONpqJ=cMKi|1JP}@9YRA z+KJOu_iVjUYbz*tjwTul9}29eVyhg;Atn;{zAJ#d1L7_L?VSm)C ziO*Q+xj?-+c$wNNBWNaTOH1nw%`L!g1!vr##W<+La0wh?m8z_?!?D~mKL@e{$d103 zM+k!xQLM}9w_5Po2Z)tVnJAw1z}XMo z4oDM+);1G`p?Ec79RXr5*c>5pao2UcDG|9xa=*%LHx^v7bYUHeatilOd=hkmV4!aO zPWsxLPt+6?DU~k!9qBQsT617z0o7~*i1hxueiZ?*m6DQtNW$q~?tFAq1~Vj-j}X^< zI4Y{ILwNHA0$a62C-UrP>ME+m_o@uw-a<>vmk_14Dt=cm%#z~bny}}WrEF0Jv5;v6 zZv?cLl9qi%ut+)6+!_B$@w%;CEP1~`Ow~&Ka&_2vnBT3mQNqU$XYqmEGHP^g|_RLK8 zgb@YWN3fH__8WtredU{|P-?Q_>n;jJ0PbCABlBk;BUx(fjEuS3eICP=gaBdwyRWX5 zE9P{U=y3Vm8Y1OZy3JihAeaFDk2`z@D{INHZhLj=bN>V~es2s~lwbSfmp%*5aZwq~ zz|`JwhRB8zHK-Lq8TU%g%Et5+PNwm3FqaEmp!pffxs%r<6B!j%NT`-oyTlRU12;U+2RHUcPknE9f_mnL0DFnoQQVSbWZGYaA);?WY<+370N z{;>5ZWq>PTAoDnWfT+1|Q3I~I&sR0)LqtW5HFxvxnDdm!6cPcY!^-lq*94X(Uhde= z&c)vTV;8-NwnEs?J$N9VG%0M77k|^; zJ%Nk-(tCjK_Zsv&29yn6X)5m{a@&Uh!ggpLd*dTS?VX<7*y6L_KOuH`at{G!y;AcxLh!UfjUhfqGG zl0XpSE5c$&ys&WPEk2vkeWuVKt*br#FI*(rA|ngU4YHGxI4#GvFhnT5ePG!M=E@H{ zZO~~|SWO=>)urvrM3=d2H9cA>w7lqPhv$Y|@C!`J7Ut%_7#sNv%-MiZF`LykG#IYk zpJoBpW#87Pme)R$nti3^C#$M0@TL45@~Eu5rif$Ui`za_w02alpZ``~W8L0`;ofKH zt*UOyAXkYtHibnQnVIm^!ZAyX*Po{Bryv?b zN^RDUM(uYdzNCV%9~uBq)QB#H4qvH%C(CR8hrB*e6tosSrkBca?aybfUKoxhCsE4F z^#;V1>l?gJ$W0X-D08aYU%?uL^w(I!d2fHnnK2R2e|JI3X5J=CJBB@XvNa6rG$9^d zaH%xLk|Wx~`DH-@h8DJp`~Rt8>jiRi=m9K(`VkscFX=b;?~riS;mvJv+w5g^&4+i8 z4D>0aUeTZ35AMpnd9!h}p6k9gbq5CIumOf4I`L3Jy7%9~f`1^t1eN>VzRfZF(jw`V zq1*&a&-%Aw7&wdsHY+BbLca*s8g^*B%)Xoo8igGXvNd;sv-9akP?#pCMR{J{G(b|6 zvxdtJ>M&?6U299HU3h(vEpT-Jh0vqA;^QA#L{>o#ys#;_#_x@rT3WpLeqPx)S-Tgl zIHBG{5r~@@M~R1EE1_ z^rA(+jSB8ArM&Pu-pdo%Dk8m3^l)w-P#qfg&hLSJ;{FC-`ocolY1+}f}ql*x_TQ9!dUMYZ`LV0xbR4m$xb z@%QCpc`Mn?+kQih_Ogx|e28)UwgNCcY}UQSOMvbj0Be6vR~o)2WdPivn3oh0dDjqJ z)~aL%Sc80n*4!!vNh*N9G=W(@Jv}ck=o5$y^%ALE+7G~e4u2bXlCDlL*Lm@biD|vX zr9D! zWreqq5ux&fi7+m)<*lE$0=#gI)%r$>{a+%Uc)ZPhC%Epb;iLOA@Xu=dbA+wgx7#9V zS^DK=HIxz=nI=elR@P^=+Sald^39_C(r!T*k~FP{TU5fq1oc9CcJklTAF%KOTU=1x zO*z%@9Sr_G#&P#OB19yB=ouxYBJdiYyqrBxYHn=}Ayr|Iiuw8DS3*M0k=_BogWX_( zg%nzvf=&f)n!F-&Q29bYK$XkBw3gOjdNwZt18A^8tE;O9YD)2aIXV2*I}dU?{QEd{ z9QtaK@Z+TLsrax=wYhU%!2zgws*|!hv_o?DVKCLf7;CXTQZfwl-JVw$2pfWxZzH?m zSyD2cayVkaq}*w_kS}3-xLpul8D!`rGVmYoulK}2iC+E4`&n<}lhE&a7qe2ks zBNvgs}z?QqRHm3siIyZn-Rf;BV{~U%%Sk zS+DF1bt)eg6|tiHsoS0XHBTs%ON32OW_*4JQ2`+8rXpVR!}-&fi&kY6YZTrU`Z~6@ z`-!QH=^f255R#7K5viwxR5=s@8_6-?h>da%1x*uK?QwS!nGvlwi9;lvnAqz-C1(s$ zSo?sB0vpUu_U6gf>dz|yWE^+7Gw2TiuK~O;RV#Ac$zUHT?xD6siY-PwPAhw^NvqH$<{F z%4pEiGSEDI_U!4?YY1@hw_D7D!-ujZZcfaNzNP{k-cEx&McYzu#i%H$dv(70^|Fpx z&==k_fuXbV}79ssTmlsByQ(~$r>vE3X&L15GeHgVvh0u_lpOG8fCLS0;(MtsxH-+S>i z5xUfE#W||Th=?7LScO1Tgso_8^{Q)iOUsg1RCHZlA;!Y8O;BY=0)rzjPQHix`{N$h zX_UD&p3W}XX5xXcTJx_qB4cl(WOoOVU8i{n{u!w0Kuu_(w4+S=JI`EIL1Aa7zha?1 zGPp7nfa+6$TkyXE>hfVZKLEGj0Qo~0o|2S=dEk_sWPtAKBqYR&7#XF#aKl^&^f%}s z{*v+ThXzMV6VQpH1ETM#OzI#`$GHz&dWbf};{N+8B`4PlC8b)iDarju4;~V2Xj_;V z>!u|p78y<4-tqtEPXI>M<(R=nFcDy6?{XL)#ltlOkvlAnhdaE5F8g5+*|eK_C-C#G z%Mg+D2Qg0D@Ny~L1-LJQE0VrN$S5in!EkLC&jRJ+87r`9$!+lOLmnjNW+NXntsVS? zz9(Wc~c39|dsb=3CN} z!QsjXf_B2M&kY$=@S(t98e*Ht4ZfY7p@!#-yz-2J@Dc#+E#D=+H$hNf3lNySor{TY zEEd}%>dG=SbTm1Wm5ohZAbPC)a6AC5jPY<00v4_t2%OzoJk=-&B#S_>3*7GDPJkQs z4v@f{wt!FsWut8WoriPF$YZdSR!Q8Fw`!I{pU=z8bi6kSU3u?wjY!E&CO#%8bS5xYuDt z4R1e)olIdZ^2?DW<8%fBae+j^Y+@Yz^T(@;8kY<7errnUue8gNs2`It4S}7!Ti_0v zBeB}s#@$It6zO~sAQu43G;Cwwuv20O2MR6B%^-7l0_+4R0w;~&_PS6L(r<$a4XlPx zK1zo_APU8c3%rd0VDYn{WV4{V0%%@@C27w7F0w3#%+=}UK_3R+#9*#wUt8Nlq^(^N zsz2MgqcTvHmGi_16@b_j)D&r6{JnJ$r_$9^8(l*6R!XWJ4ro~K9oN2d1CU^vi3to~ zpov8D6xtGrrFgeGGF+Bp81xAn+?-*dq2irQ0K60t74@aC3r@jeKQ+;^(Ul_@R-Z@( zu7g&Y+g;?JKgmhO#Z#A<5U`MVUf_7_9Piygz{@#SVW^y6525GH`rBbbwDk1Ac_ni@ zPR_l#(qcAI0eL)lEd`&`y_@b#pKrx&~K5<=ICoAU1~X!e`h+acCrdgcZT z?7sW@`wP#1H2aTC!J`Y(vsNSLBbM3FYq~}1vVED;m*3#Ax5L|e7@jZi_KlYnP-#>+PWIJ%`6)y&TlN)eeCzFDfvuXv9hx$c&Zef3qRpQW z0pIh_G(NZD-K;e8eN0O@uO$qOi>G4Y5UCOW-+Kfk z5b)gFn_LhaJOkh#nb@+mixK(fOfpw8K=PZ#u$=>uDuf>G6qV+wC~}WPQeee0MzB*; z=N1#ZwVQS(05&_qfoG~Uj(0<8cnx}tf~!=+;j@5O5ami`UU@@X<=Iu00{Iu|mbGLL zAL2GNQU{F^K@I%*a~c^V_T7jtjnWEuf1HPKVcktEsc(RB$SAo1gjDCT!zM*w&?Hg=pk0^Ey zf4$z}x&Es?mdF)}sKebB1XE1Qfvgl~53(r!D)l%lw0ncit0k9w<(M zd;23QDlRCZ$Z}n-Un|XhIE!=!K9&R9({xsr00L2gv#LZNH zX+UY-K=bsTBoe50QenRLWKWdMYEGPB3RqLHuoPduMw3V03876vevs3qa6dDMr9839 zOh6VNfi|lSricqAa@YB)FMu&`?c*ci7}vlNBM$Lp`%bnyUIWfd{Bnpx_9xK7fuN+(S;~HYq-PkpzS&0w0X(cly=S zRRspK)j(OMC-Tk?e)F%Ho|cvt-WLO4USIB4&o#&_LH>Y04_{m|yynK#+`_2{X`r2=Rqy|$7guX9>qWgGe*V|hPjyh7X%*a{MB85igDY?0) z07<-XeOq7imMSKXg1SJy(GECpZpSC%LvOw`G<(hh#{Ouhs6x0FBUrWAbOK6qYzK$x zL-^&u;2#iw3{X~(0m1!2rnZz}Vhpo1fWzIWlg6%ZX<|*<@INBqv)jGIRv1ilJodEZ z)i>4^GFMP|4!8_xq9vU0#J_!j*Dln=UaQ6xvgHfp)t#?;vKX#VNveqJnW++sTInxN zhWWi13BWQ~ak#rkJ?VZFeYo-Gf>-=%f-4p#i{+^Cq9k=Eui4-{{Lek+NdGx?xrF`S ze_c`jwB!r>F1!O#>f;+Xo+gUi{>3hR;B#oMqjR4|@QN&gKzh>dfOv-XbFn!ZdU~l0 z#7m%E8+xx!t@4++N{Y%Mv7Fe~G46b0hwtq?#l*^L1@4LTw@V~u6mIEydKZ7<0m&n#(%E#Mg1m&`X>xaEAu%x9xw+wvR9=n zIinXAr0mVrE8KcFz!o)9QVfx$<*u#939A*r0-Sv`M@C0y?Jzk2TUKbS7&VFvAPWcv z`oL=a5h0O=;j*>vq_HiPg6;01C;lWl`(}pY0itz-6wF{K?NJ*`Q9-d zR91!e_g2Tx>qPmt7g2ym58OuNPkp^?^#1)S`4(5 z!^AL_)BFe!Vd_QeD2V7?(yP=dkeEZy~t!lh5^9N(CmvgKeoTx7#a( zkN3D_6@^ueOq<^ojBC}PepF&tddxmCEalPDC5!s;k$htiu025)>Lt0L)Z~ zFRE2SdSW)4?tdgBF}Pl{*2bf9^xN0Qco47e5=jgL*1H&xq%i;knFzDcp`y(pz)T}k z`@1h7tjyMZy}3kBfz92+4Uj)sT?E?j;(=tWuTK`VGX!ygZT1=y|MJ-0`|R9BhaC}c zDV(MQ45(2c0?h(hni>OS2Z+DH1psuw?_NIhQnh$b=w5IDFAKIB&FG=2y*Kw(k6|_f z5~lxFgfCnd_um*jQiLTdShv9SfK`j8eQ32zs~a_k$zVg(89Abc+~aL+&ApPtWuH=d zP_+#7^#P9nLVo^Hr_|Ti%gmymC0D4GaumMAJNf&9^}zb!E$~6vw*5oe`B)MYlz#E!md{q zJ(=;po|1j8I-GE3%*@dkEIOPRoN&gg(6x9>CVYRn%3cf|RyM~M4&44vgk)80YEEB} z$m}q)L`Y&-FOJ*CO{&wDUU}nF?_@V)Ax2y4LkhSO&UKNL>UL|+>!d%!K+(5ZmeiAV zIT~N5RqH?!P@}t#iv&(Cs|B5ho2xL>V<28aWVG=ax^oesF)X(K$SfEG0IO#@S_qFF zY?%P>l$`%^1m~tYM0sQ#IdAaalm?!^@py^`1RsQnQGgA2F90mZewGOe-_u%*OiaGx z4@?gHqXj%7HHVC`rV@6EJI9X8`ej(-EUaODZPLm)UqDY-d3i53IFf-S?yU;LLv&B< zJP-M|(cpPoZfRdz8^a}$!rI$Mo_H8b5sSv+F-{^M!|2CQKECD5a-zCQo?m*z7>YA& z_#EFv>DjLxfBf5DMXfoIu71Ud-}9>mrZ_kTyu9N-ORqyWZ)|WjQF%Vq)32`FL273) zf{#3Wbeqsbf?0Y8*3ECaEeQx#a2MXCc4%(&7WgJq$9^xO^i8HW`~oXQ3jg`t^B?m?j#AASFqE=d7f-F0exe7 znQ@b+BZ%sh16%<#W(!MN=ne575nP2Q>5D!HX$%1E_SwCIv8?KF5=X9HkFf%?)QuoF;rZQ8-vi&MfKB z2&~Qrp2OB){(c|I`_Xa(RJ>F)2LHVvkB)uf=+zgJL>y#K=LA8KPxDyF7@hdc&^PCf zuX@{!kB}p}d?7?d(NL7YQq?Eh4+gBj`VI&+yY}aYcVgPPxYQ~4Bgn?^Q=k1h_dxn{ zbc%V--a2T>A|ePvG|!#hQS}YgG%cy5_sN%|xH`+qXMD_OM$%lInFi~|ziS<^VIG=k zqq{6qB?nG8yQvPt=mPmO+FA$rzNbg7@Rs=c49#T(eyQ3^bEA?s8`iQeUgw4>Jg0TX zQb~eJlu|D?Ja;53DM<+9zH^7rR{<8=*UrBr{jCTE+OE$OtBthL-> z$v*8(oiC@BWt8OqE>I8%xr{f|2;!4Du4ALbZr+bNMB*h9102e_f-hf~dv zj76LCDlYh&(R8=XM4UVdyczNuBC!diwc_}_1?r;6<7L*q$8j^d8_QsC{K)qS9|AF8 z1xskbcsDF-$5OG-{&L%dW8)g68fjQeY*T*keaFzz+>YtuDihQEqrki&GVkk2T(75fZ)xY}<#)Wr^vm0XEuZk|Ubw2trf?-fHCz#nHO# zI5(;>Juz?$&^2l98r9?#&B+U~M%)*o%J9OB@hzAI-r)D!`|yu+Oc`W0M})HWjLB0(+$C%R4nAv-%XT1p$GQU@K-NKv~@CG{}^~U4O>WLYGnnR zgv55?tiL6;L~GN0CpWqyZdb}(FYc3`>a+D^Uqj`$%+T1jFZ_&%Xp3RvVq~0zzvrj= zD>)7BLK8}s7v6uhpZ#Q!^8V|vy@GW-JF}#{xTH-TB-Wu(`cL=0e25owzk=#Bvpe4; zE;iiV{xL=jJqs%aqfBbw;(%*L$w0&KHuV=7E1Z9$t%B#R+E#fgJCJ_eGNTDn*_oej zzNA;Y(s6vBUim2qrYdrdN02K+t0$3~p1!^4C+>Ve4B3T)@GPa*7R=9+A+kS4El+63 zxB*X7rK`etP^i7P?2nm)3?eoLk#WCRp5Q-6wz4mEp z6NeB*8xG%F-p%pxAC8Vxk;e~`5m<99x7d#A=e%$)-ZS93lgi1*q$;Jz1|s~uK^mot zLBMJ1?3b@!pu9x37X7^U+a%$&a6Mm$*{u?V0v#>|t6FJPUT~I13yR8wJPCrp^rU}T z2|;`aEWN8-a3GCo+ywMSO@(Dm#L?im<;Tba!;F}7a)~t52f4DaGb114r|y0c zb&K_c6uI)wcuOiOk@Zmr$dK@=p8-+w0?tFtGPjD#%C}D+LN~v^(H)_gv%A#1OZ88L z;zeY>b*B!lM1@bYG`eDIr7&Hxnph*>GRl-yP3&))qv> z2)Njj@VxlkK6wP~g@FhNV!&h-lI*e4St90+Ac3Lgq?rBg-`Q=t;ziC8ap71p%l(DJ zV9V>ZeCO6vr4bS05D0nYbiAP9o;z=ZyD~8N`abb`1KGA>+>1{y6G%498^(h17|*9i zXRe9LM5oG#mgyb7w9*?a?o6bm@+a&b+K*!G9={|?&Ls;`d4`KoBfb5a@9qc;I&LRE zeM+15luli(FN(#)C)pG1ysznUaY5aDmxPb}Ml{+%Zehm*IN4cD!1$cKzMeI|CJxri zWiDoGyc|CnG44VO2?MP^_AfUb&u?&y)gqET7UBfFU--w7?{n!uBrr~Dk6O7?5 z^#3kVx_N5T>hB7}lqcuWbM^vmaf{g%cTwUEODt%U(=%Dbb$lTJUAaw+fXno;s}c?f z*6jOK=Nz|N=!3nvtw)aDJa{J}+!$n+F)9UFd9aK_J}fN_yQm6tF7>eFFWoRDUNfa# zy+hB)NXoQuH~+7vb?PNJA{|$L2iz}MfB^|Ejmlg%nV$$fgZhXtYGH;c^ z=@R_!j#-uRvl!8pz9N&^{+#bu5hcwm;ZXh%A28_-uh3^#s-@8=&P&Y3ap!G!%7x7o2)XS69Pw{j`S8^nFO~D7)Mm_H5;#9hGi8$X>Fy z7z?)iuGF!Dex3wnP0*9}`eR?dJyB2<#8z#=HVHl`ia~>)l0^m72Q+kaijs~v=m2kC zyZQ0hIz@3U)6(WQVOMVBKH;H1(F61e`G#vVo>iMDhy5ns5(7c;(!tc%0~ z7tiyeU0jVfg?wV9_1XxtpFpgRxYGCunA`4<_&>E2DvUjqSR_a$wq5Lvw~7{n%H zXJj(poGl_e>~MKBAtNr;j?bgZT-7i%wAFNBh6ucqyYbP^OpWA)V{F#y`uxkz&pBRy zwa-)*-3_p@Lz?3^KpmT)}~w&2+fcm%X2F{ey$$ zcCcbtc2{2(6z8XLPKZ9kJ-vB7=o+q#S|mXq&Vdg#EkqFb0X|AO|Kwl;Ln}BGKU6e! zn5!%azbB0NG>L?aUy84UWN&RE{U7xm0rUh?S;L^ku9vQ#KTqCW8WhyhIt-whjjh^a zVP-X2pPwEmO%S6vvHmAyOJxmC_A7oT08?q*`zjK@m~E28G#44e?Dyq)a)<&B4h}&% zCm2C!X=t+Fa$nAb61p><6nyf1fb`kt^Sc{gR>U9b<_!%E#Xm6Xv14OXgs#DQH`o15 ziX_o^TRYq1=N_~ihDK>JQbZ+A!`BjIhpNBZj}Fk&;VMJ4vI?ukKfuIZaDAv0-c3P6 zi_;1!O6HQ6G8-J|Hx7;CzB)w*h_%u^9tA9z!#pTs(%;YEIsUGp%=r z-ur(VhV$}FP(KMgwnj^2SpjGIh!Stz$nIt_0ZPt{ zboIoRg>@Hs}Xt=q(tYFuav*wv_-V(~# zP%u{eXMflZZd@*UY0S%q;`6yZqsz)JX@Vok))n6ZVp0FZU+}uGH6b5mRW2BgsuDh3 zv<_N`kukk=GJyonSB&QTt}QCi_d$+Ds7VQ^PsJ1W-;YCyKc4P=%NXBCzcVWzflN+Le0CbVs10T zJ8!z&qP0|u(-o;UyjV-evGyBOl)K}0hCjSmEoXq%*B=2^{>d0OV5$TGZ;+xS{DMt= z1q~S*QR!~1daOX+5nb_`zeKNqYp>_6UGV^XFU(9#%) zz0B8}56*4ZUpRa&SGQ!YQqm-erH_n=#E-%!Y`H}MJ=FwiqXSR%m#XiglwT&r^0b28 zw7&Sqm5T*G_l8?rG3yD%RYNj2C1GoU-KlvLRR69#X!hNo8ZvSgOt)WhB_WC9+k!9B zF?}BfHxHk*di(+_nakcD<#GlASb1z2Z+20irTv1Zi<4_azG~jU;4{+$LtqhI8_yI? zZOJi0kZ^VENBhUGI`8@|QXl^^WjE|Qdc;HG4y{tBR&}~czv_yx6h6mW%=q}^Xs|m0O{cquDPGCd+q!r;v35c?b}ouDs(^-wCz(X=QEM*?fQC zEd-$FXJK|l45VF5__tT^eT05_r44bQyaZhUg<^PRbzcfLh9~zkGb{Q(J^7kA9MX#3 zQCzN{6+~7S)-Rj5s1_N=w|4ImahEdQVuXh%y&&9?r@AD(5q!%j?QaX;uTG4Q4mPhn zz(w~YEM60rIv-0d*KHr5Ck#{0`?v+~tPUx8x@8SOK+M#NY|NJO-48{W%qR|Kl2@6T z^Y3hqwj_BL=?g#zmegtSgu5g$^`$ouQI~J^EJr{K^J()rYgcN(?%77Gw~xPsRD0Lp zpovP6+z@le_VsfA&-++fV5{_8-%Y)^6(iBoi zi!Zm*Z))YwubXCbr^JNki7YVkh{l+RpShTI!nxt4U6aPXRO_vX)g7iX(8M@Wj^ zR7ZXSb@=^5^y^^VB4Yl{QFVO-+B(l#s|X#!WyPh@ip*$}Gx#uJI9%@7SfU<&pctnq z+>u4geB+zP_@V3TICWtPZ1BnM_Jl?uza1_lGwzDMW-6ImuecY8v&QRoLJLvU><+h(k9!*UuH5{KPV%X?jd~2MwH=P`5YC^7X(-5E( zwoSRBQuc0@RKBq};;zy&+#0OgWiIPs%B>4Y4NXmdmPV^rTc}!}*t(vdp+a*nrehc+ zW;50FKA8K3jVvk4Rpux4`o-eX$oCgv46)?4I=@PPR#ax^E&L!&ByJA z?Qaa1$)B=QSUW&KMktPw1Ku9UIR^etq=YK_!gP~PbN}&AV`C(~y(d^z$7mx?e#+TD zhUXI1VgPVfX%?)@KSKlNitIMkzB4)q!?O&9clJ!JzNwvoWwj95lSzMb4Z8EKr*w4t zo1^Dv!g1i#myBaR_-0QC3EO(4M|@_J1I}~5QBXepg)XRO^EAtSa)ZA>%A@O&@JhU2 z!|g3Zb`QPj_OcL7fQ;5h5AKxwe+!UpT4bxtM=TB>VAN1ijEjd6sAz_exXrQEsgTH2FP z+vc$FaIMn0y4h`LYs5NrsMH)-DHFx@W`i?|N^D>@(OAv4~*m_5B4BPsSef>|dixMz^RMOb((X^s{7$!76r_?^inu5J)M zLc-@nf^**9#uTB-#jM5IO#Hi`NOSw}Eitju(zh+&aErjZZ9X-NLCnISclY=4-*w${ z8EF%ir~batolWkYRm(4IHc=f=nE)iRqUX7XjeRhRCg3n1=Xbss8QvX)c!knZ&PRlg zK>4n3Fb{YUI$Bx`&-qsgvJw&@f3+oJpTAaD51~yHta8;qF~=wYnf=TR_VNQK_lq*n#^M=ae1`YcIQck6&W_3G$n(f#O!B1|ehmx6gug_^*2glUh9U zsf~1)`=PZHGfz!=SBZ&{ZA#ss%Ot*&o0ZE9Pz!SM){&Oal26nlz)u)mvt&JaxC|)S zJ_SXb%!5QBU8@@nxlHV3N<9^Zc;w_?G-WT0O;N^|L`*e|vV28r0= z-3%$vcXj1O;FygLiWIpfEH{1=Ny=~?c*v&MuRbX4YR`1zhUXV&4taY(XmI`3Vl&R; zah}F|5G9aOL2(}T^(z|kVQMS=TY%P6T3SRv%?Uam`= z$xId5y}sdAnBd$NZs@u@P4RhsB;Bkb)qG}TsxOaMkNV=h@ea9xEj;f)q)zf_w=SQVn&3nSZ3#t9oppoZDe0DaM&4)?$3HrjK zqMWxCSQL2Um!~fhlj>!amF=wxFHNS~fc^-PD#2=e|3Iv>0O0kL*EtW@UO0fxkHumF zjZvLhRQAAefdvz@09HN0=Ei7Wm~_^DZ)l8aRkg_pei_YSnW7h z*w}!ivXN)~a-o~V-DP^}8PC8l1|BB2IW7l*+`p*$KLWk{{Htu{Q^cN8;n6JZ`Un0l z5TE@j_lZ@(n(7JgY4t?0o|M34-^0S3OvMci#b~~DTx5nZaE!}lx^iVWBBG3^W)wmN z_2Y9zwIq$h!dtR{iki{T)^;6%ktnCG%H_wBCy2I=j#7j2_j|9di^QTQXoUv{V?R== z*Ut!g6Zy<68w9J9)rVen(^gZ~RlN%v;9M;3&YF*)HBl6&39#WIAS5i_IY2&A z=B#DYOw$cTRYXH$ytTj)<~U6ndV1uD(RhnB810!2mmhl9Tc8RynZZ^KVCEC~EfW|X zuY$8ibEH~HhpY`KDVgOBIyYjVaiywp%Zka~2gT0h_3OlZZqm=fnuw&AVG0df^?6gM z)*5jP7i_EwGhvmE!IjL;#?~`9NXT8<**Pa?u68IzmMlhLm9v@Fq3@4B68BddoTpZt zW1#3LJ}`gDEAe0u&DlOjtH|6L6_5Hvp2Jkuy)Er;4wi;k4(B*5PYBp-QcBiFoRel; zVxiz~u2lu%A}r@%}9m z4l^nezw<W`+(KW-9!|{l zV@qGs;P3p7$_N{egeWA|=-eM2Zhi@iO|qcC5CsU`bRyGLbn)?0dh^EecX-h6@R$&F zh5EdFX5~zMo3WZ%d3{!d^@H6t!z);b3XRRN~q)a)nw^R%zLKILqevx~l8W9hbIqiN)mSo_M=#0$PjG=5HxD*59 zw2aIQ-Y~hGh&MEV2=IHHHxFH~7=?@5Bk#Q7Xk8owgNSWAOD1KFnB#HX1W(~LKfh-Z z64uN9`9BJ4$?RrD7_990V9%;0qkJL1<=_H)&wM{2vh-oW?cLHb>>Zt)4IkY+PG)W; z33>Soe7#Sy(5Hh&j{8|Q?b~MB`EBqFw@Lbar~ToF>)W6+9R$k-#m+|;^q}%cO-fl_ z)-_Z22sBO3m&TKL?f8ryOZHFGgC|0}bV9L9S_`E2^ffybE*^1Rxv9JCn*(^5Msc5b zT;wWBJ`}n#SZ7%FXI6OK`yaD4x*{IDv+%1||JC^a_Wv9%N6csc+SmyqoyxM~4rP; zoO5f8`~N!p!T9`Ov-etSzH`o}w2ib9fBxjdx3by>AJOH07A97vysvOMs$ zN(=IUf3yc^lw_=G+XqA3t}$sChqM2Gug2m+zwT@V2$%0;k@lx%X7*=VIl+btq0-6+ zbfPcZNG3qv)%6{Ku`w}sy|{GEUTI1d!`|m2TGccYDgcP!yp(#zDsL7dfI+VjQ9Zm> zZnq{qER6gb8v0(}!+_&O60c=#W1}M+1LLZ!pmo!+|7_NmGDij5*YFp9vIBQ7w89XU zfd;k|1U;%enk|!SB_aZ-aC0~huU&O(Vx;o*_Odfw%=r29 z&)kyh_BV4K>x|6Ikg6C4<(N9BJ8Dt4@9d=c6_`w!>Fr4dr=#MJ##7F4A0P6pK0xeC z%7NX7N=^M*KMex6>gO9UsIg0F>;dos40ZPq-Ab6O+cF-}Y_+x0V%+)+itrzejI@xq zc4Ccpky)Y9S&%hJ^q*TF<4DL!j8>D| z1PI(PZNZ5;t176{8+^gwCc($U+gTr0_DpJ@dg$rmONKtk!gAH!*H^-5*SC!hc@iD3 ztf^5VNFv(WbP17_)#m%pGa5IkHvTHcdTwrxHIEQe10UaYtct&G4fdgvvH zW)5_y(ey*R%dH#UQX4Ix#6|cXBc}ok&H}No%h-h zWd?AKPd!w@zfy02(~k!B$(Y6!Y62X0x} z2c){XbA1K3VXXZ5^Ls?Z9RxTc?*IKQtE^4=Sd{k>lF;PHK->d~%x zue4-g^X~q4tcGKMPd)#F=;e-$O>x5=8Z$DgSr56^@BoiFUNC|pMhuJ%6^h*NigN;! zSGEoKfe2z{)w|={*4AbMgQhRz{p61yKNcoAm^f>hX;cNop^5%xWnoEM^Z4I0eEt!B z4Vdd~Y?O$2gyDICyFN3M&eep|kDwG6f6=Ed9yX95{(_blTsL3}iD&HXs;B1RNegc^ zo$8ho6>Tss&M!>1v!tPMVz^H5CeZ({uuclrun4ssbO~pnd21%Z~$haoBoa2YuwM7 zm@c{Rzl*(c32z+86+CF}5ot`^uZs#&uOm8VQnWsumjwZFZCiXl6W|x>a;8@n8M^vEld~r{jxIdgZ>U8oRW*5nf%c)(HZLLJ>Be8 z1VTpfI!}haQG7z}^rn5pF#Ds|Oi00=8L_-uQx{+C!wS&BB{VkZCC4U6!-E46@nx<8 z|GfgKdVMalqC{NZkHh8UgWdV0JiCysP&Vw|{&U!ss9g6kJj`w3kb+ z0;-J8c;(+u&pM$A&_75?1!?j795+_iA1F;YOg7_vsx2VE$J*TS5 ztgt9Y|9lf5No2@waTYd=J3FXij*HK}%X~Jhsc0@N1on4yPpO_UQ747QkbvL+Y=rpl zI3i#tEv{+h9soiJ5l3*MnM|!Wyrr6`m^mKVFjXhQ{M7Z_(sKLDw;18i(dwLqqs7J^ zKplu)^@YztP8>F@1ZWO-fm#JGZ)%!h6O5uazlHy^Gi`w=&3bq{pgwnUQBfkm* ze6l7--R+1O7+%B7XXEk~gC9X)eUi}7E+Kk)atb+Fr|WCRP`p-j^s_WM<_AiMk-vw! z#2dT}s)Z41%;>jn`gngch9P`cGE=p5sI-dIQkNW>r+WstfVF?il>Um3o=a%ns3*>_3Cp=x2hpMo44dD2;6pQrm0JaS$Icsz$ zBg@mS{KBHd4$e|?a%$?O-RM;)dObbNnVIy9@5qp+xZaRZ_C`*QZf=u?r(B6nLFQ=w z2^BRH^0Q3Y>PFm%v|1Y>%VsB7M_3j^y#crCtc*|y&`=Z9F_DvqfxxQ?FssynP&;V_KbsMS2WDnizk`tnp^;_?R^$^_lI5~!%Sy`Q8k=R}w7N>5g z<0h6D&)Qmg_@$)4Y@F4`Wt**OG`%zuMdMKLGiNbotJuGIPkO>%!-S1m6K3=b6@q9r;rLvl!&{@BVswv zlCi*>vI}2ae4p3>L#oJh=2pZz{de!WM(U}U~ zl(r*nl0si;sz)4-_-Q*V8=chseq0;JJV|D-^B_Xt)>$jR3)xeWMxu;T`@lgQ8CH>{ zS9Dol9k#oTj1(F=O#fx*DS6+l((}7q|F|sL>0a-vYR;kIN8BajJ@Fcb)c%g_bq$$L z&3@wgcO9SEIi2;m&5tF`3b~APwlGtlt$+Qr8ew}F~zVtLNU%SvAf6uViogloH# z6?cvv-UsYIq)N%!Mf z$)@MQTlQA^lDWnCX1y)jb1MiiISv~DyI{eMWfZx3<%{(F5vZ@#<%$6J2+qYbe|S>; z^Y2``I}kl(?y(vzs;RWBu)L>ZB@5{Q_MfbO99D{VT3hUPm}p^L10}&={bLamBB<8rbeY@y#uvptOszb*y6(yPI5h_r@=D+Ij2 z(&y;4(%R*?GH3%u=OVwwGA~R=c*xAKpgrNF+=qnA**z}-PEk9Opx+>vS6V#&3yuV6 zzcM-Rokua{ny!R~V(*G5+4nENl-}BM#jZ*)1_Id*b5Hw#!O!K^(Dd#y*b_6VRlURq zqEXdqHv6ot01l-pK{_=H_9Z?O=V@?rhmF5dNP7%#sejuaEq(poJX6M=$Fbl0Qsb6*&PUcBwBes#3CfdiJz`2EVzo#G)g6}S!t8vjWzEhxrz=OF@dWypRAyeH+60v%17Ug$ zJVJDYmGdFglk@h`CyY{dGAr=i3&Fu5XTzoMy1q6i7MV(m47Clr5uNC>oN~y??F_y( zuMMqaE*5BS>VXY8ro*LQ;N2yBmw?a49>Ng#gp$PYa<=uP$hRF79^%9bn!$m&gpImaD20VtkSof{vTge-$VmBzCb?e^pC#GDXOev448{z&|jbd$sUN1tw-@ zvfB1xSRO-2JQq(ajVEW$`Mxo_{5$iASz!yN;^~C3KupU7t&SWCxjSSLc5~9uGdk!D zcF?Cog%>rStXla4q(av--G5WisXNEY_U${Tsp$!%tosyk$;rzjO>-_v_CFM25_O0E zxOQqDRl9B1FgypOS2;oDknh0*X}L)4IA7zHG*D6ig29!?V?~H zw$t`EMxUfg$HPlosR>8g?d68=!sbBNx&X(E-$8{VWE6wBUka0y`{SyO( z^LvEwZ2R$Y{%c0<`julBa)?iXt*SAc02qe)Q~fgUmW3z|Lc%rh8R&>&d)IRi0Q4Z^ zrKQ*XF|678Wp6vIjh+QRl3*=_Xd+IIrWe&FJtb2>m&v4SUs*PI@@e*cK`}QpS2p`Q zrL>&;k12{*I9>g?1|dNk(<`+cPQi7*nw+e1F%6+E$2@MgHB;xdn-a6M>#Wk|pj+G2{<5^Z+y*!1 zah3=2etmiuo+f}1eOiPzK28RG_%?p*TVXqu8qzehvIS*l6+;=P=L)K?rVVc8Ry4+ye<{(ND4bfA9z zhmFIkZo5C@Hdp*@8!m9t9|csAv}`D zWh=b+12I{LulKhEEv|T3&w=D*;hoTdiI~0}8sw?vkUNS@Qtr)0Otip+8(A&&Hu^2V znuXo^D$`abeadbeC=idz>4L0%@ zG#&Xw2xfY<(M*E0#i#M@Vl;V?BCJ^^Dpu{&ovPaih= zK9U&E6f-40bRyTC>u~^oBCDMIxaUR&soAQ92h$M(@L^{=#W$sva0o0}CG_Q3Ei+WQ zNS|wf`(H9+a%Pgv@zIj9o5c59DW)0p{)A)eOWn8s&92KIFQt^oT|zM5;)E%Mo@;;NC;p+Hk)7=Whu98d(?2kVTl3A$>i5&*BB-yQQ)<35E2EGDllOw>L~E1AK5L?$7hPjqVY5PS(Sz3RYY z3ib}q#K;2MGFBnN^_>HI#NCH(LjNjqx|nV>Lul}pk_!IMzpeVCG_BokJuxPGhhq0Nakv1M(L*vrtrYDRQM#+&Xa5kn1U%h&jMfL%UY>?Z# z>;K+u)kS8BG(Cfq(r``y3MdR{xOh3N$tk5~OP_p!f>uHNy^s?6Z==Zo*y zahqy9<_bY^WwP@x+HDe@X~4-@uy!JOZvl*kz;1lmvHIUnK4UyNP)^Rnobeol*61U= z!1>?($TlAS+=A5SPVybqXefQ;=j*)b zE0zD|xg*})2QD!*En2x(d~Or(Og_N~rDeiJ(VP$SNL{=<9CWUz$gf|@Xeh=={{6FY zYC2rSe=}L!d2*~b7dQl6sj*z$6I|qVSFM3C>i_+0{&PuQEC(7|rh;b?Lu+1L8{br0 z277eQJkdx0mG8K+-F&P4+=9N(b0invzE6%38Wy~ztg>uVN&2q~UF2QSCzT@q5?}RA z@8uf{^=gbyUH53HXa)wm<+M&#v(f&&UH*`?4_2^rVWpR(xE6ck^c#%rh0h{h)I7LY zGTi7#MJ~;C!FR~W%!Y2Q9L+uXg!-V`q9`NNH`txALiqCEOPyP=hHC03c4GPn4Q<2k z8vipDw$6A<`OA*4{#~ywH%-ZJtIR$6X($@~Ic%-p#GhPsFTHb6FoS-V`AhVFe&NYD zx%?l!@M7``EsWDfsp*C=OfY?NrN8u!z4#L)9S+{2(bUlEe;@5mcG^_a1LPTNA0&bf z)h8*JevZVKz|tkhnu~P9{v=)aJzVw)9eiT=csV(NPzNq9?el&y36W%#?g1T3@q?)8 z9?Jj`S&y3t*_C>}hjDIbAv+!PQ)uo=bluX5tbKzD3R-=;S^qwUxO2`z+$(T3td;T0 z==_*JeAlF)qkVuKcC$rD%S%vZFdIMaoCh8N__*^hAs1d=QeIhJs5EQ;-#fc;dVci0 zj7VNS82-LY&(fs*&u4!luFPG#^4}+P@mc@>W|8%uU;6*~`y{zdvA0~|ca6x&kf5T& z3Gu1J^h(UqWaU410XRYIwvaCQ7ZfTl5|xMYwp0hN^T|K&y%{x1Zz3Rwm(yoX&uf9; zOVCPuxwD?0oJ_IXRY+imjy?=b_V4+C2*v~ie5hFCb-}|4d*RyFSDdQB35lr>1YeCM2i>fSU+Vw8>E3D=9^NARWN#Ge5^72*Z7J6c?eYeckCtOAG@O zb$!E#%)9`IofezX2SVq$%WM*h(Lc`tNeR~NgVpBy%T?5{pMua(Xv)<&ie=xNo%w&^ zqZ+F)jg8Li{~C9ieyk?*YPUWef!5QQ7TxhUrD+jlDx<}_MS0K8*M-F#DrEH;_LsYx zYbqXBo-TTA11SKqfqC;iS*KlwQWUc<17$*O*hvMezJ3e_%#jhLLc=-bVn#~nCbs%j zU7TzR^N!BSNrR-7T!EF-5k2yPlfAD`W4!G6DLcDO-`AIA92qFO*ucn`n1H$2w<#$p zvYXui<-iVUq;NCvNQdN;s|3ApOpWFJ;!m0~D#@v-2n?4JTVZNKhm$w#?jP||VuS{r zf1aD>w++Db0HZZjT3m8QUFwRuzJgCY-c ztQI;W6haC$DsJ64#_fibbMJsD;AjES0mdQ?YhOxUa4PJnAsE0AI*a$e>qE}6 z1?U)1%*t#U?Lc(_;`huFJ~ihapz{DX8B|T(Oct!{`r0(rWP7e1bpgR zKv-9v&gFPAtuCE&rH>uUqGzc>ew_;yw zO1)B4Tqt+K0%;PUT;N|T$F#NMw7p(K9*gDH8H9Hcxu;ouVKk2FAe9Obd5A@r0{FO| zky^3S$F9!QkS956WO_JZ*j5fSEn*`o%eY1)I~$~voLkk&Ws!VJ3>cv)`d9{r%y0^b z`A)L-RwX7rtNz)=rzxdgDORZyLQVT1F*%8n9Nc$xP%II8$qiXqcM6`dE+6f>$Sc(` zFwD0&B*AZc>BFyzn24yMFDlFcth+wBe;@BMg1K285JZCMRb(;=reBL{lOGyqs_l1^ zKlsQNGprf!indGm0{auTRJ3$-7h)3FC;$4{MLTHL8wSa!*WH2DPul(z>m|Avc@+05AlA2+ds$1NnM-lkt+8=I*Qpe^Hoo8ZwWOuJl*>andi$yT#biSt5+U z6BRY&x__pf_xbxik=-iK6-z%tZXMXou%7YLh)Iauzt!5j1}p-AgIj&QB%jb zU6qxAQ(*%p*l!d)lj7peww=f^ZiLZIN^h=sbZsQfYxA4kAA;+g%g>AWrj> zcU~a;O)kSP4vvkH|CybTmKL|S{o^zYd_psZb67z9)KIVqo+rn-exOtYzltjRIpKOw zSbMy%90EhL_A=Kv1Th}i_$nm=eRwYjHgp+sKr#)IHyCC^9nz}ep{Z_hu$~HJ1v%h( z%?i+Tp#0vxbq18QBIo8YEe5-ek67@ZpuYgWHk(4WkdWxsh3#h357IXfNDt^sfbIoP7E;x>`r!vZ%GiF;^ zUPSmz)Gi?hJdS7crcaTR!netKA=$=)!i{HEf9#EnzJP43r#_#Sv;7qJ@lC`}oj|0p z8r|_8;-|K(I9Cc~?y?8J=J8b!PxtuOLjK*74>b*q7ziu_3|2ZWo3J(3)U@@~aFMgp zg9!KcL_<>(2C@p2I`U)JoA+C-Han1(<=O9_-r>*R=q+Tf`BQk!6vQ zHGg9VcEpk&$+fiwmM;edM~RV)j_~miPiQ<&rf9hI0YN|@P=&>C9r*>@K^-$M#?mEPlcWws0xtyq1!uFuVCoBI0^cJe9$(_6yITeu@Z zhDm7+vdxa5!+;V5A!@(|i39u9w;emsWvma|$vwdkA^(O6GLw=|g5o}%hoRXT*Xt~A7xQN_MxYqJK$HZ!-55;!lpX9mLW__0ikf?OC;l$(qj5(&0vBK%fZ1xz9=*>(iJr4F!CZ@ zW8OejsYSCdbO_+`*Y$)$Q~h==t#MZ*3FN%>wC=B*Y!+w&45+&)?8c5gshO)KeqCcJ8TvG^2n{y1Lyy4YYOwk#zuzTM3#WT^F0(ya}Cqe-75D$B>y5xD`EvQ?*r|0fRtY8 zWPQy&oyX36e3KBuaTe}oqP<;0NdjrVuY4faB^}%z0!II<_AGN!#R97Qh@TU}=YI|+ zZ8~@ND;9mKdQ-=>SW-pmJA$H@O%+9P1@L5X}|lWSxz-y zE3Rr{ffu`d{KrYZ)2MppRu|&w>chG zWIGqX8pTCGiw;q0X95(r7YD5tfAZL^^A_b7IuhVQXH~gjM$DM~r$(2^lA%M(&>xSr zMD}=q*-1}Oi|I5YvTQsfjkc>}OEftvIFA{Py$I+l7yHC~E3lflXIDIssUkx5*6^M1 zhC)kQi$?yt>xe}3v$E+Y+`y7T4CdB0_tbA(?3~elrF=D@`Dkf<@Oo1F<;!kRm#{GB zApiiF!y4@H>gTJUg%Zg@E3@Bil{X7stekcR0_RUeO;H8G`+qmB5QDYxX>bm6DJej?k_tNNKz431o^vJDh`+v_aqK**YEW#g(0Ffp%^TlaF zjxIqGCPa?5b$@}|{s11EU!_&G_x7JcQ$0~OgO13l6vA*}<>CpheR<3uP^epli5L1u zs^Dh1gfQLZ{sgNPg*@5e=VrqMvEK_^cQt{4&W!V%ImQi(_@1#TPh zjt*?JmvbL~3$z}PZ^cmxcVn^b?oAj5=bb^bu|qJsXj&WsY5Zt+4{N@qC4?sbwHh$? znU@47svU}_UzbPG+yVGYob6*Gy-rI-b>mY_3yBnH+NC#!Pp$4vK#kMMz>k(&_?yWX zaG~IrmF>;9+CqMvOG>-C#zE+AULGbQLKf(D_mTNv@*Hf9nrpy71oWp7tlC;w{!#$l zz#$PKQ8*=V1b(~$uJiOkz(JqijqObVUX94xppa;Bjf08kl_ZE}G#$I}ezO?_xb?Hu zIh9U6;hHJl?S|e3S4i$07#tieGW=W>lO;&9dG7{?{p_sNN^D#Fn5E#OTqn1&=?j(Rej z555sQwOJ^XmJU_=BIKQs7ME+=Qv@Wo=rA=xFw^gefq?<<_IYV-W3->) ztykSYe&jz|l!O7nC`q1{me!Au^~E34_kf4XhcYrUx}6hObjX@xpzg3faG1xjfZ~d4 zOS0qCEvHCJMbp7JeKu`a4C0W%ER}^&oI(U~M??i*%Bgks)&1i0za4vwcJUjc>w*|1 zgq}1dK24D1{*6yqGiV>Al|q^+QJ+`!H)7OxcC1y3x14u5zz8#=)F~)-Z1vRq0b!C( z|A>7~4awe=w!WxQ6g;}1UXz{K7nTZ#mRe*fV18W^j^F}|l^8IoC`DJaT^V^d+jTY@ z%)!D!fTUV0(9)u7Yu7F?PIkc%r*wsPWShllo{X7eb7!~}+F)^+Um@jM8!U zk%MZ7SIbYqsNT{@#u62jK-fG8g7#?6g3{wZo2gSCO_#`?8ZULPqB^5ds{*aq?V)s0 z{Qxmy1+j+&Q?^7_fWwVevQhxih*CTfv``KdhelZf!a1JVaUo$Tp^BMy){FjjH; zcBw@Y%(E(-*RYp}qnIroR<3->GF~X^(99#Mtsw=vK>y(2kEgcb(_zKGs8 zLIy2ZNkJa2v7w=ui$APQwMjiJ18yLK@#Z}kKwyF-wSLCaP1%QsCP8%9<`_L(EVs9y zOx<3dTp1R3Ll#HU#=UxR!1YxZTRBt0&lzuR%>-y{by}Mqo>FK(jTdY+V%CwrKp9s4 zRWoL?^{F$-j)>PqSI>xlJSo9a&GYuEB*XWcpXa~~$2B6kX!G!-eRMxy)O*r(yo8*b zDVI4*BVa|+djB!k&GU!y7NR{K_yl2m{#?Ayz6gwpenxwr!*K}$r#nyb@}NN@94WLk zAHYS}%qJC@{P)4l4E)3s*Hx6l*G3C-Risj~vhE_Vt%)3Q1F;{bS8#BZ6+R=T*F!9< z^sQPd_~PM1*E38?{;ht#*=G7F94m)#9iOfZ5vfjXDvZ>LG(tyrMGkLNn<0v-sx^vSRN zC7-Y}&^?*2Pmrkc6L5(7*O|(eOv1s zauz<|1_LpNbI|Sy;TM_lFFy0_7&oo}vlhft@o;f3AMe7dA=V|WuD3|CRiE*cB|op4 zOWTZ=(cj4LrpeOM8H;CREw$@jP3O>1jvXW3(oZdmB=!hQFS6>NIm)twp8m1=Iy#ve z)r82ZNhYkF%=C<8hI^I62T8B{wNvw4c+o_W8&BB61#Z2o4 z8kXyp#DUUiquL*#Q5G5H^PBy#j8-$Xba zoXim*2E8Lef-$BlP7wF73A=?}3jefe;>=-2ZEFdxLy{;SyClP)FP2$W%>ZWDo?G>G z@&be3Y_vg~Bmhnzm{Jkejv#!7QcQEqFRnd_{ zw(MxtOtk;`p=wPHgY&|wes z6-v@}f@=p1rq?U(#5{MRNoz?>Pk(@ib{TW13HVn$_2LX|{LYw2N(8DgIat_kE$BjRl3 z2j#Ra;IP5YF$APy(5Hh{0nE6dd~mg+hjd%e!`ZD5&3ARPz)?3ppNxEui4o;3zUB_f z38+CF!?45v4Q&M{vsZm!4GCLmyPj3zQ5O3nvp4q%5HSa5~&-NESbank9Td5e}I_>Oz>R7jHq_(mL1+Jwnn;kGz}^pnAA zCmm1(++7_f(=U>TN_@ZeXmmsg0+rw)6qP?B#?noCEtRcS4a_19BP0>r)xfV8ylgJ^ zuamTFU>KRDQI(UostKNc^QPzUh=8jZh%&}YY}{ZUGhSwk%j-xLQ4j=@Qt2XQso3hQ z7u&ZSD#y!hft-(l)bI$4%njKs4SgGX0m3xAOq%{&XnSEM*LwF(Sp7%WV6W*w-!6o# zXbay-)~x@YpjYjS_JMd7vULZb`smJ|C<>EdnoipM1$iViFF*;v(w)Pz3QbIQeH_2K zwp%EzAS|q)8!***{FiCEIbTsCS;gOskamDUE4RSOHY_0ACrv%Z@n2?|$lwj#AB7Gh~ZI7_?h~ zfx=1C;+sHBXrH$Rkq1pJ6HFp$Mf6k;;6tf$eISC_%~@zZw@*R!$Mo;EqWR+GFW_Ae zvSJ52nJ;&iaJJ60m6h?3)YMex$03r(18|(46`H`jWCR`r zqX_75VF=3^egrrKIJRyb=>8^t2Xb<^y&AXvyu&^$4l_|h9l2FlOkAG0b|FdxRc*W_v84`^Vq)sl9Lx#othQ&xh?F}8KtQ`cP`$t#snaNWBhRl~4OL1Y} z)5{20(!R{z6woqEljAU@m&CM%v%Zwg+HmM+mAK1w5JE5%&q5oJsPx9)EaWbS%L5GS zg9COMds|4FgD!KlXm6}wEf!XJ4;};+o2hL!2Itn?3gfb-m7s&gkL<=>1cc@i5xL*T z26}q9+>G3$MR0Af`2OPyYJ*j~-^~KNPth6SuJn3@Pf$k!taBzOCgnuey0YZoiwva? znx`{U%HDCsL*t{OOQAqnfY}Y4hlK(8RaK7*5tpAf&it5%!9A4bu-DPeEqI=F`a|vW z(nzVxuhF+I-0FZ%LGTz5PxbY$$F?@o^4Q#&m%=B&M=l&sx2vM9KRxXlOrZQF>ul!A z5I38h3JW&aJCrHv>IGk{ zK3iVEim^Av7bFS213g=%4jWJ|VDpolbwx<99-cmXCx;SNv&DfDuw-&tt>cCI70#<4 z*%#XUVv5{HFW`NFRCZSWJOj7Op4i4?^``yc&IR+~s@V4Vu7$_6v=1Kf*vr?`l}ag)IxW>%kb)!CN&SjD4|h2b z#*|f+1%>M^U(}N&Ed#^U#ibI&(J@?3Az_+te`a6TJmOo7Z~`nqR+eS#2Wzta=R(u* zz^K)4_&hnw!#|u|hqEd@-45QfS{WzC$M+~h>VnvKCFCI{4Lr* zSCLlW%M(CVzQ4?@^YHB4BjPv&pM>$U0!n(=T#wDNo3Jf_gx|94d2@OYyYgr7zKUg` zSR1yphUk>?;>;wO^43-%Dw<5(3GqSCP^TtXBPd2)M__kw2@SjVI`Txb3F{=pu~# zbvcP@pZ`uxd%U-N~PK%7^eZ-nA2NUPnKRJhA{A*8ROLuzqN3YoQoFzW12B zvy*Xgq?GV<%NvDi*`e0v6>6y1yZ5Lo+p{wIj&rknPR>){1alVZ07U2im{&0u;~78e zTndSl*-cH^%aj!Si^VDN8Hw>3b@Wp!d1v`HH87h&Vl|kSH4%-YjQjrl*c3{vyYKPa z{+JIdqceLFAuisB@-j+GV zabdO7HUJwvVP!p8Du>t?#S?CSj?Y?F-b*2vKp4P_{r(I_ zm6mHf0gkx9pgjwlcw8D4{fLHbZ)x~*q~G?`%mN@J7pR{k?y=DQ7GMR8&u>=Sq5~b_KMtUP z+8|G@j;;WOB<;amO6TdNiEB;{WdvOuSfKNj*M-Q!Mk$RUw?(r4ZP^I?6Xww1;23th z`q9>en24JQ^ahgA&yK1#WRFCusUPr$J;diQLPezA?C%%72|jaCkrY$^ zo4oq@h{y3YNhfSSf%6Kc3i8zYKeMx6$s3T;(JvpOJ;cs!whKMMl)=fT0`CRxGTRr9 z)6UNP%%}cudnx=DaXjRLLqMPjU>umLC=l=$PNk4^2KF$UW}-B3g>Rg;JYx+9E)N75 zi=NQ{njGlHuNfdmc*8P~l6CW1(qik^LRgN2MeiQNq6^%wVD5wbS?X?UXuw3kC+A|k zp&lA)Sy>6T2Iw0Wsc3J(DOBbMIiWOoD2jx9{VwqG<%eLS3qkJcE(;F(pxsb~E1sdl z=CY(Sf1oj`K7sO~VfEGJ{*cfuk>YDUNiYzA0+fK&I_t<5_8c(a!Kbjd)F0A5UtzyX zQT>yOn%$;x-2-%>@}jLkgCtew!xDcKaNn13q&V-=_J*0K0caPF{G7lPcWcXpLEj9E zUh?>pTwe#A_R)8F7HSq+r@@I@iR2N$d1-mxfGwnE~8i zEi0W4l2&l8)$?zCXRd$25odlnQ9m5uyBh)@`#Z45bE8+SHeEo&7XMLa4l5i`vRpv+ zrn*d)2kzd$JsYxt?IqKkP&dEicle)|@dmmq+6IZ7kn?iRt( z52}z0oW@Lphh1pfVB(X?{_k3SV9bI8{*v(|4V|Pndo;uAC54-^%<6*5#uU_!1 zK$nQPXBN(VqfZab9Ld}G;{^$`EoDUj&6-UB!P*wLv|PJ&+1s!Z?nMr}DV0KvZ<&7b za8`r8IpA~2N54_x%F2dktA^mVRk>`HqUiq`91|tpP+?I6y+c(cd?lygM_DPGk@RFs z&q<-%&$tg&a&=DAJ4q1P1N|ZFL*r8Ddi1RgMiK+|i_2AP_TBX96D|1J(0_IJ-OV- z_&^HTxS=-t6V z?RgB?0>_lUcbP7k+ENS_Ap-~q8u?oYbp35nY%z~R`dKcvkaO`#D-emONr7?qahFdO z33B0)1(QGGi+fa>-vPTTmn|t0ioI9KvC+&Ha_K+&T=lv9vW@l@vDnL<5N2e9b<+3!4&Wk9OT0eH-e?qO#pjLkT$1CbI zfZdJsx1QDFFsL1Sg0kU_{59!r>#G~88v@5L4dMd0j&%*>MdmA+X>m-(6Ea5ved9~A z4S_W%xCw`<9OUc$`VPW!0pr}FqysX*%Rn+T_r@7e&P#`AuM8HO|5?3)!1=!OqBJMR z075|iQQT*P3*Z3)HNtr1#$x;EJxCr)W!5{td(DIRoJO?ph>Mg&+jjks>+SL4rWL4_ z5^u4#&JL7Jfmu>m=7_|c>vQl**bvI6`BC}h_uHn`JOycKFgOJ|6vzxl$}B!dRbS;z z0HqT!>MEShEeOmz!5^KTE3(H^9fR5Phbzt@`Yw=SSVh9OMtCt0*B71S927k4NbIC!(w(LXGMCLn$rNpdS?*70|HCt%<2AB^3mCvMfQ0~pDXiBP z8Up++Fh1Y1yr1U7>C!R;&hv6=GKz}&Yu-ZW=NjVj2`)Q3y1zy-m{$mq0NN%gGgHH7;;E>eAQz3F<>RN(~iq7q6WXSo1eUYy`tP?R`sP zH(-#;rM9D?cUmW|R`NPdb0JDf;h@#^tBVhzrlUjQGATquhh+J97bIhD&w+U~95oHf zZkjz^EUa7zD1dSY!jLz;+TR&um$N`A4%ieBcE%&01s+J4yB_2|niZD$ z4*3mWJN5<@cg6s^oAg%;_``D65+_hq|8)x)bnX5zFCY;ov@ji8QTKLYTpa9A3FS$K zT6<3$HU0p^(NyRT)VCJAtV0$cA%UCN!B|D{N-QbTwJUzA^KrIAM{n-rS16$IR&Yar zWgq@?BR--z*M+kgfM=jo^V*tPKtPXNp$4bw3gpnr+3~A<`bu=(eLvKvpbr(?0ZeZ7 z8?A1$BrDnOwbUA=dbCI?3Z)(ia!)De-+CVaLMMwdr_pjl#F zu|qCg%a?aY^gdNs(I^g;mX?)uihj zV4LpNpCWc208enOaKPEo0LLxylp;XbK=L-4d;F(+W}&B-rSThn{MlM+J{Ucx@o=+? zEGdrDs3|P=SIXMkC%W-<_2K!5v_e?N_1%r#@xCtJ^KdHOQCqlULfX4X^%bxpJ_^*V zgS@yilOK;;OFHcJ8jNwK&*;)Dpt~kMJiIDX%cIEfgK^uvuOaM>>xV}OZdbPc=s;ep zvZzos5wDPXsqK$P&ERGruCBqv{A?)C6)$3FzFzzs768DsQa1XuP*3|D=p1HxIu0e} z^2UNY)xSD<-$R3obmh9N;Pp0uU1O+^AVl}5R2gjpSD^_nJ~k@Q7MQ(TTRt?1iAezXJ+Qu`>@fwXIlB{E&&DxuJcaa;c-=GDtj#kD)gdnG<@I#ZeIkhVH-B} zEbI{G`_CejF0IT>20;2Vjh%(oK{V*KoN*qC?P`LTN<=gG6)aLdzx_q4p8w?H?Hj|l zT=snZhMc>Jukf9~QIEJFOtiM96>v>V2OE6O8YkdsgwqbTy&Zd2lrpe$6c!GJy=xmu zR`CDZgtD0p>me<7n&j)(s^u1x$j;ih&2-6q7{%c9ejSnf{@}YS%te1WXSNSH_a0H; zpwFMNe~!62QWDS#2I&K4V-8b+Qes9&@cu!$V;0Nzy1i|)^K*(4)H+r#EH#9fimxE? z+S7m>YgRZ42)~17GN>5~q>O3t@fF}C3>Kt7*r4zw9LVy+*=^Qm?oah?3GTanM5*kp zL`R{kLfbF|gL+SojF~8Pm?{L^Vgi%|IPp2!gv5a)|e12jOIy-`I8Gcu#4Rw}435trPf{sOMSgzF3EDGtN&_e3=? zg@i|hEZ;1@sAvEVXyCMQyDm?E7UzLY(QSgUY`bg*xszeIaDjTU;lVQC)Y~Za2Ha6m zbcQB6(!-C$O)j6kzfBIR|%+^gBhikK{fbtSMS~9dVkOdu&zqc`+bA;fx5yEeOQmahk(Bhv>>!+ zF)2w&qLqx`AB(CG-oi^2v(`7#8iNTA@RZJVLRto1xWDN~_k)O!V73g(Clvl15DO&H zh(Q)k>zy4y6xUsvKbV8rBT)2r(&=Wh-bz0ozLXJHahmRE187e3IO zg+UjigKzPciOcE;y^A!^9Ww|cvG(?t{BnSQ`L6-KcEc?0T_-nK!9WQ8wR&)Q$@&L? z(DIp$JIYj^;VNXYgH8fG*zlacE&#I( z(H>yYFLf!3_k5|Q_ODcy5kvqEYrLWh>OVj*0TM7slK=@uhB&JJ-^5_d+YdK`9}b87 zFrM!@?ue>v?L5qaBy8ShsRx{a>Dr;<02#6p$%+NA;{{A3K?>~m&3Ce5F(0r`VLN7~ zih(Q3w*iFO+5CNFsQ0;GRV~UC00Ofe+!y74?+KFjj&1T!^Ci_<%=hDoq zC-U2eTc)>rBVe}nIQ-y<_4i2W!FRk-fT10%_SS$vCe)buiu~*}|Jku^p|9x*Excev zLt3`M$-&_j_4eSbpF!?&D8=?^WU#TQK|9=DEspEpy99QoL|rKl%!zkhQc26!oX_?Q z?JUMd2>kZ<>;Pr84HCmhNV&PX((GY4_1rx3OkRM!S8Uf9i+t!5R`0N!q`5>AKn(!* z1Gm+jl1}@0#bG{hOe?$$%@}lN`a#eSRVg%dYHCU|+s7cVmPw5|d-WFv$a4x|op%*g z{-Ai>vD9uE`&ZyS3eu^jB;3t_D56UOVmaEp{x{G=G9T%9PqrKgrZ|nV>0v1J2o;xYaWf?^0Mq0?RMzld34UylYWKStd{H`)1>&x@IEwD#r zNM#1&#^>)#-!tGNz56PjUmyxv*`b8@q3yR~pcsPqONYrrQSwpLojCTbhGQmNGt$yPFz9G!QC$$6nBf}?POgH)_fQ!|W=IjF zk%zHCj)eH_)i%v3ShEo@K9QoQpATk@1f^q6wE&U;un&+F*X_8%2O%7;sIGpvV*!!e zL`Rm2uLz{I+ls`BHktD>5>XHs*jNlDctprq8q&O0QR!^A19J`RSpea%$GU#wh8VY% z5bOZPNIdH-g_6)+Ys?V=5!*xpwD4uuh@WMG*Ksge>oEVJ>2|ty7vA&P{(Vt$3)C+hiwfa zwptmuY%2J}4;*t*k}5%+^l^S(HAX*K4pb}|>2&JZzceDxK8U;*eraKlo{&I^jb8WE zL7EaP|d4iKI?GgP?YHakcLu8;Igm{6?Zvn}`UXy(x*WcS4Ok4(w z4&dpv?(kpA0_-eW*~09#O3|jPoSYtgpfR8*!@Rj%8Xj{+D>p_7gaKUQg_JZmJ8SM^ zv&^qBONS&F0H!0&9c_cX{lOH2+sAGQ0e`WKt1THD%S!JDy12w^M1ULm+i zg^L7!jRyW4h>1!1pvAL6FcqAjU1vHsm-AcbkE6?y`~Lx1_wN7SJXY^uTEP4Gfcb_&z?=gPD~!B8*CgqS(vaNCFi%%l9%^k*g2-BIsqIq z)SKr9{kIlvq%U}#8bB@wuzHnQAvAe920AR_p|nD(pLMC=V{&}3sj8~tdYB!lS$gH~ z-iwBDWBSIH%Et@o3n)Kk(^z(QZjk_l3eZ)9r#n?H^M?lw^Q4gPS{oP#AAlK%x}yi& z<5I^K2Wmb^5A+b#dhW(RW=lieAgms4`h33OHvm{_*8 z{&T-*xQl@1;_x@Aue^*FU@#y*7pn8`btyZ!LU3}1lAy32g=^;J|;yHio~dR>j}(nk2DWPL>8 zEG)1SsXA?m@}vbCA=`5aWCu3sgfJ>**ulDUj*ftKGd0|e=nRY%Pls3C^60KqUQm*y zS1~ZO()|4kZbrTlPtO%hFo9flNpYn)E$m?c#$l-Z<0D2c5qql6wm~Zvy~^CzuAs<$ zL%obqRcfUpnC_Ve#xJP-GV8p5ds1PE|Hq}9p4(?QcYM^6mBwn$zdr9JM@-}!gK@ARDCAXt{D9E7o#9em^tCtC7&gX1w5A+}~=ugIO z`Z)DnsdY~ek6#6$L=3gLfWxyM&)4y|zZKLK2>r%Q1egMBlj`;WVN-Q~o0Lb__qn_&1t3RwdrOQ2K-H!?R3&BaN|8%V+ve%#j!E|mDi%Lm0kdrwl>a6&WapkQ2ND-HDd3MivI~nk409=q`9fU5=_|6W{o*14sU&Xqo zKmXM;@Vc9Ch1>cR4LLbj85yZ+>|8_ruiL!tJCINBlUYFbFJc7}HzT0vJ$jS^+mT$G zMII}l*92X9S56$7W$lg+^!Ba~)$YSeMQMFtARMj)_!(~Nt^|U{IRA_L{zH<40^73Sk^|!dZ zC(+qOiGIRav$D3f-^BURZ$U9psd)l*4N4M(VL-|AWGwLG9_TcWkH`NDiZyQ=EOfbkD@TU_s7fIWt1d||f@GRnuJ zxoX|kxe&X}o(ag4Lcp6U=y5=TK!;0%e`~qwd~4f}+u9qVy0cSL9cBm4;T{Jj*afR) z3}2{}gD_Pv3^u1ACIVJGxwm5_Zc}WvvOE!>&Vp^+q^xAF%v4p^v1d-WSqwz0e`ChH zqHCelgP!BI_Q7?Zj}SS`ULs?r_1RNc!JYOL!j9m*T3oQy&j2>8pwVwanGw3lt4O z6f`tc_6M^-M3<9;fnYuQd{(APhi^4<5UBB(SjZ?nMNA-L9c;9aKykdZEDaBL_e3g4 z1)%qoVqyogA0)v$Zt$)_?>4^{0_XDUKJ5(gM1^vfa|dK;s8$tzW(rr^|L|w9PdNMC z=OSZcNy$jm^z`ClLTWc*j{z8)!TQFGFCEJhkY?0L!%uZ`P$L1ZN-6R;F5-k3lAwnI z`en^cw{{Th-QwF2?2AHPzI6i$sw+9$aP~QxE5=p%MDf&F5a5qyw4C88sw&VZZ_f>{9=;DW+(?VcTmkBi zjC?0h_KX(iNJxpV0U`qhF(hz49s>9s*sOG{T7ZWpWjH<|P}%IM{i$JRHjVk3FoZ+E zM=Gsmkx)kju)&S-f^QzN*ARHmP+r1y1Cpj<^y}By@(=QEuxH2ton2i6^z{b4rJl{( z$N0qT0F;VkF@s3~=#zwYYyZPTdZ0oJyyU&R=pmWw6;2meNY^XVKr${$!3U;rM!>iR z?qzT%uEG49JuM(YC~5Y06AMxg=%8V|Q}8R4u8j$~{uOmGjRXZqMgX~GnMj_Iwq5}+^RHw#!X0Y-Pha<7kPL=M$3P0*R;+x@X=fWyOiG}pHW?NFYsh{(uCOWVA)=I?5#a0bCLpbM8^WWa_(VpyPW@M!0qlx3^ zb8jsNS{AH@f?Dpu$X0kp^=f7!lyBS{q^O+X!jE=2{Mb129>lSDGR35%r}J!fW+XG4 z!$Ij?(*fc-q#k6o40L?u#-Zw`f(m%xC6uDu=X&zJI7nxp*guv?WSe|-E2@?z&4h%aqRMq#RWqp{OH?!bYo zZ|@0Ju+#BM2NCmRf*Qn^%Qs3no?wwkEad-}_Os-uau{qJJrD;b*b&dAxq(=;tIB3~ zM{159|JL360?O}h1r-)<+MPm6atbOIUnzPp7LV1gCO|(8?*>Uvt51lV*iG+OMz6^L z5%|*j`3Qt8Esy&8e?2!pzL_BiE@F8r0L=o(^D3sT;^}H2#$IDo758%2Ks5gA`vR{i zIfWHzXeFNFk}3!5IXm+rK`nAHkVx6A2q-ZGc5o-fK|)Y|LeLY41J1zc;v1upzo+>w z2Otut!f#D9T*SlA#+@#wd;NJmr8%M6Ei)U|p?h5C+{8^wn+LuM-(6C!GoufY1=MIP zF^3ZLfrIrq_f5M82Vr5|-xukpmODNI;^)lF6-WIkaHxZbpnCL>o}S))ndpg8-&AIG zPMD&qDmXE?BBej$qeQX2>zg@dH*k0};l5=sKa!&q@LXIr#VVesI+TRE^4%&qL=c#) zuFn}bPHma$>c)PhxW}og`afvvuFb3sYWdd?iw0(;b{0CFQkS-UzovUwjS>(aoNo(R zm9XP6lPQ@%%~aXyZSIUxBB9Pgwj09A{7r<`B;=H}&V%Ww=*Zl8&4C~@15NSb9 z0TQdyf>M6ZYEC-Zu4y#Qi+W-yT zwdX<~X=yxeLwd?Zr%x>0J_8=nCycnh5Nrv13eW~wPr6=5V3Sx-QZTdI*4J0-yNz{w zABFM}r~B?n3Pg6j-xL`plM_%51v;F?WVMo_;xhcxpgNih_n*sTu&ZMdN_)cfMiexZGC?)d$qZkegU?V1|N_Ogglv{plId;&D8I z12mrB^?a{F-^XEM(p`K02Zx6!G?O4QhxEB_*xJB3XxW!g+s7l{@b9k;T)%T%7)2;A z0k?7M7LW=pUs;KZXW=sM-$zM+WlvRUuAtpsyL@fM|3ICh@I5{7I7MPxbU=I@G{Q7! zjam;5Riq2l6+*@`cGd$t4_GU{Ml&!kfQJZtu*)m+m|VHXq*u)SW!`wzXZy=JT3mI2 z{e$2k2!>d~%$}_F*^ADB;7Www`5-Fl=|f)L0Q|uFycS3#vCYojjT?oTo6Um_Fwci$ zqwaEx9)XbyEDR-?@QC6$Oi#{WoD1BZta44)r>o#`buQ=b?o*I|AqR%}U+{^*c_;^? zpv=r5LnGOE1Q9q1sPvd6FPTY9GKL5E1_B4&@1`?WkDVB_yYU<2p8Qk{TmZ-gwh?T) zpxu>=t-wM+JNq!cw89k+vg_Ecd%>Ls-{RWIB054xN5|n{i6B6Yz9I6eJ0gT1p$wM< z113R&k{WZY=ls|-z!<|ZS`lGb;F-d5DH+opjQ{+CmsQqTWOpg*n%fxfvDtNK+{Vqm$lTG~d@JY4jT9LW6H))Yz#+$RKWMOr zL3-X~H7WuxM!!G~H!BzLU|HztV~bvj6{Y6F50|jWH&J`~rO$h~z*!ae!?2wK-3@%U zwNNm_ANnTJ#sGHzGpy zs;oS5)XxK_M~|52zo@dujEsxT1-3Bv3f(PjojzSU22?vr0?4>!I62vnFwuXi0J9kX z^JfWOY1wFws3!-z?LWZ?kJofNjYUmE<2x`6OPK4jv$Z<^1OEHi#WJtHyn7u~+s|Gs zgYRkMWN`@qT#^A@ApHX94+Wg~%hj)+7_70+N=Icu;6SV4E<~=wi>v)Yq?qHX=`Do?@xkJmA<|W*J_v&b)Ot8!nt6Jp zdWo|2%3@VYhU;9Zm1R5r481)~4MVm@gtn#>mrY)v;Lnj^gU`mCC=ga5w~^+dHVCH& zQ*5YDGu!&82RZXS9Af1#Z-6&SH^{+Y6qpoD`fP_QpQx?%$2r+JC|}VoC&%*HcDGkZ zqa+CA90USxeT??)nnQZkG>v~MEb5;CSk*#f)@AgZcy-^6h@NCIp=+cyVa$U<{Jo{t1*z;H9Y!aBCxgCbe-393>^WTO$Q7$k(dA4 zTXJf&C7B5ENIij(d``iM(*{Eipebz~KuiYr<`ED{@JJ)*b}Jw&vOiVXGsBbTe}S()X-vedyEN)_~;Is?anaiX?0LcYM zKi*#~M!Z`z^7?Pjy1psm84(cKKA$+cu-$^JvhGFqZ?aBAXRuelQnTTub)b3G5w^!b z+UszrP^!N!&*ojgIGuOa*=b~3Mymyt2!3>5;hr;pl-6c^H&|uDbKMfQ$^bMHN$hH< zj;qX8Oa)<wQU5g>oL)CiR@Ls1Gk-SV{5ozjgA=Lxl< zNn730Khh>mhP6Rn2B*1w#DAt;UDk;z1Iw==sxKu4epMW4{6Q`fgie6tr`}xB21{=jj@%ITj>(VKPALq(#)*XNbkmQ}yEiRw-FB#$1ibZbMeoqFoxqq$ODpQ6grD$42!=okhV>|vw6vRG zzK1oCfl5GDryWROVed*mzn%Oyw+);dF7qdrL0-dUVH40bcIs9$6Z&h5nd|=Ew+G*LScIQPp~(!uEhq$CHu?*B&y_!RdA2}#`J4Kug=J1M zjbSxV#on6ysL3h$+Ir||V0I9p25`r*$}8k@j+P6=+p6j?LIpB#Umw+7fY(#oJf^)@ zEHPh4R`o`aC5}jXCQvN?yl&}?zklJ$XM2$e=0hNk8wir%;6P8E1+$Nq;cIo^*VwiN zhiSALQ_FWbyX|eMvW+7?z9F@Pn;6$W1WqgVj~I)6{t1~7H@y{eN<4l@KK%OiTW^0q zYgUKZ3f$?xn`9s-HQ#hx$wuvaT%`TcQY6?r0cm*IR0?kr(S5wliQ;$t>L9zi0BJsf zUrWtxtZnzbv4(1wSF~55VlX|ag@0r{qH%#H^$nV0LD%WDthSSaeoq-w)8&2AL!FrW zkO#Nr%zZWySLIYwI6qppO~O}h-FN`QK6rjngu%eT(Ao-PZBYC|&vA3_oSz=#x(D-F zWjFUX`}=abJJ+_%FYDLV#y;&{9B~KYid>SsS8&>xv8PeC-13^(J1AuP1&TF9m_R!? z>940LFbE*V4g4lT?%OV4MOD>lgEti@k8dkd;Dw*Ek#^X8%3wTQM|6J7q9fM;=lq^) z^S_qcn1Suh%`o;WEKXk>NS?0aZ*aa$423x za6BtJ1_WT$)bO9d4K?}6$<O{i|i_jyXwaWe|ty;vS{179JWZF|MkB$jZuw zmktEFv)#Bh9WqY}emaN?uBPBGRK+GExrt0T>z^d$k3IQqJ zN}p|5CX;*jU%Ax#_vAG-Foa-ZP+QPO5D@vHcnQ{2s+HL$Wy@?qR9Bh@bk451_I0+_Z28x%usIJ=<4YM6?|RQ8`aC2p9iuu;PpJ2$^$+c z-O+P#qj1q{rCe$bTxxT+(0Kt_wbnHOBxJRxQq8TcIOw0?Vr4irQ{VPd*_QPWl)vT$ z)8ZEbx}qvrh*PPFT3B1K(5`eF3nONPeQaOfpLnq1gmjUDF^8}t{?%1f-{Hn(l*Z$U;&qBUX-@k)pH$`F z!MG#%)_tf&P=qssqn@Xi|A5~P_3Gz1IYxsLVs*&`4RNXDQ{4>eQ&6YS{Dj$$)GzMl z9gp2W2&J`S>8%x{L_~H#e5!6?JwJa1Ak{B&1^&lNgwobTHKMcQ6gE%UYuzxT3o0U` z2XjhF(WH(EHu|Scp^vMptNrY!3#V<<_`o(|i!|GzWfrtKBdZEo!bITUJ5;gJ^*s4r zu-6RgBT8J)j$5#QF)ZQ*<%pO7Q0VezR(6E2IIUofWU1xcqYcN*0<#W1s8&OI0GWl7 zV0`#Zb(lw2P3>Dd<1?6-c?j+Rk}yxF%6+>v;Okw0slS8$td>@tXo?rKXt1E%Uz;$6 z$tN4pP}QXq>`Ou=99EZ?`=)N9yxje42p)wnOJh)oU)kI8!7sqbXnQ5uXJ0(ieb)j^ zvO8;&%KseGR_fIIYFmomPEcH3(4)|n@Hp)72R5a#bfwr;JtZ{fMKP^X@L@eTosa(*fBiSevj2!XJvn|4gMQAnQVz~ zx;h)Osyt>VK%@#(DZTNh&aTer*FOM9f8{N%?*TseZM_GWqM&T`jFR#O={V3dNDBlFX@ILdLL7Bx%io4tp@pn-t_;6YMSjgb2K z3xSl}XLR)L0=<%wGJ^I8x8gg|ydWGY%v>m*7A$xp?R`hTBSOdbqYoto{Co?3&0!=h zF-a5t%RIq^?N#FI_GR=VYW455UrT>Ep!=E&BL9c>L&KLi4@ySHk%U`A-$ES#xARl$|^ zVS8-bFuItbYgB4?OxHeZvXId^3v+M*S@) zYnWsM61`9S!7Eui1L{IB;@rHw)R?UM=Xm4CN|V0F8Th#AXh-LL?xK$ugf1#8u|xD7 z%G81GB*cXv^)202Ff9iiO!7^zB(l*xlrd{e^mgeDoRGihfqXofdr#MPPnqAtJmn1v$-^I2R|1VC#?D85kA@1p_&<_9`29`v8@ z2u_X_l*`1Go?i#yt-I9(ninw3%AH3K!ITRCx63!1{IgM#Bgn-^VM&uj$7Je+pFKD@ zuA4Oxy+vh3hsvEc2YQ`Fjw%*kG_Y}kCR`;b_d*mx5UGi~Nv^CI_uE{#FDWPh*$aPl zZ1C?EXN3Oc8hF355(PRemEJHLmz1o5mYX#)d!mLc zf+>#HBHr}11&>q0X`E0XDk87+BxJLFm&h8apU8D}67}Cat%p9%v-76=`L%_PNCw3- zkNpMrHxi({!mM*x=!ly+?mJ!CcO_yT=gD`}#!E9lJ~ivx`X#%1Qy{(?Y<8sTIPpf1 zpBo2StqSxJe8R(h=?+f@3L27T{H(y7) zr3V%dKb$NNjB1vrKFOP?Hhywzd|XGy|F$###AhZ~C%1|B0;6;D^T6zl5c`L)N+ub=#gk1E``MJu7rOA>HYiULU`D~l71T+FQ0>}b+)LgA@7zHu%!l^(sQ z=kd{Crt7vwwgxMH;}a7jPdHgg_`7uHf&w4l{4pk-UYVI%nGv=C@sq1_9dTD@4yx)S zvBpOd@;btkI~7G~{-grg9S#x?aZP)U6rrP2QeuYz@kdS%Awu#%pSjQn-X{*LW@6us z4Lx0!mJKZpAH+N+c`#~lCeB>8u~*S%!DT$R^$b!f*i*mVy>+L`^|GKcD)Y#az-a8p zH2kyYm->}1iz$ID$o1==pa_qOy0<>GLyCU8){EuG>1rm8mb7dec&j-OxYD$xq=MUW zMoSJ&;K@qHNP@k=cs9;akC<(=>L9F1aZH8y!XbgJzpqbCRaI!qO)iZwfP!5|Ts-Pa zT**e+Bc_5<0`%)?ox0)+O?3X2f=LQPB*e=USqap37MJvug1POj0NSP z-|>CPrWs1(&&JLQ%bxEMhofO_3uRlCBV3fo+uc@E2310&mGK8=L@p{q9_YCL*B#L~ z9L1fEphd2L>@7tR`sUP6k7Uh9i?r=z+$?lL^5=-jNH7rQZw|P3Gjek)TxQ7w#KkI= zClB9)(E#nSw)h9d3d+kf4Ghd0fCqQEoS}ZWOYsy9sjM6!9e8Y6VXF2fNb(`1 z#B)^z{hnr^D?Zn=Tg!1f8e(J;@RL(N*^eM@;4^?+YM(d|ME|9-k?VyS2|1qw7k9+7 zB*ipYVl-rBC9}%WLZi<4aM?Bj6VoqEj=Wc62IZF6=OMB1kZWF*Cnt=9n<(`SH`rBG zzJFreb^6IyaN9h5ppbVd-MCZ#LG+hecb5nKAN~FJ)`;;~^s834w<7cOr+PlwlC8P& z@a?IjGDiL=KK$qKSW=LMf!TTLSVvx+l(-QGyOQXd0X}U*oBF2cpAgarc@$xH_un5g zcKiEO@^tt=w>}r4J|yR1eK*!T%wAXc{_Gy^#Y&03o5;@e`O_d>I&VdEgoMNl;HNV} zK4qKNhAjRSx4emVI)!8VsQLia3+5u+YH1j%MW9>G$|BO|OwYVp7bZ?6*b^jtj@xDjs}X?lC8d-9UB>%tdhLg z(we-W$GYn3v>{KE-#DMGJwmX^mvVDcU!*bCZ12aj+lJ)VZfIkD$DbEJ)$mrQLiLqat4T?(*EvF)^jD34MmMckhkCU?GZEi^2RnQGz5fYXJs=O+rq9Ly(r6 z+S56{PdYApJ)EEQUi;-Eifi5|^?%BhF;^oZ)X8~lD&woNdH1uC77z8Qh>ASgFmzs2^rL7F-Z8P^C@O zMy%XKoka6q*o$n%!Ae{DagDmF>gMq-Pi$_n;qMnSYbh!7KHp+FS-C_NS>dFObFIU; z?bq7e4j%&0c)d>!iMfq_yk#pWBFZBmB;1@R(3_d(89B5o-o!wF)C%XjR*4%5gG7k2 zxX}R!CA94)JN3%7R8-gud+tG>%UmXBVC6Ei_vqFq5z#u62W6WO!`3|Qz0e*rwxAN! zkeq7BcJGs!zEXNvn0oOF@vP12hBLqKSKrR&_SD|6$L+EbfUQ3Fd;Nfl?EnUd%>NFk zUW90IxgJg=+1POSCca}Yvay=*Ku2~L<;S^OYD#%`NJ`L{*RMSHY{Gb ztaarIc*Z$R9iPQ|cCpx|9&F4Hg=m_psJLlrItw|fzw_Z1)JG753{Q?X`(`$@OS`+Z$F22G_s9PElITOc zP$l)pu`Fb7H=4J18y}^R4l)y!5}m)f+>VNh67raoe7y9w!d^c9w9BMV@81FP#7*+@ zF3PV?y=tqXx#5wUB^(^^^M80w_45s$=$b^=I0b+DCM@r?ZB}Ap_-`hG=Q*jwBa><~ zi;#+mX?(bW10JtmNsDj;m>s`oqGb|bgt_gn+BDGAA^v27OE7-Sn>g{ix9xD`^<}r= zlnHtKgIm`vLbx6cX^=@unkc6G@#(FUl1p6dV7k{N2ds+ zJ)hw|#Svs;O6Nm=iuxX?Sa`ml%_uh?P4{)G#|Crg@-Uo~lrl+)yM_nilC-Lyd_>ceA5r&Wd8$uW z0#4zB1!;)ud3e(l%EpGfPL!k^Dgts7P2PB;&KP{s2-rzeJr;3`8^Taj(_`Hn>|gRM zoA?Li8&PMNNTR~uZ{!PW2A)exkyAlmrh?E#{Etnix1IbyP@%eyPvpwU+J(I}zJcI3 zI<}`jfMVCW9kWrSmFDnHtcgmsS$<=+lY?l(!QwVRLjYF|Hypv&} zm`FgyoYccdOht)_52&f1QnQb?bgPMLk+hJ$+m>>@;Qwc2yJlf|vgg3en6jD z++v7qA1Y6db|k-6pS}>I678;!^Drej<0}(cbE^_M=Mjr-n&nvG;@@?&>0s6*i8}>F zC8mkiZkW#s55KTDzE!vsEZg9BI~g-R`|)Z zJJz26{wW@KFFQT`?Zw_R1Oh=oL0fp_nECrx2<=VpVD~+n+c|j4jo*pD9 zBx-ujq{?A`hRc((#@)@)Li3IF;C)Lq)j{B#KH#TX090I&vCK8EZfZ0rySMwJ52Xo7 za3qe=ZlcyVzw(YU;T51>?U~o+AhBOOu>ZI~OaEy7UriHKZL&I`xF3L5)6Txc(MnhP zkIVTz+=9!g7SIMJattub9Xv=3o(?YfI7f_)|Dx~V!_KZP6GT|+1+FiLVGjT!3&B)N zExQbl&Y<-LH9r*%CsR?87@DXh+}v+=23b|6q~~Pb!P%d%C1k(9|7yD1m__dmn(Q9K6~8#lo>&89;{MN(B!>WS69zVNh!D}dt~YG z^7M4X*hdD8)t#0@K^G?9e&(eW?H>xU=vL?|X$g96a3GNXsZ(Kb(L9Z6J-&~H8Cd!5 z^cQO9;^3vA82SOIzRCU>1AtDCIJpzzJ|Dus~OaUgr$5B3scT0jQDr|f!dOp`7l~w9L#5{JSV$;FW4RoO5us; z4(IQVHJS@lIi~c6k*&MvRJt+paC~^8e&^0{HO~&5k|H~z)7dTVf352cr^W*Y4~a0YU&EiuHD&D5hP_jLVdL({ytd4uW1-L4 z0XhxqfDL*%zVfJnUR)<;ZERcz4m|+$zA<}kU~G3ewi%?J4paJ~y`X}Jj3O%O*8^TzM+iSEF{V1tS4{TE+Z?1<~+f(Mlj zvqjFd-iqayUG&IMxmCs6xd(8UDWA_C-saxqcUxL6fe9Q`5IEgmJTS4a>~v$+K+|4A z5!yl`M= z5A&(^j~6kiP&h$#KpmQbk3R>wqe`bGHrd(PWXOl-)NPDEsL&4H*;7$D-6o&XQ&nAG zSOM*TxCZZ7PFBA!wI`6?l;+d`iETAmU721!7qICgchjZ82Yu8FqcV^ZPCl4#^=t);17}0xTa99$qa{v*=?Bo z1Umr1Hh=ijh7~hRm`qv<;BTPB0MwcTTNfT-+YNoHt)Cd%>W{9WAn3xv-_b|AAUx@^ z)_y(eb{#d|O=Agdq^rMMtJG;Ca##I;oaY^!)nSA_55wSu_d5BkcBb+NbU z095&~P^fdI_;La6R952!0}~1pRztY@8>q(e6H%{y5P6iUZVK<%?pdR`FHA^cYiv47 zr6a6~9eQZTD7yH9@ilMpEy<25!kl22C@&%`EKjFY*a?Sl{xnj^@nlln`n39hdcYi- zK@=}Tj*kgPxl8jPCNZ63-e`0nVT%DsMHUnpqsBF!x=Z7g&T@1*q-RB70YtD|*D zrQx!m+}BXkdv-lbG{MB<*k42$kGeACHCzk;%Y`^nk`*(!YLKg8S{kF^-d=0#g@HvM zz2F1l+Uzh~6*ab5+>oL|AuzVvR@kQ)&Qs3O^~m89%yo2#TDo>Q1_PBy_vmy%nnIr> zfX`(v$dz0kdV$xk@rEqEiDr1q&~!%fYRaoGg7E-c1I*0y(X96TZqgQ&TAeXmCXuq%@-@}OJMua+}tjXqXx`7z{3DB(-#|u;+hEZawrHTfK$4i z9|kIqq6}fMi;cpBZf9ToYcO;ZnqQD;`ZMXeg`X1wwW0esutmjKX|$+yC|NNZ&N z5P4aRxop+%@)iKSNYH3TXZjXQA<@kb*l7(ARr-7s`z-NC z=j_zz&0Q}Zqtw5?QW+J_{sXCIO9PEcPV7tqcu2E>jpUHJuY;MnISS>Wbgm-w{xI9> zF2Hbic}he`Xl!64e~?ffK}1BTC9p^}banCKgDk9!9$RFc^{o;B{fbYa+>n=d z74#wz4#o;fu z>HF3!>nIQhcBTD|d6R^^)1wSW3pXWQ-SfCu(om%yc6BpaYIN#r#S=zshxe2XwGABb{k&U;JM?8OKLaYHy8-T2aydSrx#?SMG|GYqfgm2RV}#Cjtxpza5b_S z_MAsg7vK5rGuF+izqNk{rRg{SucU10*20i5XSZsUl1_Ko)yqi7Gd7ftSLkYIbCkV^ z52XTm9p$x_r4=*A-mvnq%F+P-4^6X;e`p-aj;pm|LZZ4g$}phT*FQPR%*fanFUd$q zKt}-Ll`2bKWjZH{W4uv;)W9E`?FYsgJvkGL^FnD|7Sb(5S9Q4rcwe$;P1G zCdT_GLhm1*Dz2y~_3j-#E~iA`y_oMY2PZj~)x*lyul+LDhZK!@3$%`RD?@s7KSZ(b z;{i&%AKI-{84_wG&1ZTXfo@7KNW2w?id?yGi7gC_OPsjyQGdSO-qEICYUT%D-uCY* z--?~4skfWzn{Zxs>$bZ|i$%(nySKW>epgisFMPLN_^EARvgUGVqO|H$g%oNan!cUM zb$GXb|G~o1RPBefMZ3z7e6dF&4{<&`5pcCL=gN9vqM_m=4O(efdp9dyvN4%x^qRI%3B#4zb~eC3k|CH zuKuD_IU0PP+dH@6w|cda%Y;?qjdcX4i8g7DsHhYKU!x%b%kO<7qC=l|LL?__6AqWx z$;e1hkbmDC{{D?c01#yIC;vSwEYG{D28C8#V8;N32=aDoi8$D(o3wv-xee#{&C;>N zB!SzoE4h2W=Q1VG+Qg~2C~3WjC=cbO@Z>}eN##J#$f0UcbF<{#`(yHYdK+EcMeiw| zk5W6|bjNz`eULSD4Uge)$K&~*tkt&=o*Cr(kwa7Eg^jH}r-P!Te`GluF3=@dfcIx1@4Ce|lh0W*Axhh3Vq%Z@$>rTv z>N7L1B{oy6m}NHKf5arvmLED%b@(3Ip^rFifaMw=zo@v*%gcLJ`k{IKoWV7@6D%#9 zd6y)>1)eM4eT-9Pv{P~@EYNQUI@rHM5B##cxbY%5n3bD7u@?~Y-axX2StX2Lab7gH zXUCXxZzgqZ{rgr*_fq_|5<|$P_Kg!RIM+JQ?KwkRD&HOBvmM$x6l#|`#YsZuZXEya zNz;&&zI;p+RBvsMUHfAnwB|pY{b}eEHw(fB<5U{H=FPAl@0Z=UAjz<;??PF_>DVq$EBxeYm@W3AqfN!$}3=oD&lTU(7$EX5uR`>5NvR#DO&e8suBa^m8jdGb;}S7!L);NFGQ%;7r_Bc^m&TLYV0 zr`-)yXefByuoWr9NTZ&b7v`tON1kV>{$5?Y+z!YSY86IB;HW%s7!QoUz>IxF!!h8atpb+Wnu90oJbrJWPwqYo}7Y5^Tbx!VZT#Ay9 zs0qA*?`eKMj7r=JU`F`=34$_OiP1QLN-?n}cPw9QbrG(P1Qhq=l$79kXLol% z+j_W?=5GdmsU=cWR`%m(5=wh~6Y<*ma_`~O2e4J*U<~gYCz%Roz?~fA?=o_ds`|ahp1}U2#dO z{C&sM+(q~x95#n2yIV^0^N*e@ECGoo`U}0Kkr_TA=tMiQof+ZEBoAbH9KN?cyVKR% zHpoIr&F(#WKkxy_r#*8h#bfM7SdSur^QYPAfb8%(3slD|`Fm(XlKjIpA%aRuHF3 zK;O-qJn>AjV3vIz7B*h(aU;G{LT(=>vXrG919BRw{RrjS`lOaP7AcRNq^B5b-Rn*D zT7U~39k15eb&nC$S&9*lEOf?RtnU=w>O~2T06px&D992k-W)y?o;HZWf131q2FkeN z0WuY_PjX3$@Uy`l(6@oMQ~xLbdr`VOA7Bf`O!t_I>)7oA!cSr7;Nn?hWMOD(WneM| zvyw**8>S%_%{E~{q*&TTW^V3-TQKnj3MiPB z9xD+)&~fEO>W}Mk0dmZOa~2zGfdavFbtu9^{i2kF%HLZuMW!{byP*{O?|l6V@+X)o z!}^FqsQPs0QxXi|zJBo+>owtu#=|FL-to!y@98P}@Zoo`w&@!VCWi~UR9Rw)Bt=*u z2D392Ul2dF->`lqKLS~jFb%m@KbM!L>3vd#;svW_?pg|{kF>&m{j`KAAjB&)DW^RB zfARL#QB|&8-{_4EC`bxONH@|TAR%26(g;X*OSg(hcT1;8x0H$~CDJ7&NOyoS zIsnj(izd!1rf6xJgYzwnleGtOZM3UwAigHGZ8Byc1g2z6)y_@tmA`wNWA<>aFLo@g zsrLCLB)pa1{!bsIUHMO8hG9H}r#srYX>0&Ck<#<+s(K*hT(5R@pHsqYz=d@NB^fbh zKo|g`2js8k5?{7R%sC}L?eO^bk?}&OvodRm6R*KXfd0mxS38aUxU8yr&h!Wb$}2pO zl(aH@R#!ALx}%P;7T9+xP{j%Dxexk%Iu>Am?33{%p6Ax;;?c;0+* ztKsZ0`D(H7Gkud66cuF}svkE5ufT+s@*?H42&rsH`IxAkEA#FodqNYcq?9Wu_N{4a z54aQ)RO3%8f8F|78AE@Z*<(1}-4BwCVOh*+dC4Z&J-l0-l!dO<#hKS1`f;}ZQ_LTk} zOwTlWz(0x_92U0!u($G4RXinIY)vI=R)1`Q9Db<8nt`#1%KB*53K$VU-u}%g zo(&Bc)adwl0BXniSSmwMya11LzRk_T&d%xjXOrlU5Fht+Nhc5Jx#u;-W9$r~H<)opAxa#GQ;%NvT6f?&34cK87dY>nTd|joQU$T2uzF8r#*!zRT5r@`OPv% zXb;5hz2`q5gU~rtWM!C%IyW0;SP+3!#IAOAh=P)`ap53z;jE677`x1%OAX2-{CS#d zf9k;N4;OJ8bssQNa?whjaIvW|>)An;H|Wnp2)VMO{dA?J-R#W7K;;bR&vdp7z6lK{ z5hULOw9{PW_b4Mhp5cc0GX9M#W+HZr5#I98y~h*uYc|}QT#u?Xf3JmE^o+J+=rp%Y z%g;?MYqU(Mw-n=;vPi3Rj_p+vj!cCzFpl-~L_{VwI;`!3Y^$({Z+K`3zL^_%G{ zaY^rjzswC8n=EAKntY?B!68i8A@oOBo%#rNAHN9-l&qjZo>e^PRI|qavADnX=`X4A zj{HiVFk2Z8;wmu7OK=SV>4F()$=DlOY&1^_X8O7>Fn!e0(vr;GU8mvsWn^GLWw=o5 zsgjc0+#ekyDa!AY6Bqr_`q3=e$3p1l;P#*qd%4r9}j7LGw^J z74|z@vUsT2+#m3#o3k_nfC~b}`%ml64hF~cirehln;&q@vly~}l=(ovo+1vwQZF@g zHYl3_a@k#SBtzOvaWggfgH?63)VxmyzWSy|0GMVjt?_jqqQD}$5)iF|HfmwayxIr> z&>RKFHvyQdka`4>2}D{uA%u4cnNE!7xB7jsYN8~vE&^`>Y@YFB3AnWsK-E3jrI|sS zlO1?nMa;;=3!*g@j=axAuoeK7l7`)^SG*X*8Ewz>>Ctdi`!~dD4yDo1XWX5x#E)~-z%nri7;8Wo$fKv z=o(n^{nVNvRco;=x^;7L;kCne6JsBXidlMMr2lxZe)a8BsG9tDOj-0A=RQsMxA7Fl zb%8QMrGUwO@uNWef`n+K#)$2-&3@4_&eGN%8Q{6g;(bbT0Cp(ObzV zqI2kQQG>EzMF4%(tyBBj^BnPgte(VYzBH=AP%qzUIeNMWYz(MV{kppD5 z>u|=PsnVH6%q8DBrJhgI;mZ(49LiVvF}b>W9$*7d8XW`i-OfQav(C#u+Lin;2lcwR za&#a|WDl2Xx<|hWM%HUg=zQ^hFbxJ_MOc_88TGQ=n$J_CbMdY~-I|Q(yXt=?h`R8CwM@@HYxhf5&ssW{1P+$qM8n2oIN5`Db zt>-eK+5G6fR%73px)Bi+(o#?$j+&aF%5|--sPt*a560dhMRPZ=o7Z-Bo@glRXWt(^ zXnf5q(xs}?mCNWgMt6fbp!GK-e)5oC<+>Hn$;`l%(XPamEL-OLLsbLZ5I?n&Lgmq+ z{7Br1votq@#1Quwh!n!C%|Lw3WTS2!PH&o4Qq z!C2rLRz&sj<4H)eFgB*=Bq}N>;G^Ww2Gp{oB7onhB`ICCn*iP|6BED#wiK9(u43QU zy(F(=F4sS0QjETl_v0)ys-xO3CwLx%Wc)rTP(T|Df6BitizH7CowGz;k|e-Q!gK1& zO~%7!fQj^wQqBqj9g4w@hvBpvCoik5H0a;juLCj&8y4`6tjaV_qP;&;c{e<&dvSge z2rtGzc;av9y3X&)&P!$RGseET%#&(n=Z_B1tC5%azv6|Hz5H@O##iO`MdnW4{AKiD zyW?V0EeR7{ZxJP>_D^SC;4x=4{mg*W+RdkjRjWBaUZ1>7D`rwAMSBhT-HMUMZ;7TP zXgp8BoxHO(1571|P=jASz8lB=SW=LvF77UC$99522eXVCZPu z!`+EiR8>t$P3$sKqi1AXS~jbio!jp(hk?G?vz*~$J{dLgPEK9ltUUnuzOTg^od0EE z2VAyS5i5-AMb6BJ#@`mP(yU*Oo#!^X|DMchy)Wkds|avS_qm^I+&V>tMNCZ0-}#*S z7#Uc^&GSKkm6v|6#%ow)mxz$Vq78V^9^(0oygO*dLe?F;F*EgD12Rc0Lj&bYLEFO3 zd)a!XQ=2n$+JNo5%`8TKxfWR43bT7{MoYJWJBP@ct)26cnrHwrRhcJqmw~>`CFFI=ZaB%GjvLl{+j>(C zJD||QlEyT&8fgF89(L$AGUi0<48_%t)Ccb>z6!N>c|UJD~eNB6c#?!)2o@wFatGgc&p{FEh;U%vlGbJ2TOZa;Ejlc zTzWbtgR%l}-BA&_p*?x3tfk$#Zj?@I@QZIH9#4*DB|PQsSI4;OE1%np0ir1E; z5UDuDqJ>Go-3yc-!a9pA&CDY1bl#?uQ*{b82=1bQpY>bz5hZ(KVOG)6`O53i&nGA8 z;x%E`6~)D2ax|2CCQ9~!uj`V;98ZH}a%Jir2mk(-pFM>PX0TUQmK*gNp$Z3WPpd&5 zFGZ6na|yKFYX%dV23~l^?v@|p7dgfwu~0vv3y|JtqNpn_J_9ufvbl1SC{!nTUb1Lr zSv9M4Mf%;*ZJ?P~=B%dXA5gGiQJFculB0Nf+ai0^S&PLv)4N(T^yTMgTCxGbDTrQ? zsku3Kl49klfoa9Fb{zNvz9=9zDTfOIAql<9e}C+XeumMzNqDoPKO z+!W8Wh-L!Vl_e+_RK>OaNG82)^k7whktW5S5!}q}g78f%&})TvS1g&F`dHA0e1687 za8C0Vxk?DjwkFjhkVB7*&EXw6IP7iw1YyB9a|flWKB9uE8D0v1C;Tf3`TMv3_kSE! z|9g-B`BT;@{J(0r|9;2(*b8bnDgOKF&z{8qGLYn$NFW9mEiz5He4{Kun z`{w@pg6}m~|FSSb@@B3Bew3!Ef(Pb~@a#7>-q!%+1I0;y)Stb*TT6tRQ2vLi{@OW; zFCK?-u)gXcJrlzNr+r(HzA+beFYE}t7Hest=2n~jBahilTeN=d-+K)C4|i$~9mA!U zkHFN^GB;FJbd^2vsg@dqZ|uN0nYe+H{6etAFO)sGAJn}5jM-qSvZ)(d~X!LN19XLELN<$<@OVe)X*DFQ9shp2vmL4P zMGSb#-w(ju6Q06ZQq~C7b>FTY^?DM&M+caKl5H;Zl|Vbd5kxHb9IOr&b>)AtKqUzi zXM5>)xv%*cz8}Th!6t$s+EQD8jE(U?K?FLYDg78I8=5n*Bclc?fVFGX9@O=h`Bja8o zbMtbkqgdHIl6vhMYKCPpT$UukbGE)4xVL%;IkrDV6v_*bKHnr@ll0XcVaR?a&m z4Gkt;kta_Y1HX7Y->CCw{bSPY09_}5&$pBJZtdN=bqf)Gzwj;-L3|NuF%gvvMLJwG zBwe1+oKOY&sk>sv*Rt$Z!cJ$b0Y;Kt2$vGjF;M<_F^wi=6|@d4WiJ}u68~A~i`5o; zla-kn1raNkKKXN8cdD!c;i1I91qTrom1&3iZ#m12{d$;#d7=W|Sk7h%wZ&BOa?q zJzbM2g80UOQGZXispj`aG~wv?HPjV%8B3Jtq>t=4m^OYEwH!~JY*+?;IO zt0Ok9$A`L&_8Z?~`*8^q71FkOY{z($EV!*yK-e*0u z`ObQwUC=Hwc`qeT6I}(j)pS(XU9~#iQLma}(CfmPbL8TCaX9VhiQKy_u;cEu)M1Ul z=*sRN?V$=%YPpQeZ-t&{CjMC1@zOmeFLPY^rfQNaF_no~SYQF+(29yiBhnv128^hv z#z2eYzVhRXNq7oqG7^+Td(MwSUeq6?y-@+hH*$|#aH(ypWc-PI$gN1LQO<2N2Pien ztCNbuhH3~1{~dqlHvc41j4JBGpq#~kw(T*gkh80%+t*&4J0KJR@&m@o6XFuX_yPGm z#v;wCeA9zsc^*6x93k6j+pbh4fnO0}&}p&~_`jNcmzl`~!sx0_A@0UGyja-DDaA(V zC5)bhFJ2ID`AD0)87_%-c8Iyy*N&S|cLT}?j2vTp*PSU@M|Y~uVYh@HuP=-FHZ zGfm*ISVqE&7nH!#qdaRK!2+!0yw>B*-!pOk9Q*Id>?1^sLCed@K*_<##T*^M4LM&2 z#iz%0e0;kxS-s0EcVPetG}j>71L4G8Pe1x#Z(kn)P_;m)_Ck$=ia;Q2Wf5%<+BPRq zRcSr}5)>>8df-G1r1kMBwLy}{^7pCcWPoFQQXVq(5e14RJv8itE)Mkg&hv+Y?1^%# zL9$X!=fCI?CT-4eF$z?&ufMnOOGe%pK*VOK#6+n{qII_oXx(t*oW$uE86_VQ_tYG? zG`jugcEkJkkG-{pZ|1N9dUo{>>D2#KNe(5QCR)snz}sgw|} zw@J?<1oJe)Bq$4jvb^M*YIB!b0kXU}A=lxmFWhIKym*ucTrX@@4hFun(2{d-q%PO) zfjS+Nj4wUdX#p++t3ob*?YkcJ>%dqAgs15>o~XxcP_Z-EETn!xN=*h0oSal6Mca(Z zqcSfg$^ogo>3S%qE;$C3JOXSo56CI`Y*H#YHD#pU$Vk%U&XXW7l#|a=anTFWV z1`Mg;hw5MitHwe&lA5ljW_wJ0{1a8Bp`oFk;bU6i8#To&EG*J8G9A^XJkyuKz>pvp zXJ*X8!y>5J(uDEybLB0D*gEr5G&J#V10xaLcjO@1m0I$lm#ch3X=y0|virOWcK$zG z=khWUO|k(VE^+NYRpQT<@EC#MU|F2#C*o+aF}ML3%aS=AmxQUK6BFLbHbZOKHTaI&w4|UoFjW4R-3M3$YlQg!y*#`ds;oD3!=QhI!KQU}G<@7m z>7~5Tl=yRy>Vwc~sDE(U;R67YunFLw?e7Qy0|4syzUeV-w^x*MpPbyyK^0X+FRBfv zvh~7@VhPTf8KaW-$AjOc<0!^f-~FD62B84*skbRF(4H2}zj2PC`~j*=hvxk*IJ^22b2Pt-bLvKwCW1$>4l*BIs@!$Gt| zCb=x>fhFV;F>I7F~5vJ z5q-%ILEwZgFBhU`pnJYCBwmZfpsJ?(`0=5FRvlq@F3_M<&KQIP!{rr({*O=)NZ6E@ z-ve7AeSa*J-XpbCDuhR`*$A$-OozO9gb2&@Hs8O)a{I@(<* zB*W4qiGHe0ubT0IY6RKzoSCqigA1l?^sSJyjewI?2`H?fl)Ots#AlTMuj@MLJlx__ z(C4%0c+nzzE7T_@5mptzpiujpFKOn|y#7ociTwK6nW6c)T$7#M5&87JMN0OGs8M`n zWo4FVx4P{lZC);=zJ|8!WrX0WI25>TPRmb7FC)N~fff*^OA-(gA+T4!PTN6Jz^mU$ z&~svBrvtP8Ov=zhv(RFz8SX_zJ23qXbWc=S(=yXDYgbxb{*xw=1vw(EpNs3kVil;# z=|8=7NI?y!20+Ju#W3V_D}nXNod7oPK6e6cW6T8w3J?fO{e6r{291gP2VUM0C!W>p z``EK_I-PN@bss8n!Nd#~!!Jj!>RAgUA~E*&vnf!cSt0}@Q)@t#0(v?!kN>wT3gH#f z-7YLLuFy~w5KQ}85ELknAe3LKd3m5j09aKpO;4#kL_sPJv`5_R_x7P+*JZ%~xL;iC ztD1_6nyR|Crq-LgQ6O^#x2127ep_rMY6l|&O8OH@j6}e4+1c6m_Vxw`xj>i(stoWW zK+QN4#jZnA1iUmXD6fYkB-Zi(8;%RS7QP2YEtnjG^l%$KfaV?>xHVD9?&{^+*_R>S zEhI$l?$ldY`=IUUk_%b0vIdoF8}kipZO}I!4Dv(yrxka}cQ>R^GF%Vb<=9StKGcVi zag1+vCP&NBV3ICQ)`WM~ABZ%0qd=Ji+z5}28~6dhiOB2mjf#+PX<^b9N}tFSaoWod zBcnSyl%d~nUY!+ZiD#sweEFS^sw4WFL`rjW790{Pi@U(0Bf>iJxBhd6w;XOonM!G7 zZ-Oiv1?xt(Y6~q5x~+57x?GmBzpP5k5|EIgVSx(Loe}ZTE2E9Uu9VY}nfl@;-3bG` zZk-*Bh_JL$Mj2Yig&&GtO||O93xSK8W|jl;tb;(SOQ1(32btUh(n8FX@3SlAo`{kp z{#vf4bfvlW(wqpy0_IP?aaA9{V4qhT6n^mC$|}_h4WSR>vG?x;1))JQ8YctZm7dXN z6?u8Q92N40saRaR$dRR=on*AX_~sOZeqONZ9E*kr^?*vQ|g4>Ac6hS)>d3l z?CelU?XVd5TtcIro_er>tgwN&C`z+?Ziq|G@5}ThBxnd7spI=wahFz9fri`nK6Y+X zlg;1CD!U-_I~`qAWF+|ua(&p2Vvoc2kB6$@IRJecI;f;R_Uh3uv>lMMgO0d{wptN~ zc)Y&Iuk|vbe&@P+d^h`K#wkDYtM7;s*07icJDhaUk_ToM1vNcHdOGq>v!#CE@P}Mz z)kEw)=awE6(QErxeQ8)sp@pJ5ON682?b}&L!n;m&&)21_o>J5lPyXzCs;#}FeG>-D zLgky_g94jx)hHcCu*34&VWSdYqgNflK}GexwU_xL&5WJOAV0UD;Ds13up8j;0tnI)c;6N*TOjF-^qJV=SFb{6 zi~l|dtkd&z@AK)hHXqR?e$E>gkiW%++&*Z7%8uj(yxEi@`F^MGT_MzQKJ|9}454@T zgO9IPTKhk)C1Fn-FD!z*f}Gy`9%6WY#f`)tdrSXSgh#`hzYWJIU?avX$-sdMsFYYn zX67X%CN4HuMS>bFR4N=1&QR>RqBO2s{6)m_1lklQC+vY#B{ozf{e2d-K)a}}D&2Wr z#%I%ECb*uU9+RPAq-MTsCy4idF|+$KrjY{DR8d)83doJ{5b^nU2#oq>qNMx+cL)e| zEavApz&P?wrGQ=MUD*&6g%RYI61V)`-d!C{P+{}!>ys}D?fiG!9ed1Zb#tNYJ5t3 zF%tCW=VA{DW>>%z|af&!%hG6)%Yh}LUn3qcg-VL+#tKZFREu)j;(^3;5C>eA|Xb6bmVd08=o`!tD ze2iF`wK=Q2658cZO#q#rDhDwvE-h;$Bunj%Sp9S>FH501Y@ke2y7$z^#NrBqw!IBR zVvvHAwA|yavl#R=!91Jc;ePwd4SE|qCTi-Ok~}b&Nw*{dB=uNa{6l7@^2zdlPC@wB z{P`X1_XTPI7CuuOBv>q{IcZyMz_t$ExZ_z>SC^YxttO(|G#axa6sN_V{Iwug$rvhL z`#1N#pOg4g<4OD>!gd)f{V?M{IA~QDb?M(i10U7S|Keu;pgvD!hA_~kz=%gem=2s; z=ub%g`$B*heO{$eH{brhpW?H#qW}LP-2eIU|6k+BeL{7Y(*hNwSW5DrJ33KBN=+@| z!d|rBYS$^^w=~N~jpyOqgn}OmyRw1;&VhkC4ef;F#FNFXzxemx_nF{>IoqXOoq1mC zwD(;6LmZiAdx2_1o|6{9?oo&M~eDsULX_{09PX9>nF=9Zu%ymUy0C z0uE7DHd=dxQ84YCoE16`KZ&gB6D?994?LuZL}NwKJcbEo2uzxJ|zH>gUl`Rxww~3Zxd{0NO%;c23 zE6Zp&G5zrSiH)~0HPq%-3lj@luH^bkPgy_>om0jKu^6E6gAS~-qXWDzhu5!JTIAzA zG*1Et(F{(syxdJ=BcoTH8}0&OZ-U^%$W%;9!h*B^qua5_hT>A@aC7}-a94c>wfhLH|VCgk`g%u!{d{+QSV1z zvcN=iYj+Q#jm4e6ex-$+G=)UoZ^C+a5UV=P_g7wD2@u!Pc2PU!W@iNe@*QM9xi0-i zN5Fh#Jt!cZ0MXT;oxV%tDtc|tTKe=FsqOjaXedrvG1j6qru`Ni!S`uz`1*8l`sh5p zpl-fDPk_XlSygoo!-|`dqdvoLc(l1Pizq}^>PN>}i-{wc9>ETU@+oip1PtXt9Wv+~ zPsUOwnvUD_&pvLst=%EOEyyUQX<}S7bk%Uh^{3z{^&y*fU{7Q^-gD5`)by_j2eq*~ zkMw)n_I7g=kqjtmypDG*^h-b}59_fQT8cD}YKpBAYFD?9^b8D%Y)%b!{1Cnoq2YEE zrN{K0Iyn!#tNUQMfQQ(L-z}O9=dQau-_$$?r;OE80~p$!!{~C67gYW}GjsdwWgfU9 z;wcGa1qJwM&i1r@_{Y`(I?OjZou5!0V7nL98PS)=$g4B5Yd-g%X`bQ%ON`BAY2Vj& zjMx_1+$#ZMpbHbj8Mv6QXa(6@z-|8SFt3Zs+$@8RIRmR#{_XRVMSXzC@bKgwxDkOH z1>%={HnUTTx;C_jWeU5E${(jdX8x-CPoT3U#J;+Nf`90%%#*(I;2Y4k)T|3;_U1!U zDK^W3!-~z+-WA`(ej3h+7u_odAaJ?ZQ){Y|t7HE~*VIZ6XJn*K#_18-o@Mb(_ ztsVnN7Tbx+pY0j64231OBQBd_IJ9eXGpvg%@t?xyT4hr`1<8sWmdq>#54<7mF#`-N zXC|mHm{7t5l-2>H+&VvW8yL!Y(v5sP`U&w&p$B$Y;?mcAlctErXBH+Dzdibtpvv_Ry2Rup?-%sg1pVAelb^f80<6;#| zApdgWDqEVdBD;;dy!PADWx8&a3#fC{UuvK zLU3ntZ}tNw0wFl&rk4HN*C6E&5zeMZz{IRy@j!r7?^2dD!>PD$0NX?cP3+QYt| zSu80+neWIMnjwDcBqw|W2qogxI$2p+R{~O3cSv=EXt6h7VvUH4^pAUe7g8=7K}CN* z*n4HF=3p7`Blp-BC~px+*T}gg8$?Jriu{^=#z>(Fc7XY2TDQ;nG$jIL z7HnM=sSHI8RaK;?=aXT@18^51irSsmd@R@69T+ZG-#z)>Nns;cg?VFGM}vJo9rrU-%=vuI=h14h-iIf{$E>7}Yc?w5yDP0bNFs}fQfROM;a3ms3BP*ZCt0f~-5Z8q- z#GlrE=ZNs02R5m~i(CSzB5#9lt?~NB5KQzt7>f(lp6v~}db`bHV&8}#+Ghl!#g|M* zY7~P$W^RTJxqagbxa(szu6v2Yt&KtafU>OFx2f@~>CI-b|IIA!hD$tOU!@1RdAuuA zTKD_A2##Yj&gJ=AjJI~3 za0Zy589^*udk zR_<<`0>@-lSqF6C$ggKp{iV@@$f4Kan~p3)9O$d5NDcZ23#wKUnBjK*k;cEa7>Iu7 ztf{v`QhnwL(5*criTsuhmSZsBY^2OMRB9SHXmD*(4?$4Fbp#U2sy-8l={~lRa3m7A z67a=sap@`oZaHH4=Kh?b95$!Ek$Tc_y^nij++r%%7?_zqp8t9W7I-bD+xpG5Sa3&4 z%Xg2Lt^5M_lBs;!4Mf}LoP(0%7-+dZBlcJZZ8bF%#PduDPMG@%Nw?3yrbO2{Vr5N4h0!Y&0M*v)VqcBRCo;MUT1)=Mjmg-c2XXfC4uJ@pcrtE z$)5E_z*LOPF31|fD(3m}dY7kX7&3&)Ko6m=!k#Bb9NJ-Br@1luL1{JwP%Fk^E6aBI zlhhN}b`A=Lq0(rSKdO+6<9QR+Wo=G4HdfrDlaWF1u)&Fm5%X3!I~Q9Ng|xWXx4zl@ z-x8RjwwaKqaYxXWgWVCT!KGyj_zF&YBvYXD+v(P=Di|BpAw#3j5gK!|xdb4Xoy+gu zzaMkiGii;bVhVUOif$7@;U|Z zKmQNfxef?yq`=amnbH7Bw^WyvMeo;}m!n?RYjTbUIyG4!j;^e_+-$W0nA;1VprQsu zUwREtZ+!`X@S9tRNmqi{cYCmGqdWtxBzW9U7NBp!q>6VZ{*o|h7J|sgbb^Tj0lTDU z->dF#uOgM~0T+T|gaXZtp^C8eiksVqsTwex*#xBdD4L@8gK3dlts*m%9ikTzUSbyH zm1GMf(s4YYG*H{0O$>h#fvNlSQ5H=)C^lePHp1$?%6hS(VG^rYJ&R7$;E0m!ovtOKt6z(xy9l2D>&=HQZgC6`A~yNh+r{ra&Qd|_oMiB$e6fGaKoH7c*713 zMDbrhFec^!#`WTfhWifRKp-3V5I&KNdTYciNHnZkM%^Y zj0V4s-jt%U*6TADhgEt?N^2t{Ulu~tXHf2{FsR=9b>X+yN_wiL6~++VuB?@mMtM&W z5M)r}GvFclDw;+O%+%tSkgIMVy0;q=Oz8^l{fJ#TK%RAEv&{r)PBUJ(2CcYT3n~cp zSO0*VIn99(`c7o?d*^k{lIPW|DT2mth3_2_U}GQ6d_JLRrVaD^%;ff0&p@qBMop7L^3Z;AwB6=&!xA>* zJ!^o9>m1Mt%CF|cmr(H;N}m57bUXTaAwQUoqJc_zq{`6CT7Iqjo--6U zI7IkW7Dq2=LbjTOggoE$WXtfrkacsrikSIK*~%wx{OESmKJOGb@V<<|uk^@q7rY>l zA#(EJUuYC=?4D-)XnyXq1B{lz>|i7^nWax#XISdb_39{vY0zmKC!C zq}Mz|=d&tw4T118$F;dPGB=i#2+XDN8$eDEicTILj&*}U!i&HTShH~JWL~WZPpi9u zSgJAO3l#)AWd*xi{{kBX1$CKyAbe5ygGuMG0D>9=h6v%qe09szW*m~w!P`PUuGjkz z+g!##AyvTVtBIrg^9>6lvrTf=S>WXkw<-8swvt-2O!KvDSS}+&RwL3hkTTFFiLq*x z;lvr<>ap8${EZnCv0Iy~wK%%;$ov)9_jm{1#NIZT@{7jqOy;^_d$-Bk4=Um3b;^cA zqDTZ-#=M46xRZYmjk#TfZ_SzQ-9~yY-9jMd47rVsuc-pc_P?30i)o$`e%+Qrd^+Z! z1yJXx_Sx6hNG~?_8zB?W!;%Pw ztS!KLhU>zClW2OeFgx1}MzWD&^;dYP>x<8Q^0sL~^Lx>70*NFrM5u#~uJ3$?eSOAg znENt=rsgPoN)X5N@!(u4;dkAhgViJXAXvh*Di?nuhL;SBjeDXHf>q#SsI21B9?M<8 zv6T>+e&NQUbv$Wim4T~y+><{*qo#}nZuQl<)t*nk!B%vUnfaJVmclRd#U*9I8(*gF z#L*zH>l@#AVM@2l%g=NnVSyz@#^+WP9pNMt*{8Ri*KglI5V1>C-j+W-UA%ve{Yf+K z>hN!Ty9#JmZx)yN2_5ZqZAT76KjB|H%R0h}i|?{LdRc$~06A1gXM3R720)_xHs9?o zlB<9nf`-3l)#@#bhPbO?YwiFuRC+54=mI~1dBn3X;4Q% zC}mdyw}`6$4pkk@KNrjVnK$_eof=%9?V$kAfaGM%plG%}&aUO3RCY$3(+0;|l2ZdK^AQ zh_y`^NiG2Y;iBolkpcN=XUY%Rz|1WBVk88UQQojo>ePeJ<78zzm)8mQPzxLrX& z5V}FOB2WUGD`u^E2!gRpj4D~FIf*ta?)*0_z_w6wNglZq&~66Jdd%#D49p0n0bo6@^JIf(>OXzHO49 zex>gI6s~Vn9@AKKMSUNlihu5{zV;+OtMlA})?R)^eb=FymVv;zPcrVteb34AQDUku zaBzq=Qd|Vufl&&QVqc*jY)pozk-mR#xli_I)rCQv0_9UqXAQ_5Anm48bldp`l)YHz zd8S5FKAS+H+w~wW(|i{p)9jki&EUJNviA&&J9z5~Ua+XYdPjd|lE%&NS$yli2s zQBhg;78kT)&F0^rw}D(LpkN^cY;K+n0xhAYI`k9)g%MygRIlUawhpB%174}pM60T+ zORgH@;$HG?dDBDzK>Yf{%?E!N^?j&r27Gzwu|>pgpTug@vPu|D<&AD+Y}~sNdFXW9 zglWWz=CQvxlvh(BWo+ascL^H^Zj&Er-`gOr-pgp5#(kLdUCD;;JfYVU~$*cLCh;1N$tH)*tqq zZH~4xio?`8r#=Go3W`8I1xe4LsBSu#XAmew9<6#6Dch2k4D-RGZfhASrA}0u#hblW zeQ(hX=TyjJOG{sWYn^1J*94-?;ST%wJrVl;wbNw?=)y%bG?UKRO_S_|Kh_>zgbs$? z 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 ce:plan -> ce:work -> ce:review -> ce:compound` + +If the problem is still fuzzy, start with `ce:ideate` to clarify direction. + +In practical terms: + +- When requirements are unclear, use `ce:brainstorm` to explore +- Once direction is clear, use `ce:plan` to break down work before writing code +- During implementation, use `ce:work` against the plan rather than a vague prompt +- After implementation, use `ce:review` to check for bugs, regressions, architecture issues, and missing tests +- When something valuable is learned, use `ce:compound` to capture it back into the repo for future reference + +These skills can be invoked proactively. They do not need to wait for the user to name them first. + +Reference material: + +- Plugin: [EveryInc/compound-engineering-plugin](https://github.com/EveryInc/compound-engineering-plugin) +- Article: [Compound Engineering: How Every Codes With Agents](https://every.to/chain-of-thought/compound-engineering-how-every-codes-with-agents) + +## Documentation Policy + +All durable documentation must be maintained in synchronized Chinese and English versions: + +- English docs: `docs/en/` +- Simplified Chinese docs: `docs/zh-Hans/` + +Update both language versions in the same change. diff --git a/docs/zh-Hans/CONTRIBUTING.md b/docs/zh-Hans/CONTRIBUTING.md new file mode 100644 index 0000000..3766da8 --- /dev/null +++ b/docs/zh-Hans/CONTRIBUTING.md @@ -0,0 +1,128 @@ +# 贡献指南 + +## 项目范围 + +本仓库采用 Turborepo monorepo 结构,包含以下部分: + +- `apps/api`:NestJS API,负责 Feed 拉取、Prisma 数据持久化、摘要生成 +- `apps/web`:Next.js Web 阅读器 +- `packages/*`:共享 UI 组件和共享配置包 + +应用代码统一放在 `apps/`,共享代码和配置统一放在 `packages/`。 + +## 环境要求 + +- Node.js `24.14.1` 或更高版本 +- pnpm `10.33.0` 或更高版本 +- 本地 PostgreSQL 18,用于开发和测试 + +默认本地数据库连接: + +- `postgresql://rssift:rssift@127.0.0.1:5432/rssift` +- `postgresql://rssift:rssift@127.0.0.1:5432/rssift_test`:用于本地 e2e 测试和测试运行 + +## 首次初始化 + +```bash +git clone https://github.com/sommio/RSSift +cd RSSift +pnpm install +cp apps/api/.env.example apps/api/.env.local +cp apps/web/.env.example apps/web/.env.local +cp apps/api/feeds.opml.example apps/api/feeds.opml +pnpm --filter api db:deploy +``` + +如果需要示例数据,可以运行: + +```bash +pnpm --filter api db:seed +``` + +## 本地开发 + +启动整个 workspace: + +```bash +pnpm dev +``` + +也可以分别启动两个应用: + +```bash +pnpm --filter api dev +pnpm --filter web dev +``` + +本地默认地址: + +- API:`http://127.0.0.1:3000` +- Web:`http://127.0.0.1:3001` + +如果数据库 schema 落后于迁移历史,先执行: + +```bash +pnpm --filter api db:deploy +``` + +## 质量门禁 + +`pnpm install` 会通过 Husky 自动安装本地 Git hooks: + +- `pre-commit`:`pnpm lint:staged` +- `pre-push`:`pnpm typecheck` + +常用检查命令: + +```bash +pnpm format:check +pnpm lint +pnpm typecheck +pnpm test +pnpm test:e2e +``` + +## Package 命令 + +API / Prisma 常用命令: + +```bash +pnpm --filter api db:generate +pnpm --filter api db:migrate +pnpm --filter api db:deploy +pnpm --filter api db:reset +pnpm --filter api db:seed +pnpm --filter api db:studio +``` + +## AI 工作流 + +我们采用了 Every 提出的复合工程(Compound Engineering)工作流,并将其实现为可主动调用的 skills。日常开发遵循以下路径: + +`ce:brainstorm -> ce:plan -> ce:work -> ce:review -> ce:compound` + +如果问题本身还不够清晰,先用 `ce:ideate` 来梳理方向。 + +具体来说: + +- 需求模糊时,先用 `ce:brainstorm` 发散思路 +- 方向明确后,用 `ce:plan` 把工作拆解清楚,再动手 +- 开始实现时,用 `ce:work` 按计划执行,而不是对着模糊描述直接写代码 +- 完成后用 `ce:review` 重点检查 bug、回归、架构问题和遗漏的测试 +- 确认有价值的内容后,用 `ce:compound` 将经验沉淀回仓库,供后续使用 + +这些 skills 可以主动调用,不需要等用户点名才使用。 + +参考资料: + +- 插件仓库:[EveryInc/compound-engineering-plugin](https://github.com/EveryInc/compound-engineering-plugin) +- 文章:[Compound Engineering: How Every Codes With Agents](https://every.to/chain-of-thought/compound-engineering-how-every-codes-with-agents) + +## 文档规则 + +所有持久化文档必须保持中英文双语同步维护: + +- 英文文档:`docs/en/` +- 简体中文文档:`docs/zh-Hans/` + +每次修改时,需要在同一轮改动中同步更新两个语言版本。 From d8d6f07b925c830149fbd36ea4e901afaa828ec5 Mon Sep 17 00:00:00 2001 From: sommio Date: Wed, 22 Apr 2026 15:28:24 +0800 Subject: [PATCH 3/4] chore(agents): remove obsolete skill bundles Simplify root guidance to match the current repo rules and remove\nobsolete CKM/UI-UX skill bundles that are no longer part of the\nmonorepo workflow. --- .agents/skills/ckm-design-system/SKILL.md | 244 --- .../data/slide-backgrounds.csv | 11 - .../ckm-design-system/data/slide-charts.csv | 26 - .../data/slide-color-logic.csv | 14 - .../ckm-design-system/data/slide-copy.csv | 26 - .../data/slide-layout-logic.csv | 16 - .../ckm-design-system/data/slide-layouts.csv | 26 - .../data/slide-strategies.csv | 16 - .../data/slide-typography.csv | 15 - .../references/component-specs.md | 236 -- .../references/component-tokens.md | 214 -- .../references/primitive-tokens.md | 203 -- .../references/semantic-tokens.md | 215 -- .../references/states-and-variants.md | 241 --- .../references/tailwind-integration.md | 251 --- .../references/token-architecture.md | 224 -- .../scripts/embed-tokens.cjs | 99 - .../scripts/fetch-background.py | 317 --- .../scripts/generate-slide.py | 753 ------- .../scripts/generate-tokens.cjs | 205 -- .../scripts/html-token-validator.py | 327 --- .../scripts/search-slides.py | 218 -- .../scripts/slide-token-validator.py | 35 - .../scripts/slide_search_core.py | 453 ---- .../scripts/validate-tokens.cjs | 251 --- .../templates/design-tokens-starter.json | 143 -- .agents/skills/ckm-ui-styling/LICENSE.txt | 202 -- .agents/skills/ckm-ui-styling/SKILL.md | 324 --- .../canvas-fonts/ArsenalSC-OFL.txt | 93 - .../canvas-fonts/ArsenalSC-Regular.ttf | Bin 165848 -> 0 bytes .../canvas-fonts/BigShoulders-Bold.ttf | Bin 94528 -> 0 bytes .../canvas-fonts/BigShoulders-OFL.txt | 93 - .../canvas-fonts/BigShoulders-Regular.ttf | Bin 94396 -> 0 bytes .../canvas-fonts/Boldonse-OFL.txt | 93 - .../canvas-fonts/Boldonse-Regular.ttf | Bin 77168 -> 0 bytes .../canvas-fonts/BricolageGrotesque-Bold.ttf | Bin 90952 -> 0 bytes .../canvas-fonts/BricolageGrotesque-OFL.txt | 93 - .../BricolageGrotesque-Regular.ttf | Bin 90920 -> 0 bytes .../canvas-fonts/CrimsonPro-Bold.ttf | Bin 107352 -> 0 bytes .../canvas-fonts/CrimsonPro-Italic.ttf | Bin 108828 -> 0 bytes .../canvas-fonts/CrimsonPro-OFL.txt | 93 - .../canvas-fonts/CrimsonPro-Regular.ttf | Bin 106696 -> 0 bytes .../canvas-fonts/DMMono-OFL.txt | 93 - .../canvas-fonts/DMMono-Regular.ttf | Bin 48852 -> 0 bytes .../canvas-fonts/EricaOne-OFL.txt | 94 - .../canvas-fonts/EricaOne-Regular.ttf | Bin 24872 -> 0 bytes .../canvas-fonts/GeistMono-Bold.ttf | Bin 78304 -> 0 bytes .../canvas-fonts/GeistMono-OFL.txt | 93 - .../canvas-fonts/GeistMono-Regular.ttf | Bin 78232 -> 0 bytes .../canvas-fonts/Gloock-OFL.txt | 93 - .../canvas-fonts/Gloock-Regular.ttf | Bin 95156 -> 0 bytes .../canvas-fonts/IBMPlexMono-Bold.ttf | Bin 136008 -> 0 bytes .../canvas-fonts/IBMPlexMono-OFL.txt | 93 - .../canvas-fonts/IBMPlexMono-Regular.ttf | Bin 133796 -> 0 bytes .../canvas-fonts/IBMPlexSerif-Bold.ttf | Bin 161000 -> 0 bytes .../canvas-fonts/IBMPlexSerif-BoldItalic.ttf | Bin 169840 -> 0 bytes .../canvas-fonts/IBMPlexSerif-Italic.ttf | Bin 170004 -> 0 bytes .../canvas-fonts/IBMPlexSerif-Regular.ttf | Bin 160380 -> 0 bytes .../canvas-fonts/InstrumentSans-Bold.ttf | Bin 68084 -> 0 bytes .../InstrumentSans-BoldItalic.ttf | Bin 70004 -> 0 bytes .../canvas-fonts/InstrumentSans-Italic.ttf | Bin 69900 -> 0 bytes .../canvas-fonts/InstrumentSans-OFL.txt | 93 - .../canvas-fonts/InstrumentSans-Regular.ttf | Bin 68028 -> 0 bytes .../canvas-fonts/InstrumentSerif-Italic.ttf | Bin 70868 -> 0 bytes .../canvas-fonts/InstrumentSerif-Regular.ttf | Bin 69312 -> 0 bytes .../canvas-fonts/Italiana-OFL.txt | 93 - .../canvas-fonts/Italiana-Regular.ttf | Bin 27184 -> 0 bytes .../canvas-fonts/JetBrainsMono-Bold.ttf | Bin 114828 -> 0 bytes .../canvas-fonts/JetBrainsMono-OFL.txt | 93 - .../canvas-fonts/JetBrainsMono-Regular.ttf | Bin 114904 -> 0 bytes .../canvas-fonts/Jura-Light.ttf | Bin 154308 -> 0 bytes .../canvas-fonts/Jura-Medium.ttf | Bin 154488 -> 0 bytes .../ckm-ui-styling/canvas-fonts/Jura-OFL.txt | 93 - .../canvas-fonts/LibreBaskerville-OFL.txt | 93 - .../canvas-fonts/LibreBaskerville-Regular.ttf | Bin 147584 -> 0 bytes .../ckm-ui-styling/canvas-fonts/Lora-Bold.ttf | Bin 133828 -> 0 bytes .../canvas-fonts/Lora-BoldItalic.ttf | Bin 140332 -> 0 bytes .../canvas-fonts/Lora-Italic.ttf | Bin 139328 -> 0 bytes .../ckm-ui-styling/canvas-fonts/Lora-OFL.txt | 93 - .../canvas-fonts/Lora-Regular.ttf | Bin 133888 -> 0 bytes .../canvas-fonts/NationalPark-Bold.ttf | Bin 79208 -> 0 bytes .../canvas-fonts/NationalPark-OFL.txt | 93 - .../canvas-fonts/NationalPark-Regular.ttf | Bin 76424 -> 0 bytes .../canvas-fonts/NothingYouCouldDo-OFL.txt | 93 - .../NothingYouCouldDo-Regular.ttf | Bin 32020 -> 0 bytes .../canvas-fonts/Outfit-Bold.ttf | Bin 55392 -> 0 bytes .../canvas-fonts/Outfit-OFL.txt | 93 - .../canvas-fonts/Outfit-Regular.ttf | Bin 54912 -> 0 bytes .../canvas-fonts/PixelifySans-Medium.ttf | Bin 51072 -> 0 bytes .../canvas-fonts/PixelifySans-OFL.txt | 93 - .../canvas-fonts/PoiretOne-OFL.txt | 93 - .../canvas-fonts/PoiretOne-Regular.ttf | Bin 45244 -> 0 bytes .../canvas-fonts/RedHatMono-Bold.ttf | Bin 34420 -> 0 bytes .../canvas-fonts/RedHatMono-OFL.txt | 93 - .../canvas-fonts/RedHatMono-Regular.ttf | Bin 34488 -> 0 bytes .../canvas-fonts/Silkscreen-OFL.txt | 93 - .../canvas-fonts/Silkscreen-Regular.ttf | Bin 31960 -> 0 bytes .../canvas-fonts/SmoochSans-Medium.ttf | Bin 59704 -> 0 bytes .../canvas-fonts/SmoochSans-OFL.txt | 93 - .../canvas-fonts/Tektur-Medium.ttf | Bin 76248 -> 0 bytes .../canvas-fonts/Tektur-OFL.txt | 93 - .../canvas-fonts/Tektur-Regular.ttf | Bin 75604 -> 0 bytes .../canvas-fonts/WorkSans-Bold.ttf | Bin 191304 -> 0 bytes .../canvas-fonts/WorkSans-BoldItalic.ttf | Bin 175772 -> 0 bytes .../canvas-fonts/WorkSans-Italic.ttf | Bin 174280 -> 0 bytes .../canvas-fonts/WorkSans-OFL.txt | 93 - .../canvas-fonts/WorkSans-Regular.ttf | Bin 188916 -> 0 bytes .../canvas-fonts/YoungSerif-OFL.txt | 93 - .../canvas-fonts/YoungSerif-Regular.ttf | Bin 105136 -> 0 bytes .../references/canvas-design-system.md | 320 --- .../references/shadcn-accessibility.md | 471 ---- .../references/shadcn-components.md | 424 ---- .../references/shadcn-theming.md | 373 ---- .../references/tailwind-customization.md | 483 ----- .../references/tailwind-responsive.md | 382 ---- .../references/tailwind-utilities.md | 455 ---- .../ckm-ui-styling/scripts/requirements.txt | 17 - .../ckm-ui-styling/scripts/shadcn_add.py | 292 --- .../scripts/tailwind_config_gen.py | 456 ---- .../scripts/tests/coverage-ui.json | 1 - .../scripts/tests/requirements.txt | 3 - .../scripts/tests/test_shadcn_add.py | 266 --- .../scripts/tests/test_tailwind_config_gen.py | 336 --- .agents/skills/ui-ux-pro-max/SKILL.md | 659 ------ .../skills/ui-ux-pro-max/data/_sync_all.py | 414 ---- .../ui-ux-pro-max/data/app-interface.csv | 31 - .agents/skills/ui-ux-pro-max/data/charts.csv | 26 - .agents/skills/ui-ux-pro-max/data/colors.csv | 162 -- .agents/skills/ui-ux-pro-max/data/design.csv | 1776 --------------- .agents/skills/ui-ux-pro-max/data/draft.csv | 1779 --------------- .../ui-ux-pro-max/data/google-fonts.csv | 1924 ----------------- .agents/skills/ui-ux-pro-max/data/icons.csv | 106 - .agents/skills/ui-ux-pro-max/data/landing.csv | 35 - .../skills/ui-ux-pro-max/data/products.csv | 162 -- .../ui-ux-pro-max/data/react-performance.csv | 45 - .../ui-ux-pro-max/data/stacks/angular.csv | 51 - .../ui-ux-pro-max/data/stacks/astro.csv | 54 - .../ui-ux-pro-max/data/stacks/flutter.csv | 53 - .../data/stacks/html-tailwind.csv | 56 - .../data/stacks/jetpack-compose.csv | 53 - .../ui-ux-pro-max/data/stacks/laravel.csv | 51 - .../ui-ux-pro-max/data/stacks/nextjs.csv | 53 - .../ui-ux-pro-max/data/stacks/nuxt-ui.csv | 51 - .../ui-ux-pro-max/data/stacks/nuxtjs.csv | 59 - .../data/stacks/react-native.csv | 52 - .../ui-ux-pro-max/data/stacks/react.csv | 54 - .../ui-ux-pro-max/data/stacks/shadcn.csv | 61 - .../ui-ux-pro-max/data/stacks/svelte.csv | 54 - .../ui-ux-pro-max/data/stacks/swiftui.csv | 51 - .../ui-ux-pro-max/data/stacks/threejs.csv | 54 - .../skills/ui-ux-pro-max/data/stacks/vue.csv | 50 - .agents/skills/ui-ux-pro-max/data/styles.csv | 85 - .../skills/ui-ux-pro-max/data/typography.csv | 74 - .../ui-ux-pro-max/data/ui-reasoning.csv | 162 -- .../ui-ux-pro-max/data/ux-guidelines.csv | 100 - .agents/skills/ui-ux-pro-max/scripts/core.py | 262 --- .../ui-ux-pro-max/scripts/design_system.py | 1148 ---------- .../skills/ui-ux-pro-max/scripts/search.py | 114 - AGENTS.md | 76 +- 159 files changed, 41 insertions(+), 22052 deletions(-) delete mode 100644 .agents/skills/ckm-design-system/SKILL.md delete mode 100644 .agents/skills/ckm-design-system/data/slide-backgrounds.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-charts.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-color-logic.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-copy.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-layout-logic.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-layouts.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-strategies.csv delete mode 100644 .agents/skills/ckm-design-system/data/slide-typography.csv delete mode 100644 .agents/skills/ckm-design-system/references/component-specs.md delete mode 100644 .agents/skills/ckm-design-system/references/component-tokens.md delete mode 100644 .agents/skills/ckm-design-system/references/primitive-tokens.md delete mode 100644 .agents/skills/ckm-design-system/references/semantic-tokens.md delete mode 100644 .agents/skills/ckm-design-system/references/states-and-variants.md delete mode 100644 .agents/skills/ckm-design-system/references/tailwind-integration.md delete mode 100644 .agents/skills/ckm-design-system/references/token-architecture.md delete mode 100644 .agents/skills/ckm-design-system/scripts/embed-tokens.cjs delete mode 100644 .agents/skills/ckm-design-system/scripts/fetch-background.py delete mode 100644 .agents/skills/ckm-design-system/scripts/generate-slide.py delete mode 100644 .agents/skills/ckm-design-system/scripts/generate-tokens.cjs delete mode 100644 .agents/skills/ckm-design-system/scripts/html-token-validator.py delete mode 100755 .agents/skills/ckm-design-system/scripts/search-slides.py delete mode 100644 .agents/skills/ckm-design-system/scripts/slide-token-validator.py delete mode 100755 .agents/skills/ckm-design-system/scripts/slide_search_core.py delete mode 100644 .agents/skills/ckm-design-system/scripts/validate-tokens.cjs delete mode 100644 .agents/skills/ckm-design-system/templates/design-tokens-starter.json delete mode 100644 .agents/skills/ckm-ui-styling/LICENSE.txt delete mode 100644 .agents/skills/ckm-ui-styling/SKILL.md delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/ArsenalSC-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/ArsenalSC-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BigShoulders-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BigShoulders-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BigShoulders-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Boldonse-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Boldonse-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BricolageGrotesque-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BricolageGrotesque-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/BricolageGrotesque-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/CrimsonPro-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/CrimsonPro-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/CrimsonPro-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/CrimsonPro-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/DMMono-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/DMMono-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/EricaOne-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/EricaOne-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/GeistMono-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/GeistMono-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/GeistMono-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Gloock-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Gloock-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexMono-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexMono-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexMono-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexSerif-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexSerif-BoldItalic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexSerif-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/IBMPlexSerif-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSans-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSans-BoldItalic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSans-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSans-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSans-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSerif-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/InstrumentSerif-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Italiana-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Italiana-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/JetBrainsMono-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/JetBrainsMono-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/JetBrainsMono-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Jura-Light.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Jura-Medium.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Jura-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/LibreBaskerville-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/LibreBaskerville-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Lora-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Lora-BoldItalic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Lora-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Lora-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Lora-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/NationalPark-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/NationalPark-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/NationalPark-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/NothingYouCouldDo-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/NothingYouCouldDo-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Outfit-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Outfit-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Outfit-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/PixelifySans-Medium.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/PixelifySans-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/PoiretOne-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/PoiretOne-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/RedHatMono-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/RedHatMono-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/RedHatMono-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Silkscreen-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Silkscreen-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/SmoochSans-Medium.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/SmoochSans-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Tektur-Medium.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Tektur-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/Tektur-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/WorkSans-Bold.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/WorkSans-BoldItalic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/WorkSans-Italic.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/WorkSans-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/WorkSans-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/YoungSerif-OFL.txt delete mode 100644 .agents/skills/ckm-ui-styling/canvas-fonts/YoungSerif-Regular.ttf delete mode 100644 .agents/skills/ckm-ui-styling/references/canvas-design-system.md delete mode 100644 .agents/skills/ckm-ui-styling/references/shadcn-accessibility.md delete mode 100644 .agents/skills/ckm-ui-styling/references/shadcn-components.md delete mode 100644 .agents/skills/ckm-ui-styling/references/shadcn-theming.md delete mode 100644 .agents/skills/ckm-ui-styling/references/tailwind-customization.md delete mode 100644 .agents/skills/ckm-ui-styling/references/tailwind-responsive.md delete mode 100644 .agents/skills/ckm-ui-styling/references/tailwind-utilities.md delete mode 100644 .agents/skills/ckm-ui-styling/scripts/requirements.txt delete mode 100644 .agents/skills/ckm-ui-styling/scripts/shadcn_add.py delete mode 100644 .agents/skills/ckm-ui-styling/scripts/tailwind_config_gen.py delete mode 100644 .agents/skills/ckm-ui-styling/scripts/tests/coverage-ui.json delete mode 100644 .agents/skills/ckm-ui-styling/scripts/tests/requirements.txt delete mode 100644 .agents/skills/ckm-ui-styling/scripts/tests/test_shadcn_add.py delete mode 100644 .agents/skills/ckm-ui-styling/scripts/tests/test_tailwind_config_gen.py delete mode 100644 .agents/skills/ui-ux-pro-max/SKILL.md delete mode 100644 .agents/skills/ui-ux-pro-max/data/_sync_all.py delete mode 100644 .agents/skills/ui-ux-pro-max/data/app-interface.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/charts.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/colors.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/design.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/draft.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/google-fonts.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/icons.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/landing.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/products.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/react-performance.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/angular.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/astro.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/flutter.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/laravel.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/nextjs.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/react-native.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/react.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/shadcn.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/svelte.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/swiftui.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/threejs.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/stacks/vue.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/styles.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/typography.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/ui-reasoning.csv delete mode 100644 .agents/skills/ui-ux-pro-max/data/ux-guidelines.csv delete mode 100755 .agents/skills/ui-ux-pro-max/scripts/core.py delete mode 100644 .agents/skills/ui-ux-pro-max/scripts/design_system.py delete mode 100644 .agents/skills/ui-ux-pro-max/scripts/search.py diff --git a/.agents/skills/ckm-design-system/SKILL.md b/.agents/skills/ckm-design-system/SKILL.md deleted file mode 100644 index cfbcba0..0000000 --- a/.agents/skills/ckm-design-system/SKILL.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -name: ckm:design-system -description: Token architecture, component specifications, and slide generation. Three-layer tokens (primitive→semantic→component), CSS variables, spacing/typography scales, component specs, strategic slide creation. Use for design tokens, systematic design, brand-compliant presentations. -argument-hint: "[component or token]" -license: MIT -metadata: - author: claudekit - version: "1.0.0" ---- - -# Design System - -Token architecture, component specifications, systematic design, slide generation. - -## When to Use - -- Design token creation -- Component state definitions -- CSS variable systems -- Spacing/typography scales -- Design-to-code handoff -- Tailwind theme configuration -- **Slide/presentation generation** - -## Token Architecture - -Load: `references/token-architecture.md` - -### Three-Layer Structure - -``` -Primitive (raw values) - ↓ -Semantic (purpose aliases) - ↓ -Component (component-specific) -``` - -**Example:** -```css -/* Primitive */ ---color-blue-600: #2563EB; - -/* Semantic */ ---color-primary: var(--color-blue-600); - -/* Component */ ---button-bg: var(--color-primary); -``` - -## Quick Start - -**Generate tokens:** -```bash -node scripts/generate-tokens.cjs --config tokens.json -o tokens.css -``` - -**Validate usage:** -```bash -node scripts/validate-tokens.cjs --dir src/ -``` - -## References - -| Topic | File | -|-------|------| -| Token Architecture | `references/token-architecture.md` | -| Primitive Tokens | `references/primitive-tokens.md` | -| Semantic Tokens | `references/semantic-tokens.md` | -| Component Tokens | `references/component-tokens.md` | -| Component Specs | `references/component-specs.md` | -| States & Variants | `references/states-and-variants.md` | -| Tailwind Integration | `references/tailwind-integration.md` | - -## Component Spec Pattern - -| Property | Default | Hover | Active | Disabled | -|----------|---------|-------|--------|----------| -| Background | primary | primary-dark | primary-darker | muted | -| Text | white | white | white | muted-fg | -| Border | none | none | none | muted-border | -| Shadow | sm | md | none | none | - -## Scripts - -| Script | Purpose | -|--------|---------| -| `generate-tokens.cjs` | Generate CSS from JSON token config | -| `validate-tokens.cjs` | Check for hardcoded values in code | -| `search-slides.py` | BM25 search + contextual recommendations | -| `slide-token-validator.py` | Validate slide HTML for token compliance | -| `fetch-background.py` | Fetch images from Pexels/Unsplash | - -## Templates - -| Template | Purpose | -|----------|---------| -| `design-tokens-starter.json` | Starter JSON with three-layer structure | - -## Integration - -**With brand:** Extract primitives from brand colors/typography -**With ui-styling:** Component tokens → Tailwind config - -**Skill Dependencies:** brand, ui-styling -**Primary Agents:** ui-ux-designer, frontend-developer - -## Slide System - -Brand-compliant presentations using design tokens + Chart.js + contextual decision system. - -### Source of Truth - -| File | Purpose | -|------|---------| -| `docs/brand-guidelines.md` | Brand identity, voice, colors | -| `assets/design-tokens.json` | Token definitions (primitive→semantic→component) | -| `assets/design-tokens.css` | CSS variables (import in slides) | -| `assets/css/slide-animations.css` | CSS animation library | - -### Slide Search (BM25) - -```bash -# Basic search (auto-detect domain) -python scripts/search-slides.py "investor pitch" - -# Domain-specific search -python scripts/search-slides.py "problem agitation" -d copy -python scripts/search-slides.py "revenue growth" -d chart - -# Contextual search (Premium System) -python scripts/search-slides.py "problem slide" --context --position 2 --total 9 -python scripts/search-slides.py "cta" --context --position 9 --prev-emotion frustration -``` - -### Decision System CSVs - -| File | Purpose | -|------|---------| -| `data/slide-strategies.csv` | 15 deck structures + emotion arcs + sparkline beats | -| `data/slide-layouts.csv` | 25 layouts + component variants + animations | -| `data/slide-layout-logic.csv` | Goal → Layout + break_pattern flag | -| `data/slide-typography.csv` | Content type → Typography scale | -| `data/slide-color-logic.csv` | Emotion → Color treatment | -| `data/slide-backgrounds.csv` | Slide type → Image category (Pexels/Unsplash) | -| `data/slide-copy.csv` | 25 copywriting formulas (PAS, AIDA, FAB) | -| `data/slide-charts.csv` | 25 chart types with Chart.js config | - -### Contextual Decision Flow - -``` -1. Parse goal/context - ↓ -2. Search slide-strategies.csv → Get strategy + emotion beats - ↓ -3. For each slide: - a. Query slide-layout-logic.csv → layout + break_pattern - b. Query slide-typography.csv → type scale - c. Query slide-color-logic.csv → color treatment - d. Query slide-backgrounds.csv → image if needed - e. Apply animation class from slide-animations.css - ↓ -4. Generate HTML with design tokens - ↓ -5. Validate with slide-token-validator.py -``` - -### Pattern Breaking (Duarte Sparkline) - -Premium decks alternate between emotions for engagement: -``` -"What Is" (frustration) ↔ "What Could Be" (hope) -``` - -System calculates pattern breaks at 1/3 and 2/3 positions. - -### Slide Requirements - -**ALL slides MUST:** -1. Import `assets/design-tokens.css` - single source of truth -2. Use CSS variables: `var(--color-primary)`, `var(--slide-bg)`, etc. -3. Use Chart.js for charts (NOT CSS-only bars) -4. Include navigation (keyboard arrows, click, progress bar) -5. Center align content -6. Focus on persuasion/conversion - -### Chart.js Integration - -```html - - - - -``` - -### Token Compliance - -```css -/* CORRECT - uses token */ -background: var(--slide-bg); -color: var(--color-primary); -font-family: var(--typography-font-heading); - -/* WRONG - hardcoded */ -background: #0D0D0D; -color: #FF6B6B; -font-family: 'Space Grotesk'; -``` - -### Reference Implementation - -Working example with all features: -``` -assets/designs/slides/claudekit-pitch-251223.html -``` - -### Command - -```bash -/slides:create "10-slide investor pitch for ClaudeKit Marketing" -``` - -## Best Practices - -1. Never use raw hex in components - always reference tokens -2. Semantic layer enables theme switching (light/dark) -3. Component tokens enable per-component customization -4. Use HSL format for opacity control -5. Document every token's purpose -6. **Slides must import design-tokens.css and use var() exclusively** diff --git a/.agents/skills/ckm-design-system/data/slide-backgrounds.csv b/.agents/skills/ckm-design-system/data/slide-backgrounds.csv deleted file mode 100644 index 1150484..0000000 --- a/.agents/skills/ckm-design-system/data/slide-backgrounds.csv +++ /dev/null @@ -1,11 +0,0 @@ -slide_type,image_category,overlay_style,text_placement,image_sources,search_keywords -hero,abstract-tech,gradient-dark,center,pexels:unsplash,technology abstract gradient dark -vision,future-workspace,gradient-brand,left,pexels:unsplash,futuristic office modern workspace -team,professional-people,gradient-dark,bottom,pexels:unsplash,business team professional diverse -testimonial,office-environment,blur-dark,center,pexels:unsplash,modern office workspace bright -cta,celebration-success,gradient-brand,center,pexels:unsplash,success celebration achievement -problem,frustration-pain,desaturate-dark,center,pexels:unsplash,stress frustration problem dark -solution,breakthrough-moment,gradient-accent,right,pexels:unsplash,breakthrough success innovation light -hook,attention-grabbing,gradient-dark,center,pexels:unsplash,dramatic abstract attention bold -social,community-connection,blur-dark,center,pexels:unsplash,community collaboration connection -demo,product-showcase,gradient-dark,left,pexels:unsplash,technology product showcase clean diff --git a/.agents/skills/ckm-design-system/data/slide-charts.csv b/.agents/skills/ckm-design-system/data/slide-charts.csv deleted file mode 100644 index 373648e..0000000 --- a/.agents/skills/ckm-design-system/data/slide-charts.csv +++ /dev/null @@ -1,26 +0,0 @@ -id,chart_type,keywords,best_for,data_type,when_to_use,when_to_avoid,max_categories,slide_context,css_implementation,accessibility_notes,sources -1,Bar Chart Vertical,"bar, vertical, comparison, categories, ranking",Comparing values across categories,Categorical discrete,"Comparing 3-12 categories, showing ranking, highlighting differences",Continuous time data trends,12,Traction metrics feature comparison,"Chart.js or CSS flexbox with height percentage bars",Always label axes include values,Atlassian Data Charts -2,Bar Chart Horizontal,"horizontal bar, ranking, long labels, categories",Categories with long names ranking,Categorical discrete,"Long category names, 5+ items, reading left-to-right natural",Few categories time series,15,Team performance competitor analysis,"CSS flexbox with width percentage bars",Natural reading direction for labels,Datylon Blog -3,Line Chart,"line, trend, time series, growth, change over time",Showing trends over continuous time,Time series continuous,"Time-based data, showing growth trajectory, 10+ data points",Categorical comparisons,50+ points,Revenue growth MRR user growth,"Chart.js line or SVG path element",Include data point markers for screen readers,Tableau Best Practices -4,Area Chart,"area, cumulative, volume, trend, filled line",Showing volume or magnitude over time,Time series cumulative,"Emphasizing magnitude, showing cumulative totals, comparing totals",Precise value comparison,3-4 series,Revenue breakdown market share over time,"Chart.js area or SVG path with fill",Use patterns not just colors for series,Data Visualization Guide -5,Pie Chart,"pie, composition, percentage, parts, whole",Showing parts of a whole,Proportional percentage,"2-5 slices, showing simple composition, adds to 100%",More than 6 categories precise comparison,6 max,Market share budget allocation simple splits,"CSS conic-gradient or Chart.js pie",Never use 3D always include percentages,FusionCharts Blog -6,Donut Chart,"donut, composition, percentage, center metric",Parts of whole with center highlight,Proportional percentage,"Like pie but need center space for key metric, 2-5 segments",Many categories,6 max,Composition with key stat in center,"CSS conic-gradient with inner circle",Same as pie include legend,Modern alternative to pie -7,Stacked Bar,"stacked, composition, comparison, breakdown",Comparing composition across categories,Categorical + proportional,"Showing composition AND comparison, segment contribution",Too many segments precise values,5 segments,Revenue by segment across quarters,"Chart.js stacked bar or CSS nested divs",Order segments consistently use legend,Atlassian Data Charts -8,Grouped Bar,"grouped, clustered, side by side, multi-series",Comparing multiple metrics per category,Multi-series categorical,"Direct comparison of 2-3 metrics per category",Too many groups (>4) or categories (>8),4 groups,Feature comparison pricing tiers,"Chart.js grouped bar CSS grid bars",Color code consistently across groups,Data Visualization Best Practices -9,100% Stacked Bar,"100%, proportion, normalized, percentage",Comparing proportions across categories,Proportional comparative,"Comparing percentage breakdown across categories, not absolute values",Showing absolute values,5 segments,Market share comparison percentage breakdown,"CSS flexbox 100% width segments",Clearly indicate percentage scale,Proportional analysis -10,Funnel Chart,"funnel, conversion, stages, drop-off, pipeline",Showing conversion or drop-off through stages,Sequential stage-based,"Sales funnel, conversion rates, sequential process with decreasing values",Non-sequential data equal stages,6-8 stages,User conversion sales pipeline,"CSS trapezoid shapes or SVG",Label each stage with count and percentage,Marketing/Sales standard -11,Gauge Chart,"gauge, progress, goal, target, kpi",Showing progress toward a goal,Single metric vs target,"Single KPI progress, goal completion, health scores",Multiple metrics,1 metric,Goal progress health score,"CSS conic-gradient or arc SVG",Include numeric value not just visual,Dashboard widgets -12,Sparkline,"sparkline, mini, inline, trend, compact",Showing trend in minimal space,Time series inline,"Inline metrics, table cells, compact trend indication",Detailed analysis,N/A,Metric cards with trend indicator,SVG path or canvas inline,Supplement with text for accessibility,Edward Tufte -13,Scatter Plot,"scatter, correlation, relationship, distribution",Showing relationship between two variables,Bivariate continuous,"Correlation analysis, pattern detection, outlier identification",Categorical data simple comparisons,100+ points,Correlation analysis segmentation,Canvas or SVG circles positioned,Include trend line if meaningful,Statistical visualization -14,Bubble Chart,"bubble, three variables, scatter, size",Showing three variables simultaneously,Trivariate continuous,"Three-variable comparison, population/size matters",Simple comparisons,30-50 bubbles,Market analysis with size dimension,"SVG circles with varying radius",Legend for size scale essential,Data Visualization Guide -15,Heatmap,"heatmap, matrix, intensity, correlation, grid",Showing intensity across two dimensions,Matrix intensity,"Large data matrices, time-day patterns, correlation matrices",Few data points,Unlimited grid,Usage patterns correlation matrices,CSS grid with background-color intensity,Use colorblind-safe gradients,Datylon Blog -16,Waterfall Chart,"waterfall, bridge, contribution, breakdown",Showing how values add to a total,Cumulative contribution,"Financial analysis, showing positive/negative contributions",Non-additive data,10-15 items,Revenue bridge profit breakdown,"CSS positioned bars with connectors",Clear positive/negative color coding,Financial reporting standard -17,Treemap,"treemap, hierarchy, nested, proportion",Showing hierarchical proportional data,Hierarchical proportional,"Nested categories, space-efficient proportions, 2 levels max",Simple comparisons few items,50+ items,Budget breakdown category analysis,"CSS grid with calculated areas",Include text labels on larger segments,Ben Shneiderman -18,Radar Chart,"radar, spider, multi-metric, profile",Comparing multiple metrics for single item,Multi-metric profile,"Comparing 5-8 metrics for one or two items, skill profiles",More than 3 items to compare,8 axes max,Feature profile skill assessment,SVG polygon on axes,Ensure scale is clear and consistent,Profile comparison -19,Bullet Chart,"bullet, target, actual, performance",Showing actual vs target with ranges,KPI with target,"Progress against target with qualitative ranges",Simple goal tracking,1-3 per slide,KPI performance with targets,"CSS layered bars with markers",Clearly label target and actual,Stephen Few -20,Timeline,"timeline, chronology, history, milestones",Showing events over time,Event-based temporal,"History roadmap milestones, showing progression",Quantitative comparison,10-15 events,Company history product roadmap,"CSS flexbox with positioned markers",Ensure logical reading order,Chronological visualization -21,Sankey Diagram,"sankey, flow, distribution, connections",Showing flow or distribution between nodes,Flow distribution,"Showing how values flow from source to destination",Simple distributions,15-20 nodes,User flow budget flow,D3.js or dedicated library,Alternative text description essential,Complex flow visualization -22,KPI Card,"kpi, metric, number, stat, scorecard",Highlighting single important metric,Single metric,"Dashboard hero metrics, emphasizing one key number",Showing trends or comparisons,1 number,Main KPI highlight,"Large font-size centered number",Include trend context if relevant,Dashboard design -23,Progress Bar,"progress, completion, percentage, bar",Showing completion percentage,Single percentage,"Simple progress indication, goal completion",Multiple goals comparison,1 per context,Project completion goal progress,"CSS width with percentage gradient",Include numeric percentage,UI/UX standard -24,Comparison Table,"table, comparison, matrix, features",Detailed feature or value comparison,Multi-attribute categorical,"Detailed comparison, many attributes, exact values matter",Visual impact storytelling,10-15 rows,Feature matrix pricing comparison,"HTML table with CSS styling",Proper table headers and scope,Information design -25,Icon Array,"icon array, pictogram, proportion, visual",Showing proportions with visual metaphor,Proportional visual,"Making statistics tangible (e.g. 1 in 10 people), visual impact",Precise values large numbers,100 icons,Statistics visualization impact slides,"CSS grid or flexbox with icons",Describe proportion in text,ISOTYPE Otto Neurath diff --git a/.agents/skills/ckm-design-system/data/slide-color-logic.csv b/.agents/skills/ckm-design-system/data/slide-color-logic.csv deleted file mode 100644 index d9481d6..0000000 --- a/.agents/skills/ckm-design-system/data/slide-color-logic.csv +++ /dev/null @@ -1,14 +0,0 @@ -emotion,background,text_color,accent_usage,use_full_bleed,gradient,card_style -frustration,dark-surface,foreground,minimal,false,none,subtle-border -hope,accent-bleed,dark,none,true,none,none -fear,dark-background,primary,stats-only,false,none,glow-primary -relief,surface,foreground,icons,false,none,accent-bar -trust,surface-elevated,foreground,metrics,false,none,subtle-border -urgency,gradient,white,cta-button,true,primary,none -curiosity,dark-glow,gradient-text,badge,false,glow,glow-secondary -confidence,surface,foreground,chart-accent,false,none,none -warmth,dark-surface,foreground,avatar-ring,false,none,none -evaluation,surface-elevated,foreground,highlight,false,none,comparison -narrative,dark-background,foreground-secondary,timeline-dots,false,none,none -clarity,surface,foreground,icons,false,none,feature-card -interest,dark-glow,foreground,demo-highlight,false,glow,none diff --git a/.agents/skills/ckm-design-system/data/slide-copy.csv b/.agents/skills/ckm-design-system/data/slide-copy.csv deleted file mode 100644 index ed448c0..0000000 --- a/.agents/skills/ckm-design-system/data/slide-copy.csv +++ /dev/null @@ -1,26 +0,0 @@ -id,formula_name,keywords,components,use_case,example_template,emotion_trigger,slide_type,source -1,AIDA,"aida, attention, interest, desire, action",Attention→Interest→Desire→Action,Lead-gen CTAs general persuasion,"{Attention hook} → {Interesting detail} → {Desirable outcome} → {Action step}",Curiosity→Engagement→Want→Urgency,CTA slides,Classic copywriting 1898 -2,PAS,"pas, problem, agitation, solution, dan kennedy",Problem→Agitate→Solution,Sales pages problem slides most reliable,"You're struggling with {problem}. It's costing you {agitation}. {Solution} fixes this.",Frustration→Fear→Relief,Problem slides,Dan Kennedy -3,4Ps,"4ps, promise, picture, proof, push, ray edwards",Promise→Picture→Proof→Push,Home pages lead-gen,"{Promise benefit} → {Picture future state} → {Proof it works} → {Push to act}",Hope→Vision→Trust→Action,Solution slides,Ray Edwards -4,Before-After-Bridge,"bab, before, after, bridge, transformation",Before→After→Bridge,Transformation case studies,"Before: {old state}. After: {new state}. Bridge: {how to get there}",Pain→Pleasure→Path,Before/after slides,Copywriting classic -5,QUEST,"quest, qualify, understand, educate, stimulate, transition",Qualify→Understand→Educate→Stimulate→Transition,Matching solution to prospect,"{Qualify audience} → {Show understanding} → {Educate on solution} → {Stimulate desire} → {Transition to CTA}",Recognition→Empathy→Learning→Excitement,Educational slides,Michel Fortin -6,Star-Story-Solution,"star, story, solution, narrative",Star→Story→Solution,Personality brands info products,"{Introduce character} → {Tell their struggle} → {Reveal their solution}",Connection→Empathy→Hope,Case study slides,CopyHackers -7,Feature-Advantage-Benefit,"fab, feature, advantage, benefit",Feature→Advantage→Benefit,Feature explanations product slides,"{Feature name}: {What it does} → {Why that matters} → {How it helps you}",Curiosity→Understanding→Desire,Feature slides,Sales training classic -8,What If,"what if, imagination, possibility, hook",What if + Possibility,Opening hooks vision slides,"What if you could {desirable outcome} without {common obstacle}?",Wonder→Possibility,Title problem slides,Headline formula -9,How To,"how to, tutorial, guide, instruction",How to + Specific outcome,Educational actionable content,"How to {achieve specific result} in {timeframe or steps}",Curiosity→Empowerment,Educational slides,Headline formula -10,Number List,"number, list, reasons, ways, tips",Number + Topic + Promise,Scannable benefit lists,"{Number} {Ways/Reasons/Tips} to {achieve outcome}",Curiosity→Completeness,Feature summary slides,Content marketing -11,Question Hook,"question, hook, curiosity, engagement",Question that implies answer,Opening engagement slides,"{Question that reader answers yes to}? Here's how.",Recognition→Curiosity,Opening slides,Rhetorical technique -12,Proof Stack,"proof, evidence, credibility, stats",Stat→Source→Implication,Building credibility trust,"{Impressive stat} (Source: {credible source}). This means {implication for audience}.",Trust→Validation,Traction proof slides,Social proof theory -13,Future Pacing,"future, vision, imagine, picture this",Imagine + Future state,Vision and aspiration slides,"Imagine: {desirable future scenario}. That's what {solution} delivers.",Aspiration→Desire,Solution CTA slides,NLP technique -14,Social Proof,"social proof, testimonial, customers, trust",Who + Result + Quote,Credibility through others,"{Customer name} increased {metric} by {amount}. '{Quote about experience}'",Trust→FOMO,Testimonial slides,Robert Cialdini -15,Scarcity Urgency,"scarcity, urgency, limited, deadline, fomo",Limited + Deadline + Consequence,Driving action urgency,"Only {quantity} available. Offer ends {date}. {Consequence of missing out}.",Fear of loss→Action,CTA closing slides,Cialdini influence -16,Cost of Inaction,"cost, inaction, consequence, loss",Current cost + Future cost + Comparison,Motivating change,"Every {timeframe} without {solution} costs you {quantified loss}. That's {larger number} per year.",Loss aversion→Urgency,Problem agitation slides,Loss aversion psychology -17,Simple Benefit,"benefit, value, outcome, result",You get + Specific benefit,Clear value communication,"{Solution}: You get {specific tangible benefit}.",Clarity→Desire,Any slide,Direct response -18,Objection Preempt,"objection, concern, but, however, faq",Objection + Response + Proof,"Handling concerns proactively","You might think {objection}. Actually, {counter with proof}.",Doubt→Resolution,FAQ objection slides,Sales training -19,Comparison Frame,"comparison, versus, than, better, alternative",Us vs Them + Specific difference,Competitive positioning,"{Competitor approach}: {limitation}. {Our approach}: {advantage}.",Evaluation→Preference,Comparison slides,Positioning strategy -20,Pain-Claim-Gain,"pcg, pain, claim, gain",Pain point→Bold claim→Specific gain,Concise value proposition,"{Pain point}? {Bold claim about solution}. Result: {specific gain}.",Frustration→Hope→Excitement,Problem/solution slides,Copywriting framework -21,One Thing,"one thing, single, focus, key",The one thing + Why it matters,Focus and clarity,"The #1 thing {audience} needs to {outcome} is {one thing}.",Focus→Clarity,Key message slides,Gary Keller concept -22,Riddle Open,"riddle, mystery, puzzle, question",Mystery + Reveal + Implication,Engagement through curiosity,"{Intriguing mystery or paradox}. The answer: {reveal}. For you: {implication}.",Mystery→Insight,Opening slides,Storytelling technique -23,Hero Journey,"hero, journey, transformation, story",Ordinary→Call→Challenge→Triumph,Narrative structure,"{Character in ordinary world} → {Discovers challenge} → {Overcomes with solution} → {Achieves transformation}",Identification→Tension→Triumph,Full deck structure,Joseph Campbell -24,Value Stack,"value, stack, bundle, worth",Component + Value → Total value,Justifying price/investment,"{Item 1} (Worth ${X}) + {Item 2} (Worth ${Y}) + ... = Total value ${Z}. Your investment: ${actual price}.",Value perception,Pricing offer slides,Info product marketing -25,Power Statement,"power, statement, bold, declaration",Bold declaration + Supporting fact,Authority and confidence,"{Bold declaration}. {Supporting evidence or fact}.",Confidence→Trust,Key message slides,Thought leadership diff --git a/.agents/skills/ckm-design-system/data/slide-layout-logic.csv b/.agents/skills/ckm-design-system/data/slide-layout-logic.csv deleted file mode 100644 index a673bec..0000000 --- a/.agents/skills/ckm-design-system/data/slide-layout-logic.csv +++ /dev/null @@ -1,16 +0,0 @@ -goal,emotion,layout_pattern,direction,visual_weight,break_pattern,use_bg_image -hook,curiosity,split-hero,visual-right,70-visual,false,true -problem,frustration,card-grid,centered,balanced,false,false -agitation,fear,full-bleed-stat,centered,100-text,true,false -solution,relief,split-feature,visual-left,50-50,false,true -proof,trust,metric-grid,centered,numbers-dominant,false,false -social,connection,quote-hero,centered,80-text,true,true -comparison,evaluation,split-compare,side-by-side,balanced,false,false -traction,confidence,chart-insight,chart-left,60-chart,false,false -cta,urgency,gradient-cta,centered,100-text,true,true -team,warmth,team-grid,centered,balanced,false,true -pricing,evaluation,pricing-cards,centered,balanced,false,false -demo,interest,split-demo,visual-left,60-visual,false,false -vision,hope,full-bleed-hero,centered,100-visual,true,true -timeline,narrative,timeline-flow,horizontal,balanced,false,false -features,clarity,feature-grid,centered,balanced,false,false diff --git a/.agents/skills/ckm-design-system/data/slide-layouts.csv b/.agents/skills/ckm-design-system/data/slide-layouts.csv deleted file mode 100644 index 708be11..0000000 --- a/.agents/skills/ckm-design-system/data/slide-layouts.csv +++ /dev/null @@ -1,26 +0,0 @@ -id,layout_name,keywords,use_case,content_zones,visual_weight,cta_placement,recommended_for,avoid_for,css_structure,card_variant,metric_style,quote_style,grid_columns,visual_treatment,animation_class -1,Title Slide,"title, cover, opening, intro, hero",Opening slide first impression,"Center: Logo + Title + Tagline, Bottom: Date/Presenter",Visual-heavy minimal text,None or subtle,All presentations,Never skip,"display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center",none,none,none,1,gradient-glow,animate-fade-up -2,Problem Statement,"problem, pain, challenge, issue",Establish the problem being solved,"Left: Problem headline, Right: Pain point bullets or icon grid",50/50 text visual balance,None,Pitch decks sales,Internal updates,"display:grid; grid-template-columns:1fr 1fr; gap:48px; align-items:center",icon-left,none,none,2,subtle-border,animate-stagger -3,Solution Overview,"solution, answer, approach, how",Introduce your solution,"Top: Solution headline, Center: Solution visual/diagram, Bottom: 3 key points",Visual-dominant,Subtle learn more,After problem slide,Without context,"display:flex; flex-direction:column; gap:32px",accent-bar,none,none,3,icon-top,animate-scale -4,Feature Grid,"features, grid, cards, capabilities, 3-column",Showcase multiple features,"Top: Section title, Grid: 3-6 feature cards with icon+title+description",Balanced grid,Bottom CTA optional,Product demos SaaS,Storytelling slides,"display:grid; grid-template-columns:repeat(3,1fr); gap:24px",accent-bar,none,none,3,icon-top,animate-stagger -5,Metrics Dashboard,"metrics, kpis, numbers, stats, data",Display key performance data,"Top: Context headline, Center: 3-4 large metric cards, Bottom: Trend context",Numbers-dominant,None,Traction slides QBRs,Early-stage no data,"display:grid; grid-template-columns:repeat(4,1fr); gap:16px",metric-card,gradient-number,none,4,none,animate-stagger-scale -6,Comparison Table,"comparison, vs, versus, table, matrix",Compare options or competitors,"Top: Comparison title, Center: Feature comparison table, Bottom: Conclusion",Table-heavy,Highlight winner row,Competitive analysis,Storytelling,"display:flex; flex-direction:column; table width:100%",comparison,none,none,2,highlight-winner,animate-fade-up -7,Timeline Flow,"timeline, roadmap, journey, steps, process",Show progression over time,"Top: Timeline title, Center: Horizontal timeline with milestones, Bottom: Current status",Visual timeline,End milestone CTA,Roadmaps history,Dense data,"display:flex; flex-direction:column; timeline:flex with arrows",none,none,none,1,timeline-dots,animate-stagger -8,Team Grid,"team, people, founders, leadership",Introduce team members,"Top: Team title, Grid: Photo + Name + Title + Brief bio cards",Photo-heavy,None or careers link,Investor decks about,Technical content,"display:grid; grid-template-columns:repeat(4,1fr); gap:24px",avatar-card,none,none,4,avatar-ring,animate-stagger -9,Quote Testimonial,"quote, testimonial, social proof, customer",Feature customer endorsement,"Center: Large quote text, Bottom: Photo + Name + Title + Company logo",Quote-dominant minimal UI,None,Sales case studies,Without real quotes,"display:flex; flex-direction:column; justify-content:center; font-size:large; font-style:italic",none,none,large-italic,1,author-avatar,animate-fade-up -10,Two Column Split,"split, two-column, side-by-side, comparison",Present two related concepts,"Left column: Content A, Right column: Content B",50/50 balanced,Either column bottom,Comparisons before/after,Single concept,display:grid; grid-template-columns:1fr 1fr; gap:48px,none,none,none,2,offset-image,animate-fade-up -11,Big Number Hero,"big number, stat, impact, headline metric",Emphasize one powerful metric,"Center: Massive number, Below: Context label and trend",Number-dominant,None,Impact slides traction,Multiple metrics,"display:flex; flex-direction:column; justify-content:center; align-items:center; font-size:120px",none,oversized,none,1,centered,animate-count -12,Product Screenshot,"screenshot, product, demo, ui, interface",Show product in action,"Top: Feature headline, Center: Product screenshot with annotations, Bottom: Key callouts",Screenshot-dominant,Try it CTA,Product demos,Abstract concepts,"display:flex; flex-direction:column; img max-height:60vh",none,none,none,1,screenshot-shadow,animate-scale -13,Pricing Cards,"pricing, plans, tiers, packages",Present pricing options,"Top: Pricing headline, Center: 2-4 pricing cards side by side, Bottom: FAQ or guarantee",Cards balanced,Each card has CTA,Sales pricing pages,Free products,"display:grid; grid-template-columns:repeat(3,1fr); gap:24px; .popular:scale(1.05)",pricing-card,none,none,3,popular-highlight,animate-stagger -14,CTA Closing,"cta, closing, call to action, next steps, final",Drive action end presentation,"Center: Bold headline + Value reminder, Center: Primary CTA button, Below: Secondary option",CTA-dominant,Primary center,All presentations,Middle slides,"display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center",none,none,none,1,gradient-bg,animate-pulse -15,Agenda Overview,"agenda, outline, contents, structure",Preview presentation structure,"Top: Agenda title, Center: Numbered list or visual timeline of sections",Text-light scannable,None,Long presentations,Short 3-5 slides,"display:flex; flex-direction:column; ol list-style-type:decimal",none,none,none,1,numbered-list,animate-stagger -16,Before After,"before, after, transformation, results, comparison",Show transformation impact,"Left: Before state (muted), Right: After state (vibrant), Center: Arrow or transition",50/50 high contrast,After column CTA,Case studies results,No transformation data,"display:grid; grid-template-columns:1fr 1fr; .before:opacity(0.7)",comparison,none,none,2,contrast-pair,animate-scale -17,Icon Grid Stats,"icons, stats, grid, key points, summary",Summarize key points visually,"Grid: 4-6 icon + stat + label combinations",Icons-dominant,None,Summary slides,Detailed explanations,"display:grid; grid-template-columns:repeat(3,1fr); gap:32px; text-align:center",icon-stat,sparkline,none,3,icon-top,animate-stagger -18,Full Bleed Image,"image, photo, visual, background, hero",Create visual impact,"Full background image, Overlay: Text with contrast, Corner: Logo",Image-dominant,Overlay CTA optional,Emotional moments,Data-heavy,background-size:cover; color:white; text-shadow for contrast,none,none,none,1,bg-overlay,animate-ken-burns -19,Video Embed,"video, demo, embed, multimedia",Show video content,"Top: Context headline, Center: Video player (16:9), Bottom: Key points if needed",Video-dominant,After video CTA,Demos testimonials,Reading-focused,"aspect-ratio:16/9; video controls",none,none,none,1,video-frame,animate-scale -20,Funnel Diagram,"funnel, conversion, stages, pipeline",Show conversion or process flow,"Top: Funnel title, Center: Funnel visualization with stage labels and metrics",Diagram-dominant,None,Sales marketing funnels,Non-sequential data,SVG or CSS trapezoid shapes,none,funnel-numbers,none,1,funnel-gradient,animate-chart -21,Quote Plus Stats,"quote, stats, hybrid, testimonial, metrics",Combine social proof with data,"Left: Customer quote with photo, Right: 3 supporting metrics",Balanced quote/data,None,Sales enablement,Without both elements,"display:grid; grid-template-columns:1.5fr 1fr; gap:48px",metric-card,gradient-number,side-quote,2,author-avatar,animate-stagger -22,Section Divider,"section, divider, break, transition",Transition between sections,"Center: Section number + Section title, Minimal design",Typography-only,None,Long presentations,Every slide,"display:flex; justify-content:center; align-items:center; font-size:48px",none,none,none,1,section-number,animate-fade-up -23,Logo Grid,"logos, clients, partners, trust, social proof",Display client or partner logos,"Top: Trust headline, Grid: 8-16 logos evenly spaced",Logos-only,None,Credibility slides,Few logos <6,"display:grid; grid-template-columns:repeat(4,1fr); gap:32px; filter:grayscale(1)",none,none,none,4,logo-grayscale,animate-stagger -24,Chart Focus,"chart, graph, data, visualization, analytics",Present data visualization,"Top: Chart title and context, Center: Single large chart, Bottom: Key insight",Chart-dominant,None,Data-driven slides,Poor data quality,"chart max-height:65vh; annotation for key point",none,sparkline,none,1,chart-left,animate-chart -25,Q&A Slide,"qa, questions, discussion, interactive",Invite audience questions,"Center: Q&A or Questions? text, Below: Contact info or submission method",Minimal text,None,End of presentations,Skip if no time,"display:flex; justify-content:center; align-items:center; font-size:64px",none,none,none,1,centered,animate-fade-up diff --git a/.agents/skills/ckm-design-system/data/slide-strategies.csv b/.agents/skills/ckm-design-system/data/slide-strategies.csv deleted file mode 100644 index 1aa2760..0000000 --- a/.agents/skills/ckm-design-system/data/slide-strategies.csv +++ /dev/null @@ -1,16 +0,0 @@ -id,strategy_name,keywords,slide_count,structure,goal,audience,tone,narrative_arc,key_metrics,sources,emotion_arc,sparkline_beats -1,YC Seed Deck,"yc, seed, startup, investor, funding, vc, venture","10-12","1.Title 2.Problem 3.Solution 4.Traction 5.Market 6.Product 7.Business Model 8.Team 9.Financials 10.Ask",Raise seed funding from VCs,Seed investors hunting asymmetric upside,Clear concise focused narrative,Problem→Solution→Evidence→Ask,MRR ARR growth rate user count,Y Combinator Library,"curiosity→frustration→hope→confidence→trust→urgency","hook|what-is|what-could-be|proof|proof|what-could-be|proof|trust|what-could-be|action" -2,Guy Kawasaki 10/20/30,"kawasaki, pitch, investor, 10 slides, venture","10","1.Title 2.Problem/Opportunity 3.Value Proposition 4.Underlying Magic 5.Business Model 6.Go-to-Market 7.Competition 8.Team 9.Projections 10.Status/Timeline/Ask",Pitch to investors in 20 min,VCs angel investors,Confident not arrogant,Hook→Magic→Proof→Ask,5yr projections milestones,Guy Kawasaki Blog,"curiosity→frustration→hope→confidence→trust→urgency","hook|what-is|what-could-be|what-could-be|proof|proof|evaluation|trust|proof|action" -3,Series A Deck,"series a, growth, scale, investor, traction","12-15","1.Title 2.Mission 3.Problem 4.Solution 5.Traction/Metrics 6.Product Demo 7.Market Size 8.Business Model 9.Competition 10.Team 11.Go-to-Market 12.Financials 13.Use of Funds 14.Ask",Raise Series A funding,Growth-stage VCs,Data-driven confident,Traction→Scale→Vision,Revenue growth LTV CAC cohorts,YC Library,"curiosity→hope→frustration→relief→confidence→trust→urgency","hook|what-could-be|what-is|what-could-be|proof|proof|proof|proof|evaluation|trust|proof|proof|what-could-be|action" -4,Product Demo,"demo, product, walkthrough, features, saas","5-8","1.Hook/Problem 2.Solution Overview 3.Live Demo/Screenshots 4.Key Features 5.Benefits 6.Pricing 7.CTA",Demonstrate product value,Prospects users,Enthusiastic helpful,Problem→See it work→Value,Conversion engagement time-saved,Product-led growth best practices,"curiosity→frustration→hope→confidence→urgency","hook|what-is|what-could-be|what-could-be|what-could-be|evaluation|action" -5,Sales Pitch,"sales, pitch, prospect, close, deal","7-10","1.Personalized Hook 2.Their Problem 3.Cost of Inaction 4.Your Solution 5.Proof/Case Studies 6.Differentiators 7.Pricing/ROI 8.Objection Handling 9.CTA 10.Next Steps",Close deal win customer,Qualified prospects,Consultative trustworthy,Pain→Agitate→Solve→Prove,ROI case study metrics,Sandler Sales Training,"connection→frustration→fear→hope→trust→confidence→urgency","hook|what-is|what-is|what-could-be|proof|what-could-be|evaluation|trust|action|action" -6,Nancy Duarte Sparkline,"duarte, sparkline, story, transformation, resonate","Varies","Alternate: What Is→What Could Be→What Is→What Could Be→New Bliss",Transform audience perspective,Any audience needing persuasion,Inspiring visionary,Tension→Release→Tension→Release→Resolution,Audience transformation,Nancy Duarte Resonate,"frustration→hope→frustration→hope→relief","what-is|what-could-be|what-is|what-could-be|new-bliss" -7,Problem-Solution-Benefit,"psb, simple, clear, benefit, value","3-5","1.Problem Statement 2.Solution Introduction 3.Key Benefits 4.Proof 5.CTA",Quick persuasion simple message,Time-pressed audience,Direct clear,Problem→Solution→Outcome,Core value metrics,Marketing fundamentals,"frustration→hope→confidence→urgency","what-is|what-could-be|what-could-be|proof|action" -8,Quarterly Business Review,"qbr, business review, internal, stakeholder","10-15","1.Executive Summary 2.Goals vs Results 3.Key Metrics 4.Wins 5.Challenges 6.Learnings 7.Customer Insights 8.Competitive Update 9.Next Quarter Goals 10.Resource Needs",Update stakeholders on progress,Internal leadership,Professional factual,Review→Analyze→Plan,KPIs OKRs progress %,Internal communications,"clarity→trust→confidence→evaluation→hope","summary|proof|proof|celebration|what-is|insight|trust|evaluation|what-could-be|action" -9,Team All-Hands,"all-hands, company, internal, culture, update","8-12","1.Opening/Energy 2.Company Wins 3.Metrics Dashboard 4.Team Spotlights 5.Product Updates 6.Customer Stories 7.Challenges/Learnings 8.Roadmap Preview 9.Q&A 10.Closing Motivation",Align and motivate team,All employees,Transparent inspiring,Celebrate→Update→Align→Energize,Company-wide KPIs,Internal communications,"warmth→confidence→trust→connection→hope→urgency","hook|celebration|proof|connection|what-could-be|trust|what-is|what-could-be|interaction|action" -10,Conference Talk,"conference, talk, keynote, public speaking, thought leadership","15-25","1.Hook/Story 2.Credibility 3.Big Idea 4.Point 1 + Evidence 5.Point 2 + Evidence 6.Point 3 + Evidence 7.Synthesis 8.Call to Action 9.Q&A Prep",Establish thought leadership,Conference attendees,Expert engaging,Story→Teach→Inspire,Audience engagement social shares,TED Talk guidelines,"curiosity→trust→hope→confidence→confidence→confidence→clarity→urgency","hook|trust|what-could-be|proof|proof|proof|synthesis|action|interaction" -11,Workshop Training,"workshop, training, education, how-to, tutorial","20-40","1.Welcome/Objectives 2.Agenda 3.Concept 1 4.Exercise 1 5.Concept 2 6.Exercise 2 7.Concept 3 8.Exercise 3 9.Synthesis 10.Resources 11.Q&A",Teach practical skills,Learners trainees,Patient instructive,Learn→Practice→Apply→Reflect,Skill acquisition completion,Adult learning principles,"warmth→clarity→confidence→confidence→confidence→confidence→clarity→hope","welcome|structure|teaching|practice|teaching|practice|teaching|practice|synthesis|resources|interaction" -12,Case Study Presentation,"case study, success story, customer, results","8-12","1.Customer Introduction 2.Their Challenge 3.Why They Chose Us 4.Implementation 5.Solution Details 6.Results/Metrics 7.Customer Quote 8.Lessons Learned 9.Applicability 10.CTA",Prove value through example,Prospects similar to case,Authentic factual,Challenge→Journey→Transformation,Before/after metrics ROI,Marketing case study best practices,"connection→frustration→trust→hope→confidence→celebration→trust→clarity→urgency","connection|what-is|trust|what-could-be|what-could-be|proof|trust|insight|what-could-be|action" -13,Competitive Analysis,"competitive, analysis, comparison, market","6-10","1.Market Landscape 2.Competitor Overview 3.Feature Comparison Matrix 4.Pricing Comparison 5.Strengths/Weaknesses 6.Our Differentiation 7.Market Positioning 8.Strategic Recommendations",Inform strategic decisions,Internal leadership,Analytical objective,Landscape→Analysis→Strategy,Market share feature gaps,Competitive intelligence,"clarity→evaluation→evaluation→evaluation→clarity→hope→confidence→urgency","overview|evaluation|comparison|comparison|analysis|what-could-be|proof|action" -14,Board Meeting Deck,"board, governance, investor update, quarterly","15-20","1.Agenda 2.Executive Summary 3.Financial Overview 4.Key Metrics 5.Product Update 6.Sales/Marketing 7.Operations 8.Team/Hiring 9.Risks/Challenges 10.Strategic Initiatives 11.Upcoming Milestones 12.Ask/Discussion",Update board on company status,Board members,Professional detailed,Report→Analyze→Discuss→Decide,All key business metrics,Board governance best practices,"clarity→confidence→trust→trust→confidence→confidence→trust→connection→evaluation→hope→confidence→urgency","structure|summary|proof|proof|proof|proof|proof|trust|what-is|what-could-be|proof|action" -15,Webinar Presentation,"webinar, online, education, lead gen","20-30","1.Welcome/Housekeeping 2.Presenter Intro 3.Agenda 4.Hook/Problem 5.Teaching Content 6.Case Study 7.Product Introduction 8.Demo 9.Offer/CTA 10.Q&A 11.Resources",Generate leads educate prospects,Webinar registrants,Educational helpful,Teach→Demonstrate→Offer,Registrations attendance conversion,Webinar marketing best practices,"warmth→trust→clarity→curiosity→confidence→trust→hope→confidence→urgency→connection→clarity","welcome|trust|structure|hook|teaching|trust|what-could-be|proof|action|interaction|resources" diff --git a/.agents/skills/ckm-design-system/data/slide-typography.csv b/.agents/skills/ckm-design-system/data/slide-typography.csv deleted file mode 100644 index 204243c..0000000 --- a/.agents/skills/ckm-design-system/data/slide-typography.csv +++ /dev/null @@ -1,15 +0,0 @@ -content_type,primary_size,secondary_size,accent_size,weight_contrast,letter_spacing,line_height -hero-statement,120px,32px,14px,700-400,tight,1.0 -metric-callout,96px,18px,12px,700-500,normal,1.1 -feature-grid,28px,16px,12px,600-400,normal,1.4 -quote-block,36px,18px,14px,400-italic,loose,1.5 -data-insight,48px,20px,14px,700-400,normal,1.2 -cta-action,64px,24px,16px,700-500,tight,1.1 -title-only,80px,24px,14px,700-400,tight,1.0 -subtitle-heavy,56px,28px,16px,600-400,normal,1.2 -body-focus,24px,18px,14px,500-400,normal,1.6 -comparison,32px,16px,12px,600-400,normal,1.3 -timeline,28px,16px,12px,500-400,normal,1.4 -pricing,48px,20px,14px,700-500,normal,1.2 -team,24px,16px,14px,600-400,normal,1.4 -testimonial,32px,20px,14px,400-italic,loose,1.5 diff --git a/.agents/skills/ckm-design-system/references/component-specs.md b/.agents/skills/ckm-design-system/references/component-specs.md deleted file mode 100644 index cc7821b..0000000 --- a/.agents/skills/ckm-design-system/references/component-specs.md +++ /dev/null @@ -1,236 +0,0 @@ -# Component Specifications - -Detailed specs for core components with states and variants. - -## Button - -### Variants - -| Variant | Background | Text | Border | Use Case | -|---------|------------|------|--------|----------| -| default | primary | white | none | Primary actions | -| secondary | gray-100 | gray-900 | none | Secondary actions | -| outline | transparent | foreground | border | Tertiary actions | -| ghost | transparent | foreground | none | Subtle actions | -| link | transparent | primary | none | Navigation | -| destructive | red-600 | white | none | Dangerous actions | - -### Sizes - -| Size | Height | Padding X | Padding Y | Font Size | Icon Size | -|------|--------|-----------|-----------|-----------|-----------| -| sm | 32px | 12px | 6px | 14px | 16px | -| default | 40px | 16px | 8px | 14px | 18px | -| lg | 48px | 24px | 12px | 16px | 20px | -| icon | 40px | 0 | 0 | - | 18px | - -### States - -| State | Background | Text | Opacity | Cursor | -|-------|------------|------|---------|--------| -| default | token | token | 1 | pointer | -| hover | darker | token | 1 | pointer | -| active | darkest | token | 1 | pointer | -| focus | token | token | 1 | pointer | -| disabled | muted | muted-fg | 0.5 | not-allowed | -| loading | token | token | 0.7 | wait | - -### Anatomy - -``` -┌─────────────────────────────────────┐ -│ [icon] Label Text [icon] │ -└─────────────────────────────────────┘ - ↑ ↑ - leading icon trailing icon -``` - ---- - -## Input - -### Variants - -| Variant | Description | -|---------|-------------| -| default | Standard text input | -| textarea | Multi-line text | -| select | Dropdown selection | -| checkbox | Boolean toggle | -| radio | Single selection | -| switch | Toggle switch | - -### Sizes - -| Size | Height | Padding | Font Size | -|------|--------|---------|-----------| -| sm | 32px | 8px 12px | 14px | -| default | 40px | 8px 12px | 14px | -| lg | 48px | 12px 16px | 16px | - -### States - -| State | Border | Background | Ring | -|-------|--------|------------|------| -| default | gray-300 | white | none | -| hover | gray-400 | white | none | -| focus | primary | white | primary/20% | -| error | red-500 | white | red/20% | -| disabled | gray-200 | gray-100 | none | - -### Anatomy - -``` -Label (optional) -┌─────────────────────────────────────┐ -│ [icon] Placeholder/Value [action] │ -└─────────────────────────────────────┘ -Helper text or error message -``` - ---- - -## Card - -### Variants - -| Variant | Shadow | Border | Use Case | -|---------|--------|--------|----------| -| default | sm | 1px | Standard card | -| elevated | lg | none | Prominent content | -| outline | none | 1px | Subtle container | -| interactive | sm→md | 1px | Clickable card | - -### Anatomy - -``` -┌─────────────────────────────────────┐ -│ Card Header │ -│ Title │ -│ Description │ -├─────────────────────────────────────┤ -│ Card Content │ -│ Main content area │ -│ │ -├─────────────────────────────────────┤ -│ Card Footer │ -│ Actions │ -└─────────────────────────────────────┘ -``` - -### Spacing - -| Area | Padding | -|------|---------| -| header | 24px 24px 0 | -| content | 24px | -| footer | 0 24px 24px | -| gap | 16px | - ---- - -## Badge - -### Variants - -| Variant | Background | Text | -|---------|------------|------| -| default | primary | white | -| secondary | gray-100 | gray-900 | -| outline | transparent | foreground | -| destructive | red-600 | white | -| success | green-600 | white | -| warning | yellow-500 | gray-900 | - -### Sizes - -| Size | Padding | Font Size | Height | -|------|---------|-----------|--------| -| sm | 4px 8px | 11px | 20px | -| default | 4px 10px | 12px | 24px | -| lg | 6px 12px | 14px | 28px | - ---- - -## Alert - -### Variants - -| Variant | Icon | Background | Border | -|---------|------|------------|--------| -| default | info | gray-50 | gray-200 | -| destructive | alert | red-50 | red-200 | -| success | check | green-50 | green-200 | -| warning | warning | yellow-50 | yellow-200 | - -### Anatomy - -``` -┌─────────────────────────────────────┐ -│ [icon] Title [×]│ -│ Description text │ -└─────────────────────────────────────┘ -``` - ---- - -## Dialog - -### Sizes - -| Size | Max Width | Use Case | -|------|-----------|----------| -| sm | 384px | Simple confirmations | -| default | 512px | Standard dialogs | -| lg | 640px | Complex forms | -| xl | 768px | Data-heavy dialogs | -| full | 100% - 32px | Full-screen on mobile | - -### Anatomy - -``` -┌───────────────────────────────────────┐ -│ Dialog Header [×]│ -│ Title │ -│ Description │ -├───────────────────────────────────────┤ -│ Dialog Content │ -│ Scrollable if needed │ -│ │ -├───────────────────────────────────────┤ -│ Dialog Footer │ -│ [Cancel] [Confirm]│ -└───────────────────────────────────────┘ -``` - ---- - -## Table - -### Row States - -| State | Background | Use Case | -|-------|------------|----------| -| default | white | Normal row | -| hover | gray-50 | Mouse over | -| selected | primary/10% | Selected row | -| striped | gray-50/white | Alternating | - -### Cell Alignment - -| Content Type | Alignment | -|--------------|-----------| -| Text | Left | -| Numbers | Right | -| Status/Badge | Center | -| Actions | Right | - -### Spacing - -| Element | Value | -|---------|-------| -| cell padding | 12px 16px | -| header padding | 12px 16px | -| row height (compact) | 40px | -| row height (default) | 48px | -| row height (comfortable) | 56px | diff --git a/.agents/skills/ckm-design-system/references/component-tokens.md b/.agents/skills/ckm-design-system/references/component-tokens.md deleted file mode 100644 index 912ab9d..0000000 --- a/.agents/skills/ckm-design-system/references/component-tokens.md +++ /dev/null @@ -1,214 +0,0 @@ -# Component Tokens - -Component-specific tokens referencing semantic layer. - -## Button Tokens - -```css -:root { - /* Default (Primary) */ - --button-bg: var(--color-primary); - --button-fg: var(--color-primary-foreground); - --button-hover-bg: var(--color-primary-hover); - --button-active-bg: var(--color-primary-active); - - /* Secondary */ - --button-secondary-bg: var(--color-secondary); - --button-secondary-fg: var(--color-secondary-foreground); - --button-secondary-hover-bg: var(--color-secondary-hover); - - /* Outline */ - --button-outline-border: var(--color-border); - --button-outline-fg: var(--color-foreground); - --button-outline-hover-bg: var(--color-accent); - - /* Ghost */ - --button-ghost-fg: var(--color-foreground); - --button-ghost-hover-bg: var(--color-accent); - - /* Destructive */ - --button-destructive-bg: var(--color-destructive); - --button-destructive-fg: var(--color-destructive-foreground); - --button-destructive-hover-bg: var(--color-destructive-hover); - - /* Sizing */ - --button-padding-x: var(--space-4); - --button-padding-y: var(--space-2); - --button-padding-x-sm: var(--space-3); - --button-padding-y-sm: var(--space-1-5); - --button-padding-x-lg: var(--space-6); - --button-padding-y-lg: var(--space-3); - - /* Shape */ - --button-radius: var(--radius-md); - --button-font-size: var(--font-size-sm); - --button-font-weight: var(--font-weight-medium); -} -``` - -## Input Tokens - -```css -:root { - /* Background & Border */ - --input-bg: var(--color-background); - --input-border: var(--color-input); - --input-fg: var(--color-foreground); - - /* Placeholder */ - --input-placeholder: var(--color-muted-foreground); - - /* Focus */ - --input-focus-border: var(--color-ring); - --input-focus-ring: var(--color-ring); - - /* Error */ - --input-error-border: var(--color-error); - --input-error-fg: var(--color-error); - - /* Disabled */ - --input-disabled-bg: var(--color-muted); - --input-disabled-fg: var(--color-muted-foreground); - - /* Sizing */ - --input-padding-x: var(--space-3); - --input-padding-y: var(--space-2); - --input-radius: var(--radius-md); - --input-font-size: var(--font-size-sm); -} -``` - -## Card Tokens - -```css -:root { - /* Background & Border */ - --card-bg: var(--color-card); - --card-fg: var(--color-card-foreground); - --card-border: var(--color-border); - - /* Shadow */ - --card-shadow: var(--shadow-default); - --card-shadow-hover: var(--shadow-md); - - /* Spacing */ - --card-padding: var(--space-6); - --card-padding-sm: var(--space-4); - --card-gap: var(--space-4); - - /* Shape */ - --card-radius: var(--radius-lg); -} -``` - -## Badge Tokens - -```css -:root { - /* Default */ - --badge-bg: var(--color-primary); - --badge-fg: var(--color-primary-foreground); - - /* Secondary */ - --badge-secondary-bg: var(--color-secondary); - --badge-secondary-fg: var(--color-secondary-foreground); - - /* Outline */ - --badge-outline-border: var(--color-border); - --badge-outline-fg: var(--color-foreground); - - /* Destructive */ - --badge-destructive-bg: var(--color-destructive); - --badge-destructive-fg: var(--color-destructive-foreground); - - /* Sizing */ - --badge-padding-x: var(--space-2-5); - --badge-padding-y: var(--space-0-5); - --badge-radius: var(--radius-full); - --badge-font-size: var(--font-size-xs); -} -``` - -## Alert Tokens - -```css -:root { - /* Default */ - --alert-bg: var(--color-background); - --alert-fg: var(--color-foreground); - --alert-border: var(--color-border); - - /* Destructive */ - --alert-destructive-bg: var(--color-destructive); - --alert-destructive-fg: var(--color-destructive-foreground); - - /* Spacing */ - --alert-padding: var(--space-4); - --alert-radius: var(--radius-lg); -} -``` - -## Dialog/Modal Tokens - -```css -:root { - /* Overlay */ - --dialog-overlay-bg: rgb(0 0 0 / 0.5); - - /* Content */ - --dialog-bg: var(--color-background); - --dialog-fg: var(--color-foreground); - --dialog-border: var(--color-border); - --dialog-shadow: var(--shadow-lg); - - /* Spacing */ - --dialog-padding: var(--space-6); - --dialog-radius: var(--radius-lg); - --dialog-max-width: 32rem; -} -``` - -## Table Tokens - -```css -:root { - /* Header */ - --table-header-bg: var(--color-muted); - --table-header-fg: var(--color-muted-foreground); - - /* Body */ - --table-row-bg: var(--color-background); - --table-row-hover-bg: var(--color-muted); - --table-row-fg: var(--color-foreground); - - /* Border */ - --table-border: var(--color-border); - - /* Spacing */ - --table-cell-padding-x: var(--space-4); - --table-cell-padding-y: var(--space-3); -} -``` - -## Usage Example - -```css -.button { - background: var(--button-bg); - color: var(--button-fg); - padding: var(--button-padding-y) var(--button-padding-x); - border-radius: var(--button-radius); - font-size: var(--button-font-size); - font-weight: var(--button-font-weight); - transition: background var(--duration-fast); -} - -.button:hover { - background: var(--button-hover-bg); -} - -.button.secondary { - background: var(--button-secondary-bg); - color: var(--button-secondary-fg); -} -``` diff --git a/.agents/skills/ckm-design-system/references/primitive-tokens.md b/.agents/skills/ckm-design-system/references/primitive-tokens.md deleted file mode 100644 index eb251a2..0000000 --- a/.agents/skills/ckm-design-system/references/primitive-tokens.md +++ /dev/null @@ -1,203 +0,0 @@ -# Primitive Tokens - -Raw design values - foundation of the design system. - -## Color Scales - -### Gray Scale - -```css -:root { - --color-gray-50: #F9FAFB; - --color-gray-100: #F3F4F6; - --color-gray-200: #E5E7EB; - --color-gray-300: #D1D5DB; - --color-gray-400: #9CA3AF; - --color-gray-500: #6B7280; - --color-gray-600: #4B5563; - --color-gray-700: #374151; - --color-gray-800: #1F2937; - --color-gray-900: #111827; - --color-gray-950: #030712; -} -``` - -### Primary Colors (Blue) - -```css -:root { - --color-blue-50: #EFF6FF; - --color-blue-100: #DBEAFE; - --color-blue-200: #BFDBFE; - --color-blue-300: #93C5FD; - --color-blue-400: #60A5FA; - --color-blue-500: #3B82F6; - --color-blue-600: #2563EB; - --color-blue-700: #1D4ED8; - --color-blue-800: #1E40AF; - --color-blue-900: #1E3A8A; -} -``` - -### Status Colors - -```css -:root { - /* Success - Green */ - --color-green-500: #22C55E; - --color-green-600: #16A34A; - - /* Warning - Yellow */ - --color-yellow-500: #EAB308; - --color-yellow-600: #CA8A04; - - /* Error - Red */ - --color-red-500: #EF4444; - --color-red-600: #DC2626; - - /* Info - Blue */ - --color-info: var(--color-blue-500); -} -``` - -## Spacing Scale - -4px base unit system. - -```css -:root { - --space-0: 0; - --space-px: 1px; - --space-0-5: 0.125rem; /* 2px */ - --space-1: 0.25rem; /* 4px */ - --space-1-5: 0.375rem; /* 6px */ - --space-2: 0.5rem; /* 8px */ - --space-2-5: 0.625rem; /* 10px */ - --space-3: 0.75rem; /* 12px */ - --space-3-5: 0.875rem; /* 14px */ - --space-4: 1rem; /* 16px */ - --space-5: 1.25rem; /* 20px */ - --space-6: 1.5rem; /* 24px */ - --space-7: 1.75rem; /* 28px */ - --space-8: 2rem; /* 32px */ - --space-9: 2.25rem; /* 36px */ - --space-10: 2.5rem; /* 40px */ - --space-12: 3rem; /* 48px */ - --space-14: 3.5rem; /* 56px */ - --space-16: 4rem; /* 64px */ - --space-20: 5rem; /* 80px */ - --space-24: 6rem; /* 96px */ -} -``` - -## Typography Scale - -```css -:root { - /* Font Sizes */ - --font-size-xs: 0.75rem; /* 12px */ - --font-size-sm: 0.875rem; /* 14px */ - --font-size-base: 1rem; /* 16px */ - --font-size-lg: 1.125rem; /* 18px */ - --font-size-xl: 1.25rem; /* 20px */ - --font-size-2xl: 1.5rem; /* 24px */ - --font-size-3xl: 1.875rem; /* 30px */ - --font-size-4xl: 2.25rem; /* 36px */ - --font-size-5xl: 3rem; /* 48px */ - - /* Line Heights */ - --leading-none: 1; - --leading-tight: 1.25; - --leading-snug: 1.375; - --leading-normal: 1.5; - --leading-relaxed: 1.625; - --leading-loose: 2; - - /* Font Weights */ - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - - /* Letter Spacing */ - --tracking-tighter: -0.05em; - --tracking-tight: -0.025em; - --tracking-normal: 0; - --tracking-wide: 0.025em; - --tracking-wider: 0.05em; -} -``` - -## Border Radius - -```css -:root { - --radius-none: 0; - --radius-sm: 0.125rem; /* 2px */ - --radius-default: 0.25rem; /* 4px */ - --radius-md: 0.375rem; /* 6px */ - --radius-lg: 0.5rem; /* 8px */ - --radius-xl: 0.75rem; /* 12px */ - --radius-2xl: 1rem; /* 16px */ - --radius-3xl: 1.5rem; /* 24px */ - --radius-full: 9999px; -} -``` - -## Shadows - -```css -:root { - --shadow-none: none; - --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --shadow-default: 0 1px 3px 0 rgb(0 0 0 / 0.1), - 0 1px 2px -1px rgb(0 0 0 / 0.1); - --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), - 0 2px 4px -2px rgb(0 0 0 / 0.1); - --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), - 0 4px 6px -4px rgb(0 0 0 / 0.1); - --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), - 0 8px 10px -6px rgb(0 0 0 / 0.1); - --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); - --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); -} -``` - -## Motion / Duration - -```css -:root { - --duration-75: 75ms; - --duration-100: 100ms; - --duration-150: 150ms; - --duration-200: 200ms; - --duration-300: 300ms; - --duration-500: 500ms; - --duration-700: 700ms; - --duration-1000: 1000ms; - - /* Semantic durations */ - --duration-fast: var(--duration-150); - --duration-normal: var(--duration-200); - --duration-slow: var(--duration-300); -} -``` - -## Z-Index Scale - -```css -:root { - --z-auto: auto; - --z-0: 0; - --z-10: 10; - --z-20: 20; - --z-30: 30; - --z-40: 40; - --z-50: 50; - --z-dropdown: 1000; - --z-sticky: 1100; - --z-modal: 1200; - --z-popover: 1300; - --z-tooltip: 1400; -} -``` diff --git a/.agents/skills/ckm-design-system/references/semantic-tokens.md b/.agents/skills/ckm-design-system/references/semantic-tokens.md deleted file mode 100644 index b441010..0000000 --- a/.agents/skills/ckm-design-system/references/semantic-tokens.md +++ /dev/null @@ -1,215 +0,0 @@ -# Semantic Tokens - -Purpose-based aliases referencing primitive tokens. - -## Color Semantics - -### Background & Foreground - -```css -:root { - /* Page background */ - --color-background: var(--color-gray-50); - --color-foreground: var(--color-gray-900); - - /* Card/surface background */ - --color-card: white; - --color-card-foreground: var(--color-gray-900); - - /* Popover/dropdown */ - --color-popover: white; - --color-popover-foreground: var(--color-gray-900); -} -``` - -### Primary - -```css -:root { - --color-primary: var(--color-blue-600); - --color-primary-hover: var(--color-blue-700); - --color-primary-active: var(--color-blue-800); - --color-primary-foreground: white; -} -``` - -### Secondary - -```css -:root { - --color-secondary: var(--color-gray-100); - --color-secondary-hover: var(--color-gray-200); - --color-secondary-foreground: var(--color-gray-900); -} -``` - -### Muted - -```css -:root { - --color-muted: var(--color-gray-100); - --color-muted-foreground: var(--color-gray-500); -} -``` - -### Accent - -```css -:root { - --color-accent: var(--color-gray-100); - --color-accent-foreground: var(--color-gray-900); -} -``` - -### Destructive - -```css -:root { - --color-destructive: var(--color-red-600); - --color-destructive-hover: var(--color-red-700); - --color-destructive-foreground: white; -} -``` - -### Status Colors - -```css -:root { - --color-success: var(--color-green-600); - --color-success-foreground: white; - - --color-warning: var(--color-yellow-500); - --color-warning-foreground: var(--color-gray-900); - - --color-error: var(--color-red-600); - --color-error-foreground: white; - - --color-info: var(--color-blue-500); - --color-info-foreground: white; -} -``` - -### Border & Ring - -```css -:root { - --color-border: var(--color-gray-200); - --color-input: var(--color-gray-200); - --color-ring: var(--color-blue-500); -} -``` - -## Spacing Semantics - -```css -:root { - /* Component internal spacing */ - --spacing-component-xs: var(--space-1); - --spacing-component-sm: var(--space-2); - --spacing-component: var(--space-3); - --spacing-component-lg: var(--space-4); - - /* Section spacing */ - --spacing-section-sm: var(--space-8); - --spacing-section: var(--space-12); - --spacing-section-lg: var(--space-16); - - /* Page margins */ - --spacing-page-x: var(--space-4); - --spacing-page-y: var(--space-6); -} -``` - -## Typography Semantics - -```css -:root { - /* Headings */ - --font-heading: var(--font-size-2xl); - --font-heading-lg: var(--font-size-3xl); - --font-heading-xl: var(--font-size-4xl); - - /* Body */ - --font-body: var(--font-size-base); - --font-body-sm: var(--font-size-sm); - --font-body-lg: var(--font-size-lg); - - /* Labels & Captions */ - --font-label: var(--font-size-sm); - --font-caption: var(--font-size-xs); -} -``` - -## Interactive States - -```css -:root { - /* Focus ring */ - --ring-width: 2px; - --ring-offset: 2px; - --ring-color: var(--color-ring); - - /* Opacity for disabled */ - --opacity-disabled: 0.5; - - /* Transitions */ - --transition-colors: color, background-color, border-color; - --transition-transform: transform; - --transition-all: all; -} -``` - -## Dark Mode Overrides - -```css -.dark { - --color-background: var(--color-gray-950); - --color-foreground: var(--color-gray-50); - - --color-card: var(--color-gray-900); - --color-card-foreground: var(--color-gray-50); - - --color-popover: var(--color-gray-900); - --color-popover-foreground: var(--color-gray-50); - - --color-muted: var(--color-gray-800); - --color-muted-foreground: var(--color-gray-400); - - --color-secondary: var(--color-gray-800); - --color-secondary-foreground: var(--color-gray-50); - - --color-accent: var(--color-gray-800); - --color-accent-foreground: var(--color-gray-50); - - --color-border: var(--color-gray-800); - --color-input: var(--color-gray-800); -} -``` - -## Usage Patterns - -### Applying Semantic Tokens - -```css -/* Good - uses semantic tokens */ -.card { - background: var(--color-card); - color: var(--color-card-foreground); - border: 1px solid var(--color-border); -} - -/* Bad - uses primitive tokens directly */ -.card { - background: var(--color-gray-50); - color: var(--color-gray-900); -} -``` - -### Theme Switching - -Semantic tokens enable instant theme switching: - -```js -// Toggle dark mode -document.documentElement.classList.toggle('dark'); -``` diff --git a/.agents/skills/ckm-design-system/references/states-and-variants.md b/.agents/skills/ckm-design-system/references/states-and-variants.md deleted file mode 100644 index 64b808a..0000000 --- a/.agents/skills/ckm-design-system/references/states-and-variants.md +++ /dev/null @@ -1,241 +0,0 @@ -# States and Variants - -Component state definitions and variant patterns. - -## Interactive States - -### State Definitions - -| State | Trigger | Visual Change | -|-------|---------|---------------| -| default | None | Base appearance | -| hover | Mouse over | Slight color shift | -| focus | Tab/click | Focus ring | -| active | Mouse down | Darkest color | -| disabled | disabled attr | Reduced opacity | -| loading | Async action | Spinner + opacity | - -### State Priority - -When multiple states apply, priority (highest to lowest): - -1. disabled -2. loading -3. active -4. focus -5. hover -6. default - -### State Transitions - -```css -/* Standard transition for interactive elements */ -.interactive { - transition-property: color, background-color, border-color, box-shadow; - transition-duration: var(--duration-fast); - transition-timing-function: ease-in-out; -} -``` - -| Transition | Duration | Easing | -|------------|----------|--------| -| Color changes | 150ms | ease-in-out | -| Background | 150ms | ease-in-out | -| Transform | 200ms | ease-out | -| Opacity | 150ms | ease | -| Shadow | 200ms | ease-out | - -## Focus States - -### Focus Ring Spec - -```css -/* Standard focus ring */ -.focusable:focus-visible { - outline: none; - box-shadow: 0 0 0 var(--ring-offset) var(--color-background), - 0 0 0 calc(var(--ring-offset) + var(--ring-width)) var(--ring-color); -} -``` - -| Property | Value | -|----------|-------| -| Ring width | 2px | -| Ring offset | 2px | -| Ring color | primary (blue-500) | -| Offset color | background | - -### Focus Within - -```css -/* Container focus when child is focused */ -.container:focus-within { - border-color: var(--color-ring); -} -``` - -## Disabled States - -### Visual Treatment - -```css -.disabled { - opacity: var(--opacity-disabled); /* 0.5 */ - pointer-events: none; - cursor: not-allowed; -} -``` - -| Property | Disabled Value | -|----------|----------------| -| Opacity | 50% | -| Pointer events | none | -| Cursor | not-allowed | -| Background | muted | -| Color | muted-foreground | - -### Accessibility - -- Use `aria-disabled="true"` for semantic disabled -- Use `disabled` attribute for form elements -- Maintain sufficient contrast (3:1 minimum) - -## Loading States - -### Spinner Placement - -| Component | Spinner Position | -|-----------|------------------| -| Button | Replace icon or center | -| Input | Trailing position | -| Card | Center overlay | -| Page | Center of viewport | - -### Loading Treatment - -```css -.loading { - position: relative; - pointer-events: none; -} - -.loading::after { - content: ''; - /* spinner styles */ -} - -.loading > * { - opacity: 0.7; -} -``` - -## Error States - -### Visual Indicators - -```css -.error { - border-color: var(--color-error); - color: var(--color-error); -} - -.error:focus-visible { - box-shadow: 0 0 0 2px var(--color-background), - 0 0 0 4px var(--color-error); -} -``` - -| Element | Error Treatment | -|---------|-----------------| -| Input border | red-500 | -| Input focus ring | red/20% | -| Helper text | red-600 | -| Icon | red-500 | - -### Error Messages - -- Position below input -- Use error color -- Include icon for accessibility -- Clear on valid input - -## Variant Patterns - -### Color Variants - -```css -/* Pattern for color variants */ -.component { - --component-bg: var(--color-primary); - --component-fg: var(--color-primary-foreground); - background: var(--component-bg); - color: var(--component-fg); -} - -.component.secondary { - --component-bg: var(--color-secondary); - --component-fg: var(--color-secondary-foreground); -} - -.component.destructive { - --component-bg: var(--color-destructive); - --component-fg: var(--color-destructive-foreground); -} -``` - -### Size Variants - -```css -/* Pattern for size variants */ -.component { - --component-height: 40px; - --component-padding: var(--space-4); - --component-font: var(--font-size-sm); -} - -.component.sm { - --component-height: 32px; - --component-padding: var(--space-3); - --component-font: var(--font-size-xs); -} - -.component.lg { - --component-height: 48px; - --component-padding: var(--space-6); - --component-font: var(--font-size-base); -} -``` - -## Accessibility Requirements - -### Color Contrast - -| Element | Minimum Ratio | -|---------|---------------| -| Normal text | 4.5:1 | -| Large text (18px+) | 3:1 | -| UI components | 3:1 | -| Focus indicator | 3:1 | - -### State Indicators - -- Never rely on color alone -- Use icons, text, or patterns -- Ensure focus is visible -- Provide loading announcements - -### ARIA States - -```html - - - - - - - - -Error message -``` diff --git a/.agents/skills/ckm-design-system/references/tailwind-integration.md b/.agents/skills/ckm-design-system/references/tailwind-integration.md deleted file mode 100644 index c632788..0000000 --- a/.agents/skills/ckm-design-system/references/tailwind-integration.md +++ /dev/null @@ -1,251 +0,0 @@ -# Tailwind Integration - -Map design system tokens to Tailwind CSS configuration. - -## CSS Variables Setup - -### Base Layer - -```css -/* globals.css */ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - /* Primitives */ - --color-blue-600: 37 99 235; /* HSL: 217 91% 60% */ - - /* Semantic */ - --background: 0 0% 100%; - --foreground: 222 47% 11%; - --primary: 217 91% 60%; - --primary-foreground: 0 0% 100%; - --secondary: 220 14% 96%; - --secondary-foreground: 222 47% 11%; - --muted: 220 14% 96%; - --muted-foreground: 220 9% 46%; - --accent: 220 14% 96%; - --accent-foreground: 222 47% 11%; - --destructive: 0 84% 60%; - --destructive-foreground: 0 0% 100%; - --border: 220 13% 91%; - --input: 220 13% 91%; - --ring: 217 91% 60%; - --radius: 0.5rem; - } - - .dark { - --background: 222 47% 4%; - --foreground: 210 40% 98%; - --primary: 217 91% 60%; - --primary-foreground: 0 0% 100%; - --secondary: 217 33% 17%; - --secondary-foreground: 210 40% 98%; - --muted: 217 33% 17%; - --muted-foreground: 215 20% 65%; - --accent: 217 33% 17%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62% 30%; - --destructive-foreground: 0 0% 100%; - --border: 217 33% 17%; - --input: 217 33% 17%; - --ring: 217 91% 60%; - } -} -``` - -## Tailwind Config - -### tailwind.config.ts - -```typescript -import type { Config } from 'tailwindcss' - -const config: Config = { - darkMode: ['class'], - content: ['./src/**/*.{ts,tsx}'], - theme: { - extend: { - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - }, - }, - plugins: [], -} - -export default config -``` - -## HSL Format Benefits - -Using HSL without function allows opacity modifiers: - -```tsx -// With HSL format (space-separated) -

// 50% opacity -
// 80% opacity - -// CSS output -background-color: hsl(217 91% 60% / 0.5); -``` - -## Component Classes - -### Button Example - -```css -@layer components { - .btn { - @apply inline-flex items-center justify-center - rounded-md font-medium - transition-colors - focus-visible:outline-none focus-visible:ring-2 - focus-visible:ring-ring focus-visible:ring-offset-2 - disabled:pointer-events-none disabled:opacity-50; - } - - .btn-default { - @apply bg-primary text-primary-foreground - hover:bg-primary/90; - } - - .btn-secondary { - @apply bg-secondary text-secondary-foreground - hover:bg-secondary/80; - } - - .btn-outline { - @apply border border-input bg-background - hover:bg-accent hover:text-accent-foreground; - } - - .btn-ghost { - @apply hover:bg-accent hover:text-accent-foreground; - } - - .btn-destructive { - @apply bg-destructive text-destructive-foreground - hover:bg-destructive/90; - } - - /* Sizes */ - .btn-sm { @apply h-8 px-3 text-xs; } - .btn-md { @apply h-10 px-4 text-sm; } - .btn-lg { @apply h-12 px-6 text-base; } -} -``` - -## Spacing Integration - -```typescript -// tailwind.config.ts -theme: { - extend: { - spacing: { - // Map to CSS variables if needed - 'section': 'var(--spacing-section)', - 'component': 'var(--spacing-component)', - } - } -} -``` - -## Animation Tokens - -```typescript -// tailwind.config.ts -theme: { - extend: { - transitionDuration: { - fast: '150ms', - normal: '200ms', - slow: '300ms', - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - }, - } -} -``` - -## Dark Mode Toggle - -```typescript -// Toggle dark mode -function toggleDarkMode() { - document.documentElement.classList.toggle('dark') -} - -// System preference -if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.classList.add('dark') -} -``` - -## shadcn/ui Alignment - -This configuration aligns with shadcn/ui conventions: - -- Same CSS variable naming -- Same HSL format -- Same color scale structure -- Compatible with `npx shadcn@latest add` commands - -### Using with shadcn/ui - -```bash -# Initialize (uses same token structure) -npx shadcn@latest init - -# Add components (styled with these tokens) -npx shadcn@latest add button card input -``` - -Components will automatically use your design system tokens. diff --git a/.agents/skills/ckm-design-system/references/token-architecture.md b/.agents/skills/ckm-design-system/references/token-architecture.md deleted file mode 100644 index e13ed2b..0000000 --- a/.agents/skills/ckm-design-system/references/token-architecture.md +++ /dev/null @@ -1,224 +0,0 @@ -# Token Architecture - -Three-layer token system for scalable, themeable design systems. - -## Layer Overview - -``` -┌─────────────────────────────────────────┐ -│ Component Tokens │ Per-component overrides -│ --button-bg, --card-padding │ -├─────────────────────────────────────────┤ -│ Semantic Tokens │ Purpose-based aliases -│ --color-primary, --spacing-section │ -├─────────────────────────────────────────┤ -│ Primitive Tokens │ Raw design values -│ --color-blue-600, --space-4 │ -└─────────────────────────────────────────┘ -``` - -## Why Three Layers? - -| Layer | Purpose | When to Change | -|-------|---------|----------------| -| Primitive | Base values (colors, sizes) | Rarely - foundational | -| Semantic | Meaning assignment | Theme switching | -| Component | Component customization | Per-component needs | - -## Layer 1: Primitive Tokens - -Raw design values without semantic meaning. - -```css -:root { - /* Colors */ - --color-gray-50: #F9FAFB; - --color-gray-900: #111827; - --color-blue-500: #3B82F6; - --color-blue-600: #2563EB; - - /* Spacing (4px base) */ - --space-1: 0.25rem; /* 4px */ - --space-2: 0.5rem; /* 8px */ - --space-4: 1rem; /* 16px */ - --space-6: 1.5rem; /* 24px */ - - /* Typography */ - --font-size-sm: 0.875rem; - --font-size-base: 1rem; - --font-size-lg: 1.125rem; - - /* Radius */ - --radius-sm: 0.25rem; - --radius-default: 0.5rem; - --radius-lg: 0.75rem; - - /* Shadows */ - --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05); - --shadow-default: 0 1px 3px rgb(0 0 0 / 0.1); -} -``` - -## Layer 2: Semantic Tokens - -Purpose-based aliases that reference primitives. - -```css -:root { - /* Background */ - --color-background: var(--color-gray-50); - --color-foreground: var(--color-gray-900); - - /* Primary */ - --color-primary: var(--color-blue-600); - --color-primary-hover: var(--color-blue-700); - - /* Secondary */ - --color-secondary: var(--color-gray-100); - --color-secondary-foreground: var(--color-gray-900); - - /* Muted */ - --color-muted: var(--color-gray-100); - --color-muted-foreground: var(--color-gray-500); - - /* Destructive */ - --color-destructive: var(--color-red-600); - --color-destructive-foreground: white; - - /* Spacing */ - --spacing-component: var(--space-4); - --spacing-section: var(--space-6); -} -``` - -## Layer 3: Component Tokens - -Component-specific tokens referencing semantic layer. - -```css -:root { - /* Button */ - --button-bg: var(--color-primary); - --button-fg: white; - --button-hover-bg: var(--color-primary-hover); - --button-padding-x: var(--space-4); - --button-padding-y: var(--space-2); - --button-radius: var(--radius-default); - - /* Input */ - --input-bg: var(--color-background); - --input-border: var(--color-gray-300); - --input-focus-ring: var(--color-primary); - --input-padding: var(--space-2) var(--space-3); - - /* Card */ - --card-bg: var(--color-background); - --card-border: var(--color-gray-200); - --card-padding: var(--space-4); - --card-radius: var(--radius-lg); - --card-shadow: var(--shadow-default); -} -``` - -## Dark Mode - -Override semantic tokens for dark theme: - -```css -.dark { - --color-background: var(--color-gray-900); - --color-foreground: var(--color-gray-50); - --color-muted: var(--color-gray-800); - --color-muted-foreground: var(--color-gray-400); - --color-secondary: var(--color-gray-800); -} -``` - -## Naming Convention - -``` ---{category}-{item}-{variant}-{state} - -Examples: ---color-primary # category-item ---color-primary-hover # category-item-state ---button-bg-hover # component-property-state ---space-section-sm # category-semantic-variant -``` - -## Categories - -| Category | Examples | -|----------|----------| -| color | primary, secondary, muted, destructive | -| space | 1, 2, 4, 8, section, component | -| font-size | xs, sm, base, lg, xl | -| radius | sm, default, lg, full | -| shadow | sm, default, lg | -| duration | fast, normal, slow | - -## File Organization - -``` -tokens/ -├── primitives.css # Raw values -├── semantic.css # Purpose aliases -├── components.css # Component tokens -└── index.css # Imports all -``` - -Or single file with layer comments: - -```css -/* === PRIMITIVES === */ -:root { ... } - -/* === SEMANTIC === */ -:root { ... } - -/* === COMPONENTS === */ -:root { ... } - -/* === DARK MODE === */ -.dark { ... } -``` - -## Migration from Flat Tokens - -Before (flat): -```css ---button-primary-bg: #2563EB; ---button-secondary-bg: #F3F4F6; -``` - -After (three-layer): -```css -/* Primitive */ ---color-blue-600: #2563EB; ---color-gray-100: #F3F4F6; - -/* Semantic */ ---color-primary: var(--color-blue-600); ---color-secondary: var(--color-gray-100); - -/* Component */ ---button-bg: var(--color-primary); ---button-secondary-bg: var(--color-secondary); -``` - -## W3C DTCG Alignment - -Token JSON format (W3C Design Tokens Community Group): - -```json -{ - "color": { - "blue": { - "600": { - "$value": "#2563EB", - "$type": "color" - } - } - } -} -``` diff --git a/.agents/skills/ckm-design-system/scripts/embed-tokens.cjs b/.agents/skills/ckm-design-system/scripts/embed-tokens.cjs deleted file mode 100644 index 419c104..0000000 --- a/.agents/skills/ckm-design-system/scripts/embed-tokens.cjs +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env node -/** - * embed-tokens.cjs - * Reads design-tokens.css and outputs embeddable inline CSS. - * Use when generating standalone HTML files (infographics, slides, etc.) - * - * Usage: - * node embed-tokens.cjs # Output full CSS - * node embed-tokens.cjs --minimal # Output only commonly used tokens - * node embed-tokens.cjs --style # Wrap in `; - } else { - output = `/* Design Tokens (embedded for standalone HTML) */\n${output}`; - } - - console.log(output); -} catch (err) { - console.error(`Error reading tokens: ${err.message}`); - process.exit(1); -} diff --git a/.agents/skills/ckm-design-system/scripts/fetch-background.py b/.agents/skills/ckm-design-system/scripts/fetch-background.py deleted file mode 100644 index bcbd357..0000000 --- a/.agents/skills/ckm-design-system/scripts/fetch-background.py +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/bin/env python3 -""" -Background Image Fetcher -Fetches real images from Pexels for slide backgrounds. -Uses web scraping (no API key required) or WebFetch tool integration. -""" - -import json -import csv -import re -import sys -from pathlib import Path - -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent -TOKENS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' -BACKGROUNDS_CSV = Path(__file__).parent.parent / 'data' / 'slide-backgrounds.csv' - - -def resolve_token_reference(ref: str, tokens: dict) -> str: - """Resolve token reference like {primitive.color.ocean-blue.500} to hex value.""" - if not ref or not ref.startswith('{') or not ref.endswith('}'): - return ref # Already a value, not a reference - - # Parse reference: {primitive.color.ocean-blue.500} - path = ref[1:-1].split('.') # ['primitive', 'color', 'ocean-blue', '500'] - current = tokens - for key in path: - if isinstance(current, dict): - current = current.get(key) - else: - return None # Invalid path - # Return $value if it's a token object - if isinstance(current, dict) and '$value' in current: - return current['$value'] - return current - - -def load_brand_colors(): - """Load colors from assets/design-tokens.json for overlay gradients. - - Resolves semantic token references to actual hex values. - """ - try: - with open(TOKENS_PATH) as f: - tokens = json.load(f) - - colors = tokens.get('primitive', {}).get('color', {}) - semantic = tokens.get('semantic', {}).get('color', {}) - - # Try semantic tokens first (preferred) - resolve references - if semantic: - primary_ref = semantic.get('primary', {}).get('$value') - secondary_ref = semantic.get('secondary', {}).get('$value') - accent_ref = semantic.get('accent', {}).get('$value') - background_ref = semantic.get('background', {}).get('$value') - - primary = resolve_token_reference(primary_ref, tokens) - secondary = resolve_token_reference(secondary_ref, tokens) - accent = resolve_token_reference(accent_ref, tokens) - background = resolve_token_reference(background_ref, tokens) - - if primary and secondary: - return { - 'primary': primary, - 'secondary': secondary, - 'accent': accent or primary, - 'background': background or '#0D0D0D', - } - - # Fallback: find first color palette with 500 value (primary) - primary_keys = ['ocean-blue', 'coral', 'blue', 'primary'] - secondary_keys = ['golden-amber', 'purple', 'amber', 'secondary'] - accent_keys = ['emerald', 'mint', 'green', 'accent'] - - primary_color = None - secondary_color = None - accent_color = None - - for key in primary_keys: - if key in colors and isinstance(colors[key], dict): - primary_color = colors[key].get('500', {}).get('$value') - if primary_color: - break - - for key in secondary_keys: - if key in colors and isinstance(colors[key], dict): - secondary_color = colors[key].get('500', {}).get('$value') - if secondary_color: - break - - for key in accent_keys: - if key in colors and isinstance(colors[key], dict): - accent_color = colors[key].get('500', {}).get('$value') - if accent_color: - break - - background = colors.get('dark', {}).get('800', {}).get('$value', '#0D0D0D') - - return { - 'primary': primary_color or '#3B82F6', - 'secondary': secondary_color or '#F59E0B', - 'accent': accent_color or '#10B981', - 'background': background, - } - except (FileNotFoundError, KeyError, TypeError): - # Fallback defaults - return { - 'primary': '#3B82F6', - 'secondary': '#F59E0B', - 'accent': '#10B981', - 'background': '#0D0D0D', - } - - -def load_backgrounds_config(): - """Load background configuration from CSV.""" - config = {} - try: - with open(BACKGROUNDS_CSV, newline='') as f: - reader = csv.DictReader(f) - for row in reader: - config[row['slide_type']] = row - except FileNotFoundError: - print(f"Warning: {BACKGROUNDS_CSV} not found") - return config - - -def get_overlay_css(style: str, brand_colors: dict) -> str: - """Generate overlay CSS using brand colors from design-tokens.json.""" - overlays = { - 'gradient-dark': f"linear-gradient(135deg, {brand_colors['background']}E6, {brand_colors['background']}B3)", - 'gradient-brand': f"linear-gradient(135deg, {brand_colors['primary']}CC, {brand_colors['secondary']}99)", - 'gradient-accent': f"linear-gradient(135deg, {brand_colors['accent']}99, transparent)", - 'blur-dark': f"rgba(13,13,13,0.8)", - 'desaturate-dark': f"rgba(13,13,13,0.7)", - } - return overlays.get(style, overlays['gradient-dark']) - - -# Curated high-quality images from Pexels (free to use, pre-selected for brand aesthetic) -CURATED_IMAGES = { - 'hero': [ - 'https://images.pexels.com/photos/3861969/pexels-photo-3861969.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/2582937/pexels-photo-2582937.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'vision': [ - 'https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3182812/pexels-photo-3182812.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'team': [ - 'https://images.pexels.com/photos/3184418/pexels-photo-3184418.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3184338/pexels-photo-3184338.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3182773/pexels-photo-3182773.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'testimonial': [ - 'https://images.pexels.com/photos/3184465/pexels-photo-3184465.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/1181622/pexels-photo-1181622.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'cta': [ - 'https://images.pexels.com/photos/3184339/pexels-photo-3184339.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3184298/pexels-photo-3184298.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'problem': [ - 'https://images.pexels.com/photos/3760529/pexels-photo-3760529.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/897817/pexels-photo-897817.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'solution': [ - 'https://images.pexels.com/photos/3184292/pexels-photo-3184292.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3184644/pexels-photo-3184644.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'hook': [ - 'https://images.pexels.com/photos/2582937/pexels-photo-2582937.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'social': [ - 'https://images.pexels.com/photos/3184360/pexels-photo-3184360.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3184287/pexels-photo-3184287.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], - 'demo': [ - 'https://images.pexels.com/photos/1181675/pexels-photo-1181675.jpeg?auto=compress&cs=tinysrgb&w=1920', - 'https://images.pexels.com/photos/3861958/pexels-photo-3861958.jpeg?auto=compress&cs=tinysrgb&w=1920', - ], -} - - -def get_curated_images(slide_type: str) -> list: - """Get curated images for slide type.""" - return CURATED_IMAGES.get(slide_type, CURATED_IMAGES.get('hero', [])) - - -def get_pexels_search_url(keywords: str) -> str: - """Generate Pexels search URL for manual lookup.""" - import urllib.parse - return f"https://www.pexels.com/search/{urllib.parse.quote(keywords)}/" - - -def get_background_image(slide_type: str) -> dict: - """ - Get curated image matching slide type and brand aesthetic. - Uses pre-selected Pexels images (no API/scraping needed). - """ - brand_colors = load_brand_colors() - config = load_backgrounds_config() - - slide_config = config.get(slide_type) - overlay_style = 'gradient-dark' - keywords = slide_type - - if slide_config: - keywords = slide_config.get('search_keywords', slide_config.get('image_category', slide_type)) - overlay_style = slide_config.get('overlay_style', 'gradient-dark') - - # Get curated images - urls = get_curated_images(slide_type) - if urls: - return { - 'url': urls[0], - 'all_urls': urls, - 'overlay': get_overlay_css(overlay_style, brand_colors), - 'attribution': 'Photo from Pexels (free to use)', - 'source': 'pexels-curated', - 'search_url': get_pexels_search_url(keywords), - } - - # Fallback: provide search URL for manual selection - return { - 'url': None, - 'overlay': get_overlay_css(overlay_style, brand_colors), - 'keywords': keywords, - 'search_url': get_pexels_search_url(keywords), - 'available_types': list(CURATED_IMAGES.keys()), - } - - -def generate_css_for_background(result: dict, slide_class: str = '.slide-with-bg') -> str: - """Generate CSS for a background slide.""" - if not result.get('url'): - search_url = result.get('search_url', '') - return f"""/* No image scraped. Search manually: {search_url} */ -/* Overlay ready: {result.get('overlay', 'gradient-dark')} */ -""" - - return f"""{slide_class} {{ - background-image: url('{result['url']}'); - background-size: cover; - background-position: center; - position: relative; -}} - -{slide_class}::before {{ - content: ''; - position: absolute; - inset: 0; - background: {result['overlay']}; -}} - -{slide_class} .content {{ - position: relative; - z-index: 1; -}} - -/* {result.get('attribution', 'Pexels')} - {result.get('search_url', '')} */ -""" - - -def main(): - """CLI entry point.""" - import argparse - - parser = argparse.ArgumentParser(description='Get background images for slides') - parser.add_argument('slide_type', nargs='?', help='Slide type (hero, vision, team, etc.)') - parser.add_argument('--list', action='store_true', help='List available slide types') - parser.add_argument('--css', action='store_true', help='Output CSS for the background') - parser.add_argument('--json', action='store_true', help='Output JSON') - parser.add_argument('--colors', action='store_true', help='Show brand colors') - parser.add_argument('--all', action='store_true', help='Show all curated URLs') - - args = parser.parse_args() - - if args.colors: - colors = load_brand_colors() - print("\nBrand Colors (from design-tokens.json):") - for name, value in colors.items(): - print(f" {name}: {value}") - return - - if args.list: - print("\nAvailable slide types (curated images):") - for slide_type, urls in CURATED_IMAGES.items(): - print(f" {slide_type}: {len(urls)} images") - return - - if not args.slide_type: - parser.print_help() - return - - result = get_background_image(args.slide_type) - - if args.json: - print(json.dumps(result, indent=2)) - elif args.css: - print(generate_css_for_background(result)) - elif args.all: - print(f"\nAll images for '{args.slide_type}':") - for i, url in enumerate(result.get('all_urls', []), 1): - print(f" {i}. {url}") - else: - print(f"\nImage URL: {result['url']}") - print(f"Alternatives: {len(result.get('all_urls', []))} available (use --all)") - print(f"Overlay: {result['overlay']}") - - -if __name__ == '__main__': - main() diff --git a/.agents/skills/ckm-design-system/scripts/generate-slide.py b/.agents/skills/ckm-design-system/scripts/generate-slide.py deleted file mode 100644 index 228a50a..0000000 --- a/.agents/skills/ckm-design-system/scripts/generate-slide.py +++ /dev/null @@ -1,753 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Slide Generator - Generates HTML slides using design tokens -ALL styles MUST use CSS variables from design-tokens.css -NO hardcoded colors, fonts, or spacing allowed -""" - -import argparse -import json -from pathlib import Path -from datetime import datetime - -# Paths -SCRIPT_DIR = Path(__file__).parent -DATA_DIR = SCRIPT_DIR.parent / "data" -TOKENS_CSS = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.css" -TOKENS_JSON = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.json" -OUTPUT_DIR = Path(__file__).resolve().parents[4] / "assets" / "designs" / "slides" - -# ============ BRAND-COMPLIANT SLIDE TEMPLATE ============ -# ALL values reference CSS variables from design-tokens.css - -SLIDE_TEMPLATE = ''' - - - - - {title} - - - - - - - - - - - -
- {slides_content} -
- - -''' - - -# ============ SLIDE GENERATORS ============ - -def generate_title_slide(data): - """Title slide with gradient headline""" - return f''' -
-
{data.get('badge', 'Pitch Deck')}
-

{data.get('title', 'Your Title Here')}

-

{data.get('subtitle', 'Your compelling subtitle')}

-
- -
- ''' - - -def generate_problem_slide(data): - """Problem statement slide using PAS formula""" - return f''' -
-
The Problem
-

{data.get('headline', 'The problem your audience faces')}

-
-
-
01
-

{data.get('pain_1_title', 'Pain Point 1')}

-

{data.get('pain_1_desc', 'Description of the first pain point')}

-
-
-
02
-

{data.get('pain_2_title', 'Pain Point 2')}

-

{data.get('pain_2_desc', 'Description of the second pain point')}

-
-
-
03
-

{data.get('pain_3_title', 'Pain Point 3')}

-

{data.get('pain_3_desc', 'Description of the third pain point')}

-
-
- -
- ''' - - -def generate_solution_slide(data): - """Solution slide with feature highlights""" - return f''' -
-
The Solution
-

{data.get('headline', 'How we solve this')}

-
-
-
-
-
-

{data.get('feature_1_title', 'Feature 1')}

-

{data.get('feature_1_desc', 'Description of feature 1')}

-
-
-
-
-
-

{data.get('feature_2_title', 'Feature 2')}

-

{data.get('feature_2_desc', 'Description of feature 2')}

-
-
-
-
-
-

{data.get('feature_3_title', 'Feature 3')}

-

{data.get('feature_3_desc', 'Description of feature 3')}

-
-
-
-
-
-
-

Product screenshot or demo

-
-
-
- -
- ''' - - -def generate_metrics_slide(data): - """Traction/metrics slide with large numbers""" - metrics = data.get('metrics', [ - {'value': '10K+', 'label': 'Active Users'}, - {'value': '95%', 'label': 'Retention Rate'}, - {'value': '3x', 'label': 'Revenue Growth'}, - {'value': '$2M', 'label': 'ARR'} - ]) - - metrics_html = ''.join([f''' -
-
{m['value']}
-
{m['label']}
-
- ''' for m in metrics[:4]]) - - return f''' -
-
Traction
-

{data.get('headline', 'Our Growth')}

-
- {metrics_html} -
- -
- ''' - - -def generate_chart_slide(data): - """Chart slide with CSS bar chart""" - bars = data.get('bars', [ - {'label': 'Q1', 'value': 40}, - {'label': 'Q2', 'value': 60}, - {'label': 'Q3', 'value': 80}, - {'label': 'Q4', 'value': 100} - ]) - - bars_html = ''.join([f''' -
- {b.get('display', str(b['value']) + '%')} - {b['label']} -
- ''' for b in bars]) - - return f''' -
-
{data.get('badge', 'Growth')}
-

{data.get('headline', 'Revenue Growth')}

-
-
{data.get('chart_title', 'Quarterly Revenue')}
-
- {bars_html} -
-
- -
- ''' - - -def generate_testimonial_slide(data): - """Social proof slide""" - return f''' -
-
What They Say
-
-

"{data.get('quote', 'This product changed how we work. Incredible results.')}"

-

{data.get('author', 'Jane Doe')}

-

{data.get('role', 'CEO, Example Company')}

-
- -
- ''' - - -def generate_cta_slide(data): - """Closing CTA slide""" - return f''' -
-

{data.get('headline', 'Ready to get started?')}

-

{data.get('subheadline', 'Join thousands of teams already using our solution.')}

- - -
- ''' - - -# Slide type mapping -SLIDE_GENERATORS = { - 'title': generate_title_slide, - 'problem': generate_problem_slide, - 'solution': generate_solution_slide, - 'metrics': generate_metrics_slide, - 'traction': generate_metrics_slide, - 'chart': generate_chart_slide, - 'testimonial': generate_testimonial_slide, - 'cta': generate_cta_slide, - 'closing': generate_cta_slide -} - - -def generate_deck(slides_data, title="Pitch Deck"): - """Generate complete deck from slide data list""" - slides_html = "" - for slide in slides_data: - slide_type = slide.get('type', 'title') - generator = SLIDE_GENERATORS.get(slide_type) - if generator: - slides_html += generator(slide) - else: - print(f"Warning: Unknown slide type '{slide_type}'") - - # Calculate relative path to tokens CSS - tokens_rel_path = "../../../assets/design-tokens.css" - - return SLIDE_TEMPLATE.format( - title=title, - tokens_css_path=tokens_rel_path, - slides_content=slides_html - ) - - -def main(): - parser = argparse.ArgumentParser(description="Generate brand-compliant slides") - parser.add_argument("--json", "-j", help="JSON file with slide data") - parser.add_argument("--output", "-o", help="Output HTML file path") - parser.add_argument("--demo", action="store_true", help="Generate demo deck") - - args = parser.parse_args() - - if args.demo: - # Demo deck showcasing all slide types - demo_slides = [ - { - 'type': 'title', - 'badge': 'Investor Deck 2024', - 'title': 'ClaudeKit Marketing', - 'subtitle': 'Your AI marketing team. Always on.', - 'cta': 'Join Waitlist', - 'secondary_cta': 'See Demo', - 'company': 'ClaudeKit', - 'date': 'December 2024' - }, - { - 'type': 'problem', - 'headline': 'Marketing teams are drowning', - 'pain_1_title': 'Content Overload', - 'pain_1_desc': 'Need to produce 10x content with same headcount', - 'pain_2_title': 'Tool Fatigue', - 'pain_2_desc': '15+ tools that don\'t talk to each other', - 'pain_3_title': 'No Time to Think', - 'pain_3_desc': 'Strategy suffers when execution consumes all hours', - 'company': 'ClaudeKit', - 'page': '2' - }, - { - 'type': 'solution', - 'headline': 'AI agents that actually get marketing', - 'feature_1_title': 'Content Creation', - 'feature_1_desc': 'Blog posts, social, email - all on brand, all on time', - 'feature_2_title': 'Campaign Management', - 'feature_2_desc': 'Multi-channel orchestration with one command', - 'feature_3_title': 'Analytics & Insights', - 'feature_3_desc': 'Real-time optimization without the spreadsheets', - 'company': 'ClaudeKit', - 'page': '3' - }, - { - 'type': 'metrics', - 'headline': 'Early traction speaks volumes', - 'metrics': [ - {'value': '500+', 'label': 'Beta Users'}, - {'value': '85%', 'label': 'Weekly Active'}, - {'value': '4.9', 'label': 'NPS Score'}, - {'value': '50hrs', 'label': 'Saved/Week'} - ], - 'company': 'ClaudeKit', - 'page': '4' - }, - { - 'type': 'chart', - 'badge': 'Revenue', - 'headline': 'Growing month over month', - 'chart_title': 'MRR Growth ($K)', - 'bars': [ - {'label': 'Sep', 'value': 20, 'display': '$5K'}, - {'label': 'Oct', 'value': 40, 'display': '$12K'}, - {'label': 'Nov', 'value': 70, 'display': '$28K'}, - {'label': 'Dec', 'value': 100, 'display': '$45K'} - ], - 'company': 'ClaudeKit', - 'page': '5' - }, - { - 'type': 'testimonial', - 'quote': 'ClaudeKit replaced 3 tools and 2 contractors. Our content output tripled while costs dropped 60%.', - 'author': 'Sarah Chen', - 'role': 'Head of Marketing, TechStartup', - 'company': 'ClaudeKit', - 'page': '6' - }, - { - 'type': 'cta', - 'headline': 'Ship campaigns while you sleep', - 'subheadline': 'Early access available. Limited spots.', - 'cta': 'Join the Waitlist', - 'contact': 'hello@claudekit.ai', - 'website': 'claudekit.ai' - } - ] - - html = generate_deck(demo_slides, "ClaudeKit Marketing - Pitch Deck") - - OUTPUT_DIR.mkdir(parents=True, exist_ok=True) - output_path = OUTPUT_DIR / f"demo-pitch-{datetime.now().strftime('%y%m%d')}.html" - output_path.write_text(html, encoding='utf-8') - print(f"Demo deck generated: {output_path}") - - elif args.json: - with open(args.json, 'r') as f: - data = json.load(f) - - html = generate_deck(data.get('slides', []), data.get('title', 'Presentation')) - - output_path = Path(args.output) if args.output else OUTPUT_DIR / f"deck-{datetime.now().strftime('%y%m%d-%H%M')}.html" - output_path.parent.mkdir(parents=True, exist_ok=True) - output_path.write_text(html, encoding='utf-8') - print(f"Deck generated: {output_path}") - - else: - parser.print_help() - - -if __name__ == "__main__": - main() diff --git a/.agents/skills/ckm-design-system/scripts/generate-tokens.cjs b/.agents/skills/ckm-design-system/scripts/generate-tokens.cjs deleted file mode 100644 index 73cc7d7..0000000 --- a/.agents/skills/ckm-design-system/scripts/generate-tokens.cjs +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env node -/** - * Generate CSS variables from design tokens JSON - * - * Usage: - * node generate-tokens.cjs --config tokens.json -o tokens.css - * node generate-tokens.cjs --config tokens.json --format tailwind - */ - -const fs = require('fs'); -const path = require('path'); - -/** - * Parse command line arguments - */ -function parseArgs() { - const args = process.argv.slice(2); - const options = { - config: null, - output: null, - format: 'css' // css | tailwind - }; - - for (let i = 0; i < args.length; i++) { - if (args[i] === '--config' || args[i] === '-c') { - options.config = args[++i]; - } else if (args[i] === '--output' || args[i] === '-o') { - options.output = args[++i]; - } else if (args[i] === '--format' || args[i] === '-f') { - options.format = args[++i]; - } else if (args[i] === '--help' || args[i] === '-h') { - console.log(` -Usage: node generate-tokens.cjs [options] - -Options: - -c, --config Input JSON token file (required) - -o, --output Output file (default: stdout) - -f, --format Output format: css | tailwind (default: css) - -h, --help Show this help - `); - process.exit(0); - } - } - - return options; -} - -/** - * Resolve token references like {primitive.color.blue.600} - */ -function resolveReference(value, tokens) { - if (typeof value !== 'string' || !value.startsWith('{')) { - return value; - } - - const path = value.slice(1, -1).split('.'); - let result = tokens; - - for (const key of path) { - result = result?.[key]; - } - - if (result?.$value) { - return resolveReference(result.$value, tokens); - } - - return result || value; -} - -/** - * Convert token name to CSS variable name - */ -function toCssVarName(path) { - return '--' + path.join('-').replace(/\./g, '-'); -} - -/** - * Flatten tokens into CSS variables - */ -function flattenTokens(obj, tokens, prefix = [], result = {}) { - for (const [key, value] of Object.entries(obj)) { - const currentPath = [...prefix, key]; - - if (value && typeof value === 'object') { - if (value.$value !== undefined) { - // This is a token - const cssVar = toCssVarName(currentPath); - const resolvedValue = resolveReference(value.$value, tokens); - result[cssVar] = resolvedValue; - } else { - // Recurse into nested object - flattenTokens(value, tokens, currentPath, result); - } - } - } - - return result; -} - -/** - * Generate CSS output - */ -function generateCSS(tokens) { - const primitive = flattenTokens(tokens.primitive || {}, tokens, ['primitive']); - const semantic = flattenTokens(tokens.semantic || {}, tokens, []); - const component = flattenTokens(tokens.component || {}, tokens, []); - const darkSemantic = flattenTokens(tokens.dark?.semantic || {}, tokens, []); - - let css = `/* Design Tokens - Auto-generated */ -/* Do not edit directly - modify tokens.json instead */ - -/* === PRIMITIVES === */ -:root { -${Object.entries(primitive).map(([k, v]) => ` ${k}: ${v};`).join('\n')} -} - -/* === SEMANTIC === */ -:root { -${Object.entries(semantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')} -} - -/* === COMPONENTS === */ -:root { -${Object.entries(component).map(([k, v]) => ` ${k}: ${v};`).join('\n')} -} -`; - - if (Object.keys(darkSemantic).length > 0) { - css += ` -/* === DARK MODE === */ -.dark { -${Object.entries(darkSemantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')} -} -`; - } - - return css; -} - -/** - * Generate Tailwind config output - */ -function generateTailwind(tokens) { - const semantic = flattenTokens(tokens.semantic || {}, tokens, []); - - // Extract colors for Tailwind - const colors = {}; - for (const [key, value] of Object.entries(semantic)) { - if (key.includes('color')) { - const name = key.replace('--color-', '').replace(/-/g, '.'); - colors[name] = `var(${key})`; - } - } - - return `// Tailwind color config - Auto-generated -// Add to tailwind.config.ts theme.extend.colors - -module.exports = { - colors: ${JSON.stringify(colors, null, 2).replace(/"/g, "'")} -}; -`; -} - -/** - * Main - */ -function main() { - const options = parseArgs(); - - if (!options.config) { - console.error('Error: --config is required'); - process.exit(1); - } - - // Resolve config path - const configPath = path.resolve(process.cwd(), options.config); - - if (!fs.existsSync(configPath)) { - console.error(`Error: Config file not found: ${configPath}`); - process.exit(1); - } - - // Read and parse tokens - const tokens = JSON.parse(fs.readFileSync(configPath, 'utf-8')); - - // Generate output - let output; - if (options.format === 'tailwind') { - output = generateTailwind(tokens); - } else { - output = generateCSS(tokens); - } - - // Write output - if (options.output) { - const outputPath = path.resolve(process.cwd(), options.output); - fs.mkdirSync(path.dirname(outputPath), { recursive: true }); - fs.writeFileSync(outputPath, output); - console.log(`Generated: ${outputPath}`); - } else { - console.log(output); - } -} - -main(); diff --git a/.agents/skills/ckm-design-system/scripts/html-token-validator.py b/.agents/skills/ckm-design-system/scripts/html-token-validator.py deleted file mode 100644 index a722498..0000000 --- a/.agents/skills/ckm-design-system/scripts/html-token-validator.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/env python3 -""" -HTML Design Token Validator -Ensures all HTML assets (slides, infographics, etc.) use design tokens. -Source of truth: assets/design-tokens.css - -Usage: - python html-token-validator.py # Validate all HTML assets - python html-token-validator.py --type slides # Validate only slides - python html-token-validator.py --type infographics # Validate only infographics - python html-token-validator.py path/to/file.html # Validate specific file - python html-token-validator.py --fix # Auto-fix issues (WIP) -""" - -import re -import json -import sys -from pathlib import Path -from typing import Dict, List, Tuple, Optional - -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent -TOKENS_JSON_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' -TOKENS_CSS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.css' - -# Asset directories to validate -ASSET_DIRS = { - 'slides': PROJECT_ROOT / 'assets' / 'designs' / 'slides', - 'infographics': PROJECT_ROOT / 'assets' / 'infographics', -} - -# Patterns that indicate hardcoded values (should use tokens) -FORBIDDEN_PATTERNS = [ - (r'#[0-9A-Fa-f]{3,8}\b', 'hex color'), - (r'rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)', 'rgb color'), - (r'rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)', 'rgba color'), - (r'hsl\([^)]+\)', 'hsl color'), - (r"font-family:\s*'[^v][^a][^r][^']*',", 'hardcoded font'), # Exclude var() - (r'font-family:\s*"[^v][^a][^r][^"]*",', 'hardcoded font'), -] - -# Allowed rgba patterns (brand colors with transparency - CSS limitation) -# These are derived from brand tokens but need rgba for transparency -ALLOWED_RGBA_PATTERNS = [ - r'rgba\(\s*59\s*,\s*130\s*,\s*246', # --color-primary (#3B82F6) - r'rgba\(\s*245\s*,\s*158\s*,\s*11', # --color-secondary (#F59E0B) - r'rgba\(\s*16\s*,\s*185\s*,\s*129', # --color-accent (#10B981) - r'rgba\(\s*20\s*,\s*184\s*,\s*166', # --color-accent alt (#14B8A6) - r'rgba\(\s*0\s*,\s*0\s*,\s*0', # black transparency (common) - r'rgba\(\s*255\s*,\s*255\s*,\s*255', # white transparency (common) - r'rgba\(\s*15\s*,\s*23\s*,\s*42', # --color-surface (#0F172A) - r'rgba\(\s*7\s*,\s*11\s*,\s*20', # --color-background (#070B14) -] - -# Allowed exceptions (external images, etc.) -ALLOWED_EXCEPTIONS = [ - 'pexels.com', 'unsplash.com', 'youtube.com', 'ytimg.com', - 'googlefonts', 'fonts.googleapis.com', 'fonts.gstatic.com', -] - - -class ValidationResult: - """Validation result for a single file.""" - def __init__(self, file_path: Path): - self.file_path = file_path - self.errors: List[str] = [] - self.warnings: List[str] = [] - self.passed = True - - def add_error(self, msg: str): - self.errors.append(msg) - self.passed = False - - def add_warning(self, msg: str): - self.warnings.append(msg) - - -def load_css_variables() -> Dict[str, str]: - """Load CSS variables from design-tokens.css.""" - variables = {} - if TOKENS_CSS_PATH.exists(): - content = TOKENS_CSS_PATH.read_text() - # Extract --var-name: value patterns - for match in re.finditer(r'(--[\w-]+):\s*([^;]+);', content): - variables[match.group(1)] = match.group(2).strip() - return variables - - -def is_inside_block(content: str, match_pos: int, open_tag: str, close_tag: str) -> bool: - """Check if position is inside a specific HTML block.""" - pre = content[:match_pos] - tag_open = pre.rfind(open_tag) - tag_close = pre.rfind(close_tag) - return tag_open > tag_close - - -def is_allowed_exception(context: str) -> bool: - """Check if the hardcoded value is in an allowed exception context.""" - context_lower = context.lower() - return any(exc in context_lower for exc in ALLOWED_EXCEPTIONS) - - -def is_allowed_rgba(match_text: str) -> bool: - """Check if rgba pattern uses brand colors (allowed for transparency).""" - return any(re.match(pattern, match_text) for pattern in ALLOWED_RGBA_PATTERNS) - - -def get_context(content: str, pos: int, chars: int = 100) -> str: - """Get surrounding context for a match position.""" - start = max(0, pos - chars) - end = min(len(content), pos + chars) - return content[start:end] - - -def validate_html(content: str, file_path: Path, verbose: bool = False) -> ValidationResult: - """ - Validate HTML content for design token compliance. - - Checks: - 1. design-tokens.css import present - 2. No hardcoded colors in CSS (except in '): - if verbose: - result.add_warning(f"Allowed in ",High,https://nuxt.com/docs/guide/concepts/nuxt-lifecycle -18,Lifecycle,Use onMounted for DOM access,Access DOM only after component is mounted,onMounted for DOM manipulation,Direct DOM access in setup,"onMounted(() => { document.getElementById('el') })","",High,https://nuxt.com/docs/api/composables/on-mounted -19,Lifecycle,Use nextTick for post-render access,Wait for DOM updates before accessing elements,await nextTick() after state changes,Immediate DOM access after state change,"count.value++; await nextTick(); el.value.focus()","count.value++; el.value.focus()",Medium,https://nuxt.com/docs/api/utils/next-tick -20,Lifecycle,Use onPrehydrate for pre-hydration logic,Run code before Nuxt hydrates the page,onPrehydrate for client setup,onMounted for hydration-critical code,"onPrehydrate(() => { console.log(window) })",onMounted for pre-hydration needs,Low,https://nuxt.com/docs/api/composables/on-prehydrate -21,Server,Use server/api for API routes,Create API endpoints in server/api directory,server/api/users.ts for /api/users,Manual Express setup,server/api/hello.ts -> /api/hello,app.get('/api/hello'),High,https://nuxt.com/docs/guide/directory-structure/server -22,Server,Use defineEventHandler for handlers,Define server route handlers,defineEventHandler for all handlers,export default function,"export default defineEventHandler((event) => { return { hello: 'world' } })","export default function(req, res) {}",High,https://nuxt.com/docs/guide/directory-structure/server -23,Server,Use server/routes for non-api routes,Routes without /api prefix,server/routes for custom paths,server/api for non-api routes,server/routes/sitemap.xml.ts,server/api/sitemap.xml.ts,Medium,https://nuxt.com/docs/guide/directory-structure/server -24,Server,Use getQuery and readBody for input,Access query params and request body,getQuery(event) readBody(event),Direct event access,"const { id } = getQuery(event)",event.node.req.query,Medium,https://nuxt.com/docs/guide/directory-structure/server -25,Server,Validate server input,Always validate input in server handlers,Zod or similar for validation,Trust client input,"const body = await readBody(event); schema.parse(body)",const body = await readBody(event),High,https://nuxt.com/docs/guide/directory-structure/server -26,State,Use useState for shared reactive state,SSR-friendly shared state across components,useState for cross-component state,ref for shared state,"const count = useState('count', () => 0)",const count = ref(0) in composable,High,https://nuxt.com/docs/api/composables/use-state -27,State,Use unique keys for useState,Prevent state conflicts with unique keys,Descriptive unique keys for each state,Generic or duplicate keys,"useState('user-preferences', () => ({}))",useState('data') in multiple places,Medium,https://nuxt.com/docs/api/composables/use-state -28,State,Use Pinia for complex state,Pinia for advanced state management,@pinia/nuxt for complex apps,Custom state management,useMainStore() with Pinia,Custom reactive store implementation,Medium,https://nuxt.com/docs/getting-started/state-management -29,State,Use callOnce for one-time async operations,Ensure async operations run only once,callOnce for store initialization,Direct await in component,"await callOnce(store.fetch)",await store.fetch() on every render,Medium,https://nuxt.com/docs/api/utils/call-once -30,SEO,Use useSeoMeta for SEO tags,Type-safe SEO meta tag management,useSeoMeta for meta tags,useHead for simple meta,"useSeoMeta({ title: 'Home', ogTitle: 'Home', description: '...' })","useHead({ meta: [{ name: 'description', content: '...' }] })",High,https://nuxt.com/docs/api/composables/use-seo-meta -31,SEO,Use reactive values in useSeoMeta,Dynamic SEO tags with refs or getters,Computed getters for dynamic values,Static values for dynamic content,"useSeoMeta({ title: () => post.value.title })","useSeoMeta({ title: post.value.title })",Medium,https://nuxt.com/docs/api/composables/use-seo-meta -32,SEO,Use useHead for non-meta head elements,Scripts styles links in head,useHead for scripts and links,useSeoMeta for scripts,"useHead({ script: [{ src: '/analytics.js' }] })","useSeoMeta({ script: '...' })",Medium,https://nuxt.com/docs/api/composables/use-head -33,SEO,Include OpenGraph tags,Add OG tags for social sharing,ogTitle ogDescription ogImage,Missing social preview,"useSeoMeta({ ogImage: '/og.png', twitterCard: 'summary_large_image' })",No OG configuration,Medium,https://nuxt.com/docs/api/composables/use-seo-meta -34,Middleware,Use defineNuxtRouteMiddleware,Define route middleware properly,defineNuxtRouteMiddleware wrapper,export default function,"export default defineNuxtRouteMiddleware((to, from) => {})","export default function(to, from) {}",High,https://nuxt.com/docs/guide/directory-structure/middleware -35,Middleware,Use navigateTo for redirects,Redirect in middleware with navigateTo,return navigateTo('/login'),router.push in middleware,"if (!auth) return navigateTo('/login')","if (!auth) router.push('/login')",High,https://nuxt.com/docs/api/utils/navigate-to -36,Middleware,Reference middleware in definePageMeta,Apply middleware to specific pages,middleware array in definePageMeta,Global middleware for page-specific,definePageMeta({ middleware: ['auth'] }),Global auth check for one page,Medium,https://nuxt.com/docs/guide/directory-structure/middleware -37,Middleware,Use .global suffix for global middleware,Apply middleware to all routes,auth.global.ts for app-wide auth,Manual middleware on every page,middleware/auth.global.ts,middleware: ['auth'] on every page,Medium,https://nuxt.com/docs/guide/directory-structure/middleware -38,ErrorHandling,Use createError for errors,Create errors with proper status codes,createError with statusCode,throw new Error,"throw createError({ statusCode: 404, statusMessage: 'Not Found' })",throw new Error('Not Found'),High,https://nuxt.com/docs/api/utils/create-error -39,ErrorHandling,Use NuxtErrorBoundary for local errors,Handle errors within component subtree,NuxtErrorBoundary for component errors,Global error page for local errors,"