From 1ae804fd89b9dd2ae3e4ffc44243b72f9b3f2e05 Mon Sep 17 00:00:00 2001
From: Mudit Lal :
`. `brandingUrl` is not read; the local branding type
+ and loader payload no longer carry it.
+- `apps/remix/app/components/general/envelope-signing/envelope-signer-header.tsx` (V2,
+ shared by normal and direct-template signing): custom logo renders as a bare `
`; the
+ Documenso fallback keeps its ``.
+- `apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx`: V1 loader branding payload no
+ longer includes `brandingUrl`.
+- `packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts` and
+ `get-envelope-for-direct-template-signing.ts`: `brandingUrl` removed from the V2
+ `EnvelopeForSigningResponse.settings` schema/payload since it is not consumed there.
+
+# History
+
+An earlier iteration of this plan wired `brandingUrl` into the in-app signing pages so a
+custom logo linked to the Brand Website (external ``, internal `/`
+fallback otherwise) and added `brandingUrl` to the V1/V2 signing payloads. That direction
+was reversed: signing-page logos are now plain images and `brandingUrl` is email-only. The
+signing payload additions were removed.
+
+# Test coverage
+
+`packages/app-tests/e2e/signing-branding.spec.ts`:
+
+- V1 normal `/sign/:token`: custom logo is a plain image, not inside a link, and no
+ `brandingUrl` link is present.
+- V2 normal `/sign/:token` and V2 direct-template: same plain-image assertions.
+- V2 with no custom logo: Documenso fallback still links to `/`.
+- Embedded signing: no custom-logo Brand Website link is rendered.
+
+# Acceptance criteria
+
+- A custom branding logo on any signing surface (V1, V2 normal, V2 direct-template, embedded)
+ renders as a plain image with no link, and `brandingUrl` is never rendered as a link there.
+- Documenso fallback logos continue linking to `/`.
+- In transactional emails, when a custom logo and a safe `brandingUrl` are configured, the
+ email logo links to `brandingUrl` (new tab) and the footer shows the Brand Website link.
+- In transactional emails, when `brandingUrl` is empty/invalid/relative/non-http(s), the logo
+ is a plain image and no footer Brand Website link is shown.
+- URL safety is enforced through the single shared `getSafeBrandingUrl` helper.
+- Settings/admin branding previews are unchanged.
+- No schema or migration changes.
diff --git a/.agents/plans/wild-indigo-wave-rejected-expired-recipient-filters.md b/.agents/plans/wild-indigo-wave-rejected-expired-recipient-filters.md
new file mode 100644
index 000000000..b8e9fb39c
--- /dev/null
+++ b/.agents/plans/wild-indigo-wave-rejected-expired-recipient-filters.md
@@ -0,0 +1,146 @@
+---
+date: 2026-05-28
+title: Rejected Expired Recipient Filters
+---
+
+## Context
+
+Customers need to find (a) envelopes/documents in the `REJECTED` state and (b) envelopes
+with at least one recipient whose signing link has **expired**. Today the UI only exposes
+`INBOX / PENDING / COMPLETED / DRAFT / ALL` tabs, and the public API has no way to filter by
+expired recipient links — forcing a fetch-all-`PENDING`-then-inspect-each-recipient workaround.
+
+Two key facts from exploration shaped this plan:
+
+- **`REJECTED` is already fully wired in the backend** — the where-clause (`find-documents.ts`),
+ stats counts (`get-stats.ts`), tRPC response schema, `ExtendedDocumentStatus` enum, and the
+ `FRIENDLY_STATUS_MAP` display all handle it. It is simply absent from the UI tab array.
+- **Renewing expired links already works.** `resendDocument` refreshes `expiresAt` and clears
+ `expirationNotifiedAt` for unsigned, non-CC recipients (`resend-document.ts:98-121`), exposed
+ publicly via `POST /api/v2/document/redistribute` and `/api/v2/envelope/redistribute` and via the
+ resend/redistribute UI dialogs. No new renew mechanism is needed — only documentation/wording.
+
+Expiration is a per-recipient condition (not an envelope status). The approved design models it
+in the UI as an `EXPIRED` **pseudo-status tab** (reusing the existing tab machinery, mirroring how
+`REJECTED` works) and in the public API as an orthogonal boolean `hasExpiredRecipients`. Both share
+one EXISTS predicate.
+
+Definition of "expired recipient" (matches `isRecipientExpired`, `packages/lib/utils/recipients.ts:118`):
+a `Recipient` with `expiresAt IS NOT NULL AND expiresAt <= now() AND signingStatus = NOT_SIGNED AND role != CC`.
+
+## Approach
+
+### A. Shared EXISTS predicate (reused 4x, justified)
+Add a local `hasExpiredRecipient(eb)` helper — modeled on the existing per-file `recipientExists` /
+`senderEmailIs` helpers — to `find-documents.ts`, `get-stats.ts`, and `find-envelopes.ts`. It is the
+single source of truth for the expired condition above (using `new Date()` for `now`, matching the
+`period` filter's `.toJSDate()` style).
+
+### B. REJECTED tab (UI only — backend already done)
+- `apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents._index.tsx`: add
+ `ExtendedDocumentStatus.REJECTED` to the tab array (lines 149-155). Count badge, highlight, and
+ `?status=REJECTED` filtering already work via existing machinery.
+
+### C. EXPIRED pseudo-status (UI + internal stats)
+1. `packages/prisma/types/extended-document-status.ts`: add `EXPIRED: 'EXPIRED'`. Internal-only —
+ the public `DocumentStatus` enum is unaffected. This intentionally surfaces TS errors at the three
+ exhaustive/`Record
Feel free to hop into our community in [Discord](https://documen.so/discord)
issue-message: |
Thank you for opening your first issue and for being a part of the open signing revolution!
-
One of our team members will review it and get back to you as soon as it possible 💚
+
One of our team members will review it and get back to you as soon as possible 💚
Meanwhile, please feel free to hop into our community in [Discord](https://documen.so/discord)
diff --git a/.github/workflows/issue-assignee-check.yml b/.github/workflows/issue-assignee-check.yml
deleted file mode 100644
index 974fda86d..000000000
--- a/.github/workflows/issue-assignee-check.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-name: 'Issue Assignee Check'
-
-on:
- issues:
- types: ['assigned']
-
-permissions:
- issues: write
-
-jobs:
- countIssues:
- if: github.repository == 'documenso/documenso' && ${{ github.event.issue.assignee }} && github.event.action == 'assigned' && github.event.sender.type == 'User'
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 2
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '18'
-
- - name: Install Octokit
- run: npm install @octokit/rest@18
-
- - name: Check Assigned User's Issue Count
- id: parse-comment
- uses: actions/github-script@v6
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const { Octokit } = require("@octokit/rest");
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
-
- const username = context.payload.issue.assignee.login;
- console.log(`Username Extracted: ${username}`);
-
- const { data: issues } = await octokit.issues.listForRepo({
- owner: context.repo.owner,
- repo: context.repo.repo,
- assignee: username,
- state: 'open'
- });
-
- const issueCount = issues.length;
- console.log(`Issue Count For ${username}: ${issueCount}`);
-
- if (issueCount > 3) {
- let issueCountMessage = `### 🚨 Documenso Police 🚨`;
- issueCountMessage += `\n@${username} has ${issueCount} open issues assigned already. Consider whether this issue should be assigned to them or left open for another contributor.`;
-
- await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: context.issue.number,
- body: issueCountMessage,
- headers: {
- 'Authorization': `token ${{ secrets.GITHUB_TOKEN }}`,
- }
- });
- }
diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/issue-labeler.yml
index 07e2f6983..2ad688094 100644
--- a/.github/workflows/issue-labeler.yml
+++ b/.github/workflows/issue-labeler.yml
@@ -8,6 +8,7 @@ jobs:
label-when-assigned:
if: github.repository == 'documenso/documenso'
runs-on: ubuntu-latest
+ timeout-minutes: 10
steps:
- name: Label issue
uses: actions/github-script@v6
diff --git a/.github/workflows/issue-opened.yml b/.github/workflows/issue-opened.yml
index f9166587c..948d0d48d 100644
--- a/.github/workflows/issue-opened.yml
+++ b/.github/workflows/issue-opened.yml
@@ -8,6 +8,7 @@ jobs:
label_issues:
if: github.repository == 'documenso/documenso'
runs-on: ubuntu-latest
+ timeout-minutes: 10
permissions:
issues: write
steps:
diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml
index d243b1335..4d6d80ca3 100644
--- a/.github/workflows/pr-labeler.yml
+++ b/.github/workflows/pr-labeler.yml
@@ -14,6 +14,7 @@ jobs:
contents: read
pull-requests: write
runs-on: ubuntu-latest
+ timeout-minutes: 10
steps:
- uses: actions/labeler@v4
with:
diff --git a/.github/workflows/pr-review-reminder.yml b/.github/workflows/pr-review-reminder.yml
deleted file mode 100644
index b334024fe..000000000
--- a/.github/workflows/pr-review-reminder.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: 'PR Review Reminder'
-
-on:
- pull_request:
- types: ['opened', 'ready_for_review']
-
-permissions:
- pull-requests: write
-
-jobs:
- checkPRs:
- if: github.repository == 'documenso/documenso' && ${{ github.event.pull_request.user.login }} && github.event.action == ('opened' || 'ready_for_review')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 2
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '18'
-
- - name: Install Octokit
- run: npm install @octokit/rest@18
-
- - name: Check user's PRs awaiting review
- id: parse-prs
- uses: actions/github-script@v5
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const { Octokit } = require("@octokit/rest");
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
-
- const username = context.payload.pull_request.user.login;
- console.log(`Username Extracted: ${username}`);
-
- const { data: pullRequests } = await octokit.pulls.list({
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'open',
- sort: 'created',
- direction: 'asc',
- });
-
- const userPullRequests = pullRequests.filter(pr => pr.user.login === username && (pr.state === 'open' || pr.state === 'ready_for_review'));
- const prCount = userPullRequests.length;
- console.log(`PR Count for ${username}: ${prCount}`);
-
- if (prCount > 3) {
- let prReminderMessage = `🚨 @${username} has ${prCount} pull requests awaiting review. Please consider reviewing them when possible. 🚨`;
-
- await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: context.payload.pull_request.number,
- body: prReminderMessage,
- headers: {
- 'Authorization': `token ${{ secrets.GITHUB_TOKEN }}`,
- }
- });
- }
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f33295aed..907a43351 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -15,6 +15,7 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Build and publish platform containers
runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
strategy:
fail-fast: false
matrix:
@@ -80,6 +81,7 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Create and publish manifest
runs-on: ubuntu-latest
+ timeout-minutes: 60
needs: build_and_publish_platform_containers
steps:
- name: Checkout
diff --git a/.github/workflows/semantic-pull-requests.yml b/.github/workflows/semantic-pull-requests.yml
index 82996cf28..a791e2ae5 100644
--- a/.github/workflows/semantic-pull-requests.yml
+++ b/.github/workflows/semantic-pull-requests.yml
@@ -16,25 +16,8 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Validate PR title
runs-on: ubuntu-latest
+ timeout-minutes: 10
steps:
- - name: Check PR creator's previous activity
- id: check_activity
- run: |
- CREATOR=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" | jq -r '.user.login')
- ACTIVITY=$(curl -s "https://api.github.com/search/commits?q=author:${CREATOR}+repo:${{ github.repository }}" | jq -r '.total_count')
- if [ "$ACTIVITY" -eq 0 ]; then
- echo "::set-output name=is_new::true"
- else
- echo "::set-output name=is_new::false"
- fi
-
- - name: Count PRs created by user
- id: count_prs
- run: |
- CREATOR=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" | jq -r '.user.login')
- PR_COUNT=$(curl -s "https://api.github.com/search/issues?q=type:pr+is:open+author:${CREATOR}+repo:${{ github.repository }}" | jq -r '.total_count')
- echo "::set-output name=pr_count::$PR_COUNT"
-
- uses: amannn/action-semantic-pull-request@v5
id: lint_pr_title
env:
@@ -45,8 +28,6 @@ jobs:
with:
header: pr-title-lint-error
message: |
- Hey There! and thank you for opening this pull request! 📝👋🏼
-
We require pull request titles to follow the [Conventional Commits Spec](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
Details:
@@ -54,10 +35,3 @@ jobs:
```
${{ steps.lint_pr_title.outputs.error_message }}
```
-
- - if: ${{ steps.lint_pr_title.outputs.error_message == null && steps.check_activity.outputs.is_new == 'false' && steps.count_prs.outputs.pr_count < 2}}
- uses: marocchino/sticky-pull-request-comment@v2
- with:
- header: pr-title-lint-error
- message: |
- Thank you for following the naming conventions for pull request titles! 💚🚀
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 0771e5178..a93ff4aa7 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -8,6 +8,7 @@ jobs:
stale:
if: github.repository == 'documenso/documenso'
runs-on: ubuntu-latest
+ timeout-minutes: 10
permissions:
issues: write
pull-requests: write
@@ -22,4 +23,4 @@ jobs:
stale-pr-message: 'This PR has not seen activitiy for a while. It will be closed in 30 days unless further activity is detected.'
close-pr-message: 'This PR has been closed because of inactivity.'
exempt-pr-labels: 'WIP,on-hold,needs review'
- exempt-issue-labels: 'WIP,on-hold,needs review,roadmap,assigned,needs triage'
+ exempt-issue-labels: 'WIP,on-hold,needs review,roadmap,status: assigned,status: triage'
diff --git a/.github/workflows/translations-force-pull.yml b/.github/workflows/translations-force-pull.yml
index 09f6b4625..4d5f24279 100644
--- a/.github/workflows/translations-force-pull.yml
+++ b/.github/workflows/translations-force-pull.yml
@@ -19,6 +19,7 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Force pull translations
runs-on: ubuntu-latest
+ timeout-minutes: 10
environment: Translations
permissions:
contents: write
diff --git a/.github/workflows/translations-pull.yml b/.github/workflows/translations-pull.yml
index e7cf438bd..93d9616b5 100644
--- a/.github/workflows/translations-pull.yml
+++ b/.github/workflows/translations-pull.yml
@@ -17,6 +17,7 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Pull translations
runs-on: ubuntu-latest
+ timeout-minutes: 10
environment: Translations
permissions:
contents: write
diff --git a/.github/workflows/translations-upload.yml b/.github/workflows/translations-upload.yml
index f09c5c140..0e48d6396 100644
--- a/.github/workflows/translations-upload.yml
+++ b/.github/workflows/translations-upload.yml
@@ -15,6 +15,7 @@ jobs:
if: github.repository == 'documenso/documenso'
name: Extract and upload translations
runs-on: ubuntu-latest
+ timeout-minutes: 30
environment: Translations
permissions:
contents: write
diff --git a/.gitpod.yml b/.gitpod.yml
index 261f8c96b..883ac5bb3 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -31,8 +31,7 @@ vscode:
extensions:
- aaron-bond.better-comments
- bradlc.vscode-tailwindcss
- - dbaeumer.vscode-eslint
- - esbenp.prettier-vscode
+ - biomejs.biome
- mikestead.dotenv
- unifiedjs.vscode-mdx
- GitHub.vscode-pull-request-github
diff --git a/.prettierignore b/.prettierignore
deleted file mode 100644
index f5c70c1d5..000000000
--- a/.prettierignore
+++ /dev/null
@@ -1,20 +0,0 @@
-node_modules
-.next
-public
-**/**/node_modules
-**/**/.next
-**/**/public
-packages/lib/translations/**/*.js
-
-*.lock
-*.log
-*.test.ts
-
-.gitignore
-.npmignore
-.prettierignore
-.DS_Store
-.eslintignore
-
-# Docs MDX - Prettier strips indentation from code blocks inside components
-apps/docs/content/**/*.mdx
diff --git a/.well-known/security.txt b/.well-known/security.txt
index 1a3f685e5..f96fce0f0 100644
--- a/.well-known/security.txt
+++ b/.well-known/security.txt
@@ -1,7 +1,14 @@
-# General Issues
-Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
+# Report security vulnerabilities privately via GitHub Security Advisories (preferred).
+Contact: https://github.com/documenso/documenso/security/advisories/new
-# Report critical issues privately to let us take appropriate action before publishing.
+# Alternatively, report critical issues privately by email.
Contact: mailto:security@documenso.com
+
+# Security policy
+Policy: https://github.com/documenso/documenso/security/policy
+
+# General (non-security) issues
+Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
+
Preferred-Languages: en
-Canonical: https://documenso.com/.well-known/security.txt
\ No newline at end of file
+Canonical: https://documenso.com/.well-known/security.txt
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index be9dbb555..d3cee2f37 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -42,8 +42,8 @@ Documenso is an open-source document signing platform built as a **monorepo** us
| Package | Description | Port |
| -------------------------- | -------------------------------------------------------- | ---- |
| `@documenso/remix` | Main application - React Router (Remix) with Hono server | 3000 |
-| `@documenso/documentation` | Documentation site (Next.js + Nextra) | 3002 |
| `@documenso/openpage-api` | Public analytics API | 3003 |
+| `@documenso/docs` | Documentation site | 3004 |
### Core Packages (`packages/`)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5cd7a6887..7260cdfe2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,13 +1,27 @@
# Contributing to Documenso
-If you plan to contribute to Documenso, please take a moment to feel awesome ✨ People like you are what open source is about ♥. Any contributions, no matter how big or small, are highly appreciated.
+> **We are no longer accepting external pull requests.**
+>
+> Aside from a small group of trusted contributors we reach out to directly, we no longer merge external PRs. New pull requests will usually be closed with a request to open an issue instead. This is a security decision, not a judgement on your work. Read [Why We're Pausing External Pull Requests](https://documenso.com/blog/why-we-re-pausing-external-pull-requests) for the full reasoning.
+>
+> Documenso stays open source. You can still read, audit, run, and fork the code. The best way to contribute is through detailed issues.
-## Before getting started
+## How to contribute now
-- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
-- Select an issue from [here](https://github.com/documenso/documenso/issues) or create a new one
-- Consider the results from the discussion on the issue
-- Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
+The most useful contribution is a detailed issue. Treat it like a spec. The more detail, the better:
+
+- The problem you're trying to solve, and who it affects
+- How you expect the feature or change to behave
+- Edge cases, constraints, and anything you've already considered
+- Examples, mockups, or references where they help
+
+Before opening an issue, search [existing issues](https://github.com/documenso/documenso/issues) and [discussions](https://github.com/documenso/documenso/discussions) for related items. If a proposal is detailed and fits where Documenso is heading, we'll pick it up and build against it.
+
+For security vulnerabilities, do not open a public issue. Follow our [Security Policy](./SECURITY.md) instead.
+
+---
+
+The sections below are for trusted contributors working with us directly, and for anyone running Documenso locally or maintaining a fork.
## English only PRs and Issues
diff --git a/DEVALOK_FORK_NOTES.md b/DEVALOK_FORK_NOTES.md
index f6c165708..5e309da0f 100644
--- a/DEVALOK_FORK_NOTES.md
+++ b/DEVALOK_FORK_NOTES.md
@@ -10,16 +10,37 @@ It is **not** a real GitHub fork — Railway's eject pushed a squashed initial c
|---|---|---|
| 2026-05-04 | (initial Railway eject) | Squashed initial commit `af5d802` |
| 2026-06-04 | `0ecde7a` | First upstream sync. 86 commits absorbed (~v2.10.x → v2.11.x territory). 4 additive Prisma migrations applied. Guards re-applied to all 14 upstream-only workflows. |
+| 2026-08-19 | `7533016` (v2.17.0) | Second upstream sync. 120 commits, v2.11 → v2.17.0. 4 additive Prisma migrations. **Motivation:** deployed commit predated upstream `583e35c7` ("fix: ensures new expire on setSessionCookie", #2708), which froze every session cookie's `Expires` at process-start + 30 days — all logins silently broke on 2026-07-04. Guards re-applied to 13 workflows (upstream deleted `issue-assignee-check.yml` and `pr-review-reminder.yml`). |
## Upstream sync
+Histories are **unrelated** (squashed eject), so `git merge upstream/main` fails with
+`refusing to merge unrelated histories`. Every sync is a squash-import of upstream's tree.
+
+Import the tree wholesale — do **not** hand-apply a diff. The 2026-06-04 sync applied upstream's
+additions and modifications but silently kept files upstream had **deleted** (`packages/eslint-config/`,
+`packages/prettier-config/`, `prettier.config.cjs`, `.prettierignore`, `.eslintrc.cjs`,
+`packages/lib/server-only/document/send-completed-email.ts`, stale embedding docs). `read-tree` propagates
+deletions; a diff does not.
+
```bash
git remote add upstream https://github.com/documenso/documenso.git # one-time
git fetch upstream
-git merge upstream/main
-git push origin main
+git checkout -b chore/upstream-merge-
distributeDocument: true
diff --git a/apps/docs/content/docs/users/settings/delete-account.mdx b/apps/docs/content/docs/users/settings/delete-account.mdx
index 569dc1bc2..77640db70 100644
--- a/apps/docs/content/docs/users/settings/delete-account.mdx
+++ b/apps/docs/content/docs/users/settings/delete-account.mdx
@@ -7,14 +7,14 @@ import { Callout } from 'fumadocs-ui/components/callout';
import { Step, Steps } from 'fumadocs-ui/components/steps';
!Qp
z{MoO*OS =KD0q2J{+o%qIeEwRSpqHaRA}315vzGl=j_1
z)0h{pBt8UWB2!}7TT+kU81y?v7;#a8MLBk!>5GjaDjIsn&m(Dhlm{k&$w#vaxv6zZ
z{ScRGb4?!?%OH(3OlE%_%Uk$4D~0MQ&**HDl2tw|Yr$TkrMtk8!X)PFUR(*ZQ_uA8
z_%BHyYud3tQwL~6iaXo$nn+pYJ6g0Xg*05(vFXOrjlzh4A!16PA|ia0rD(w2>Y5%H
zbdm$OZJCJFEErhrN8tX+Z3_~=9Jf4{JS^9pmcR``!g|RZD&BkV>F(}}5k**ekhU(w
zMA?;vHE#61Va%z(GA2pWX7HsJpf}GJYzvmZn=x;I?C|OUSpJldV}#w*E;GSIT_b_W
zrRfrto3371zMWtJ*QT80XEjihI<$zRBavhc*W*P{l_o9icTuc$;j(m$#sOEyh1HsD
z#ItM*URjzyy$nbKOtyGz3n05klmuTcHa`(~GuK2OH3>C{Ci^I^Pn>YN6#sZiTAr49=3kO1&_G8rXz*{`c-=P;60V7BWy6{cqAF)o
zpzd5n6=U_+<{`m$y;p-h2SU4$KZ@H^$6S|izkz>|m`4MWo4~5LQa<$I-a!&xO0!e1
zoG`+o1-IS6T6@<+V*1&xonUdl7gAO$*D!DzSnM!%Ep$*`;ByNdlM+hxhH_nDhWEI4
zZgu(*kHJd8GB3IE$i>){2BM}QtA5iZ*H|=K!h49woGisrE%+Vt^U=4P!_mI3t}0sv
zv*EGE1&72>2crt?UAVY)fEWrTH65aWok$MchoAFf>^D)}H#*LYbv$Uwt!AqgY`|wO
zrn(gey1k>6nUPOyy?KYag6T746-`#I_;hp40><~dq4aeYMan!{Vu6GG6-f%*Y>CGM
z|HzS)Z{*F27r&;4W?fI`(S|IqJvHvD_e@JOk-_W|@O`piZ&!pupZ!JvGFl!tK}#ud
zszdaNge1t1erW*?JQ7m~AE|
Nuv!rY*3`!P5`ibl>*8hhGkV4SqW`lVC
zx6m~DhYHD?Y5BpOD-JNI{C3Qx%8zK3tn;eq!&JfNhB1*P!OVPE_D|xg`f#bC$M*^n
ziIl;1c)NZ#nv7CGe
zcR*scHk%*vQ*y3PZKiwaqzwDUoDty^nKmH~%sjllZJ^<4sq$sW6LLP~YKv{P@s_^;
z){I_1iaMIKvOk#zkB9QfAWQ%mFMh%n82BLVyJZYdQVntGvk;x@M>AS8=H1|=>BoAx
zFk0C;WbFoN{~pYEvjIib5^108#S)>Y(f$EGkbK2xt@M=I`VEut3h7CYL+YD2x2lB#
zmCoEY#Ye5iM*MwIs-XIxFkaK|&LU7q2dxOJp__6l6g(D!l~KE4FO@6JQ3>U;Cq|$7
z_7LtGaOE)p*0sVNT}Cb^@FO;5unHjM;<5Vk
8YD!>M