Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras

This commit is contained in:
Leonardo
2026-03-24 21:26:58 -03:00
parent a89d1f5560
commit 53a4980396
453 changed files with 121427 additions and 174407 deletions

566
docs/USER_ARCHETYPES.html Normal file
View File

@@ -0,0 +1,566 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AgenciaPsi — Arquétipos de Usuário</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: #0f1117;
color: #e2e8f0;
min-height: 100vh;
padding: 2rem 1rem 4rem;
}
/* ── Header ── */
.page-header {
text-align: center;
margin-bottom: 3rem;
}
.page-header h1 {
font-size: 2rem;
font-weight: 800;
letter-spacing: -0.04em;
background: linear-gradient(135deg, #818cf8, #34d399);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.page-header p {
margin-top: .5rem;
color: #64748b;
font-size: .95rem;
}
/* ── Grid ── */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
gap: 1.5rem;
max-width: 1400px;
margin: 0 auto;
}
/* ── Card ── */
.card {
background: #1e2330;
border: 1px solid #2d3548;
border-radius: 1.25rem;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
transition: border-color .2s, transform .2s;
}
.card:hover {
border-color: #4f6ef7;
transform: translateY(-2px);
}
/* ── Card header ── */
.card-header {
display: flex;
align-items: center;
gap: .75rem;
}
.card-icon {
width: 2.75rem;
height: 2.75rem;
border-radius: .75rem;
display: grid;
place-items: center;
font-size: 1.25rem;
flex-shrink: 0;
}
.card-title { font-size: 1.05rem; font-weight: 700; line-height: 1.2; }
.card-subtitle { font-size: .75rem; color: #64748b; margin-top: 2px; font-family: monospace; }
/* ── Tree ── */
.tree {
background: #0f1117;
border-radius: .75rem;
padding: 1rem 1.1rem;
font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
font-size: .78rem;
line-height: 1.8;
}
.tree-root { color: #a5b4fc; font-weight: 600; }
.tree-branch { color: #475569; }
.tree-leaf { color: #cbd5e1; }
.tree-comment { color: #475569; font-style: italic; }
.tree-key { color: #f472b6; }
.tree-val { color: #34d399; }
.tree-warn { color: #fb923c; }
.tree-new { color: #facc15; }
/* ── Badges ── */
.badges { display: flex; flex-wrap: wrap; gap: .4rem; }
.badge {
font-size: .68rem;
font-weight: 600;
padding: .2rem .6rem;
border-radius: 9999px;
border: 1px solid transparent;
letter-spacing: .02em;
}
.badge-purple { background: #312e81; border-color: #4f46e5; color: #a5b4fc; }
.badge-green { background: #064e3b; border-color: #059669; color: #6ee7b7; }
.badge-blue { background: #1e3a5f; border-color: #2563eb; color: #93c5fd; }
.badge-orange { background: #431407; border-color: #ea580c; color: #fdba74; }
.badge-yellow { background: #422006; border-color: #ca8a04; color: #fde047; }
.badge-pink { background: #500724; border-color: #db2777; color: #f9a8d4; }
.badge-gray { background: #1e293b; border-color: #475569; color: #94a3b8; }
.badge-red { background: #450a0a; border-color: #dc2626; color: #fca5a5; }
/* ── Notes ── */
.note {
font-size: .75rem;
color: #64748b;
line-height: 1.5;
border-left: 2px solid #2d3548;
padding-left: .75rem;
}
.note strong { color: #94a3b8; }
/* ── Phase tag ── */
.phase {
display: inline-flex;
align-items: center;
gap: .3rem;
font-size: .7rem;
font-weight: 700;
padding: .15rem .55rem;
border-radius: 9999px;
margin-left: auto;
}
.phase-1 { background: #064e3b; color: #6ee7b7; border: 1px solid #059669; }
.phase-2 { background: #422006; color: #fde047; border: 1px solid #ca8a04; }
/* ── Section label ── */
.section-label {
grid-column: 1 / -1;
font-size: .7rem;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
color: #475569;
padding: .25rem 0;
border-bottom: 1px solid #2d3548;
margin-bottom: -.25rem;
}
/* ── Legend ── */
.legend {
max-width: 1400px;
margin: 2.5rem auto 0;
background: #1e2330;
border: 1px solid #2d3548;
border-radius: 1.25rem;
padding: 1.25rem 1.5rem;
}
.legend h3 { font-size: .8rem; font-weight: 700; color: #64748b; letter-spacing: .08em; text-transform: uppercase; margin-bottom: .75rem; }
.legend-grid { display: flex; flex-wrap: wrap; gap: 1rem 2rem; }
.legend-item { display: flex; align-items: center; gap: .5rem; font-size: .78rem; color: #94a3b8; }
.legend-dot { width: .65rem; height: .65rem; border-radius: 50%; }
</style>
</head>
<body>
<div class="page-header">
<h1>AgenciaPsi — Arquétipos de Usuário</h1>
<p>Como cada tipo de usuário está estruturado no banco de dados e no sistema de permissões.</p>
</div>
<div class="grid">
<!-- ════════════════════════════════════════ PLATAFORMA ══ -->
<div class="section-label">🏛️ Plataforma</div>
<!-- SaaS Admin -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#312e81">🛡️</div>
<div>
<div class="card-title">SaaS Admin</div>
<div class="card-subtitle">profiles.role = 'saas_admin'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (saas@agenciapsi.com.br)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'saas_admin'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// sem memberships de tenant</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /saas/*</span></div>
</div>
<div class="badges">
<span class="badge badge-purple">role: saas_admin</span>
<span class="badge badge-gray">sem tenant</span>
</div>
<p class="note">Acesso total à plataforma. Gerencia planos, features, assinaturas e usuários. Nunca entra em contexto de tenant.</p>
</div>
<!-- Editor -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#1e3a5f">✍️</div>
<div>
<div class="card-title">Editor de Conteúdo</div>
<div class="card-subtitle">profiles.platform_roles[] = 'editor'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (editor@agenciapsi.com.br)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">profiles.platform_roles</span> <span class="tree-val">['editor']</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// sem memberships de tenant</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /editor/*</span></div>
</div>
<div class="badges">
<span class="badge badge-blue">platform_roles: editor</span>
<span class="badge badge-gray">sem tenant</span>
</div>
<p class="note">Papel de plataforma (não de tenant). Gerencia conteúdo público, landing pages, textos. Verificado via <code>platform_roles[]</code>.</p>
</div>
<!-- ════════════════════════════════════════ CLÍNICA ══ -->
<div class="section-label">🏥 Clínica</div>
<!-- Clinic Admin -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#064e3b">🏥</div>
<div>
<div class="card-title">Admin da Clínica</div>
<div class="card-subtitle">tenant.kind = 'clinic'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (admin@clinicax.com.br)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'tenant_member'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership: <span class="tree-val">Clínica X</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'clinic'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-val">'clinic_admin'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-val">clinic_free | clinic_pro</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /admin/*</span></div>
</div>
<div class="badges">
<span class="badge badge-green">role: clinic_admin</span>
<span class="badge badge-blue">tenant: clinic</span>
<span class="badge badge-gray">clinic_free</span>
<span class="badge badge-purple">clinic_pro</span>
</div>
<p class="note">Dono ou gestor de uma clínica. Gerencia profissionais, pacientes, agenda e módulos da clínica.</p>
</div>
<!-- Terapeuta da Clínica -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#1e3a5f">🧑‍⚕️</div>
<div>
<div class="card-title">Terapeuta da Clínica</div>
<div class="card-subtitle">tenant.kind = 'clinic' / role = 'therapist'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (terapeuta@clinicax.com.br)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'tenant_member'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership: <span class="tree-val">Clínica X</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'clinic'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">entitlements</span> <span class="tree-val">via plano da clínica</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /therapist/*</span></div>
</div>
<div class="badges">
<span class="badge badge-green">role: therapist</span>
<span class="badge badge-blue">tenant: clinic</span>
<span class="badge badge-gray">entitlements da clínica</span>
</div>
<p class="note">Terapeuta vinculado a uma clínica. Seus entitlements vêm do plano do tenant (clínica), não de assinatura pessoal.</p>
</div>
<!-- ════════════════════════════════════════ TERAPEUTA INDEPENDENTE ══ -->
<div class="section-label">🧑‍💼 Terapeuta Independente</div>
<!-- Terapeuta Solo -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#064e3b">🧑‍💼</div>
<div>
<div class="card-title">Terapeuta Solo</div>
<div class="card-subtitle">tenant.kind = 'saas'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (terapeuta@gmail.com)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'tenant_member'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership: <span class="tree-val">Tenant Pessoal</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'saas'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-val">therapist_free | therapist_pro</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// entitlements via v_user_entitlements</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /therapist/*</span></div>
</div>
<div class="badges">
<span class="badge badge-green">role: therapist</span>
<span class="badge badge-gray">tenant: saas (pessoal)</span>
<span class="badge badge-gray">therapist_free</span>
<span class="badge badge-purple">therapist_pro</span>
</div>
<p class="note">Terapeuta autônomo sem clínica. Assina diretamente. Entitlements vêm de <code>v_user_entitlements</code> (assinatura pessoal).</p>
</div>
<!-- Terapeuta Solo + Clínica -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#1e3a5f">🔀</div>
<div>
<div class="card-title">Terapeuta Solo + Clínica</div>
<div class="card-subtitle">2 memberships / contexto switcher</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (terapeuta@gmail.com)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Membership A: <span class="tree-val">Tenant Pessoal</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'saas'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-val">therapist_pro</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership B: <span class="tree-val">Clínica X</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'clinic'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// switcher de contexto no topbar</span></div>
</div>
<div class="badges">
<span class="badge badge-green">role: therapist</span>
<span class="badge badge-blue">2 tenants</span>
<span class="badge badge-purple">therapist_pro</span>
<span class="badge badge-gray">switcher de contexto</span>
</div>
<p class="note">Atua em dois contextos. No tenant pessoal usa PRO. Na clínica usa os entitlements da clínica. Precisa de switcher de tenant no topbar.</p>
</div>
<!-- ════════════════════════════════════════ SUPERVISOR ══ -->
<div class="section-label">🎓 Supervisor (Fase 1 — novo)</div>
<!-- Supervisor Solo -->
<div class="card" style="border-color: #ca8a04">
<div class="card-header">
<div class="card-icon" style="background:#422006">🎓</div>
<div>
<div class="card-title">Supervisor Solo</div>
<div class="card-subtitle">tenant.kind = 'supervisor'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (supervisor@gmail.com)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'tenant_member'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership: <span class="tree-new">Tenant Supervisão (novo)</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">plano</span> <span class="tree-new">supervisor_free | supervisor_pro</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">max_supervisees</span> <span class="tree-new">3 | 20</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /supervisor/*</span></div>
</div>
<div class="badges">
<span class="badge badge-yellow">role: supervisor</span>
<span class="badge badge-yellow">tenant: supervisor</span>
<span class="badge badge-yellow">supervisor_free</span>
<span class="badge badge-orange">supervisor_pro</span>
</div>
<p class="note"><strong>Novo.</strong> Supervisor independente. Tem sua própria sala de supervisão. O plano define o limite de terapeutas supervisionados (<code>plans.max_supervisees</code>).</p>
</div>
<!-- Terapeuta + Supervisor -->
<div class="card" style="border-color: #ca8a04">
<div class="card-header">
<div class="card-icon" style="background:#422006">🔀🎓</div>
<div>
<div class="card-title">Terapeuta + Supervisor</div>
<div class="card-subtitle">2 tenants / 2 papéis</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (terapeuta@gmail.com)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Membership A: <span class="tree-val">Tenant Pessoal</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'saas'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-val">therapist_pro</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership B: <span class="tree-new">Tenant Supervisão (novo)</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-new">supervisor_pro</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// switcher: "Meu consultório" / "Minha supervisão"</span></div>
</div>
<div class="badges">
<span class="badge badge-green">therapist</span>
<span class="badge badge-yellow">supervisor</span>
<span class="badge badge-blue">2 tenants</span>
<span class="badge badge-purple">therapist_pro</span>
<span class="badge badge-orange">supervisor_pro</span>
</div>
<p class="note"><strong>O caso mais comum.</strong> Atua como terapeuta no tenant pessoal e como supervisor no tenant de supervisão. Switcher de contexto no topbar.</p>
</div>
<!-- Terapeuta (clínica) + Supervisor -->
<div class="card" style="border-color: #ca8a04">
<div class="card-header">
<div class="card-icon" style="background:#422006">🏥🎓</div>
<div>
<div class="card-title">Terapeuta (Clínica) + Supervisor</div>
<div class="card-subtitle">3 tenants possíveis</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (terapeuta@clinicax.com.br)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Membership A: <span class="tree-val">Clínica X</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-val">'clinic'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">role</span> <span class="tree-val">'therapist'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Membership B: <span class="tree-new">Tenant Supervisão (novo)</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">tenant.kind</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">role</span> <span class="tree-new">'supervisor'</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-key">plano</span> <span class="tree-new">supervisor_free | supervisor_pro</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// supervisão é INDEPENDENTE da clínica</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// colegas da clínica podem ser supervisionados</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// via convite no tenant de supervisão</span></div>
</div>
<div class="badges">
<span class="badge badge-green">therapist (clínica)</span>
<span class="badge badge-yellow">supervisor (independente)</span>
<span class="badge badge-orange">supervisor_pro</span>
</div>
<p class="note">Trabalha na clínica como terapeuta <strong>e</strong> supervisiona outros terapeutas (inclusive colegas da clínica) de forma independente. A clínica não interfere na supervisão.</p>
</div>
<!-- ════════════════════════════════════════ FASE 2 ══ -->
<div class="section-label">🚀 Fase 2 — Marketplace de Supervisão</div>
<!-- Clínica com Supervisor Associado -->
<div class="card" style="border-color: #475569; opacity: .8">
<div class="card-header">
<div class="card-icon" style="background:#1e293b">🏥🤝🎓</div>
<div>
<div class="card-title">Clínica com Supervisor Contratado</div>
<div class="card-subtitle">repasse financeiro AgenciaPsi</div>
</div>
<span class="phase phase-2">Fase 2</span>
</div>
<div class="tree">
<div class="tree-root">Clínica X</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Ativa módulo <span class="tree-key">supervisao</span> (feature)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Associa supervisor externo via convite</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> Sessões registradas na plataforma</div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Pagamento via AgenciaPsi</div>
<div>&nbsp;</div>
<div class="tree-root">Fluxo financeiro</div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> Clínica paga <span class="tree-warn">R$ 200/sessão</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">├──</span> Supervisor recebe <span class="tree-val">R$ 180</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-branch">└──</span> AgenciaPsi retém <span class="tree-warn">R$ 20 (10%)</span></div>
</div>
<div class="badges">
<span class="badge badge-pink">split de pagamento</span>
<span class="badge badge-gray">Stripe Connect / Iugu</span>
<span class="badge badge-red">Fase 2</span>
</div>
<p class="note"><strong>Futuro.</strong> A clínica contrata supervisão via plataforma. AgenciaPsi faz o split automático. Requer gateway com marketplace split (Stripe Connect, Iugu, Pagar.me).</p>
</div>
<!-- ════════════════════════════════════════ PACIENTE ══ -->
<div class="section-label">👤 Paciente / Portal</div>
<!-- Paciente -->
<div class="card">
<div class="card-header">
<div class="card-icon" style="background:#500724">👤</div>
<div>
<div class="card-title">Paciente</div>
<div class="card-subtitle">profiles.role = 'portal_user'</div>
</div>
<span class="phase phase-1">Fase 1</span>
</div>
<div class="tree">
<div class="tree-root">Usuário (paciente@gmail.com)</div>
<div>&nbsp;&nbsp;<span class="tree-branch">├──</span> <span class="tree-key">profiles.role</span> <span class="tree-val">'portal_user'</span></div>
<div>&nbsp;&nbsp;<span class="tree-branch">└──</span> <span class="tree-comment">// sem memberships de tenant</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// acessa /portal/*</span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="tree-comment">// identidade global, não tenant</span></div>
</div>
<div class="badges">
<span class="badge badge-pink">role: portal_user</span>
<span class="badge badge-gray">sem tenant</span>
</div>
<p class="note">Acessa apenas o portal do paciente. Vê suas sessões, agenda e documentos. Nunca entra na área de tenant-app.</p>
</div>
</div><!-- /grid -->
<!-- ── LEGEND ── -->
<div class="legend">
<h3>Legenda</h3>
<div class="legend-grid">
<div class="legend-item"><div class="legend-dot" style="background:#facc15"></div> Novo — Fase 1 (supervisor)</div>
<div class="legend-item"><div class="legend-dot" style="background:#6ee7b7"></div> Existente — Fase 1</div>
<div class="legend-item"><div class="legend-dot" style="background:#fb923c"></div> Planejado — Fase 2</div>
<div class="legend-item"><div class="legend-dot" style="background:#a5b4fc"></div> Plataforma (sem tenant)</div>
<div class="legend-item"><div class="legend-dot" style="background:#94a3b8"></div> profiles.role → identidade global</div>
<div class="legend-item"><div class="legend-dot" style="background:#f9a8d4"></div> memberships.role → contexto de tenant</div>
</div>
</div>
<!-- ── RESUMO TÉCNICO ── -->
<div class="legend" style="margin-top: 1rem">
<h3>Resumo Técnico — Como o Guard decide o menu</h3>
<div style="font-family: monospace; font-size: .8rem; line-height: 2; color: #94a3b8;">
<div><span style="color:#a5b4fc">profiles.role</span> = identidade global (saas_admin | tenant_member | portal_user)</div>
<div><span style="color:#6ee7b7">memberships.role</span> = papel dentro do tenant (clinic_admin | therapist | supervisor | editor)</div>
<div><span style="color:#f9a8d4">tenant.kind</span> = tipo do tenant (clinic | saas | supervisor) → define qual menu e contexto</div>
<div><span style="color:#fde047">plans.target</span> = para quem é o plano (clinic | therapist | supervisor)</div>
<div><span style="color:#fdba74">plans.max_supervisees</span> = limite de supervisionados (novo — Fase 1)</div>
<div style="margin-top:.5rem; color: #475569">
Entitlements: v_tenant_entitlements (plano do tenant) UNION v_user_entitlements (assinatura pessoal)
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,556 @@
# Sistema de Lembretes Automáticos — WhatsApp, E-mail, SMS
> Agência PSI — Arquitetura completa
> Data: 2026-03-21
> Autor: Leonardo Nohama
---
## Sumário
1. [Decisão de Provedor WhatsApp](#1-decisão-de-provedor-whatsapp)
2. [Modelagem do Banco de Dados](#2-modelagem-do-banco-de-dados)
3. [Lógica de Agendamento (Cron + Edge Functions)](#3-lógica-de-agendamento)
4. [Integração Evolution API (MVP)](#4-integração-evolution-api-mvp)
5. [Integração API Oficial Meta (Escala)](#5-integração-api-oficial-meta)
6. [Frontend: Telas de Configuração](#6-frontend-telas-de-configuração)
7. [LGPD e Boas Práticas](#7-lgpd-e-boas-práticas)
8. [Diagrama do Fluxo Completo](#8-diagrama-do-fluxo-completo)
9. [Checklist de Produção](#9-checklist-de-produção)
---
## 1. Decisão de Provedor WhatsApp
### Comparativo de Provedores
| Critério | Evolution API | WPPConnect | Z-API (grátis) | Z-API Pro | Twilio (Meta oficial) | 360dialog (Meta oficial) | Zenvia (Meta oficial) |
|----------|--------------|------------|----------------|-----------|----------------------|-------------------------|----------------------|
| **Tipo** | Não-oficial (baileys) | Não-oficial | Não-oficial | Não-oficial (infra deles) | API Oficial Meta | API Oficial Meta | API Oficial Meta |
| **Custo mensal** | R$ 0 (self-hosted) | R$ 0 (self-hosted) | R$ 0 (limite de msgs) | R$ 99299/mês | ~R$ 0.30/msg (conversa) | €49/mês + msg | R$ 0.150.40/msg |
| **Setup** | 24h (Docker) | 48h | 30min (SaaS) | 1h (SaaS) | 12 semanas (aprovação) | 35 dias | 35 dias |
| **Risco de ban** | **MÉDIO-ALTO** | ALTO | MÉDIO | MÉDIO | **ZERO** | **ZERO** | **ZERO** |
| **Templates Meta** | ❌ Não suporta | ❌ | ❌ | ❌ | ✅ Obrigatório | ✅ Obrigatório | ✅ Obrigatório |
| **Webhooks status** | ✅ Completo | ✅ Parcial | ✅ | ✅ | ✅ Completo | ✅ Completo | ✅ Completo |
| **Multi-instância** | ✅ Nativo | ❌ Manual | ❌ | ✅ | ✅ Via WABA | ✅ | ✅ |
| **Uptime SLA** | Depende de você | Depende de você | 99.5% | 99.5% | 99.95% | 99.9% | 99.9% |
| **Escalabilidade** | ~500 msgs/dia seguro | ~300 msgs/dia | ~200 msgs/dia | ~2000 msgs/dia | Ilimitado | Ilimitado | Ilimitado |
### Recomendação
**MVP (0100 clínicas):** Evolution API self-hosted
- Custo zero, setup rápido, suficiente para validar o produto
- Cada terapeuta conecta seu próprio número via QR Code
- Limite prático: ~500 mensagens/dia por número
- Mitigação de ban: mensagens personalizadas (não genéricas), intervalos entre envios,
máximo 2 lembretes por sessão
**Escala (100+ clínicas):** Migrar para API Oficial da Meta via 360dialog ou Twilio
- Zero risco de banimento
- Templates aprovados pela Meta = entrega garantida
- Custo previsível por conversa (~R$ 0.250.40 por conversa de 24h)
- Suporte a botões interativos (confirmar/cancelar)
**Estratégia de migração:** O sistema será projetado com abstração de provedor desde o início.
A tabela `notification_channels` registra qual provedor cada tenant usa. Trocar de Evolution
para Meta oficial = mudar o `provider` e credenciais, sem alterar a fila ou templates.
---
## 2. Modelagem do Banco de Dados
> **Nota de integração:** O sistema existente já possui:
> - `email_templates_global` / `email_templates_tenant` → serão estendidos (não duplicados)
> - `notifications` → continuam para notificações in-app (realtime)
> - `profiles.notify_reminders` → será respeitado como opt-out global
> - `TEMPLATE_CHANNELS` em emailTemplateConstants.js → já prevê whatsapp/sms
### Relação entre tabelas novas e existentes
```
┌─────────────────────────────────────────────────────────────────┐
│ EXISTENTES (não alterar) │
│ │
│ email_templates_global ──→ templates de email (11 seeds) │
│ email_templates_tenant ──→ overrides por tenant/owner │
│ notifications ──→ notificações in-app (realtime) │
│ profiles ──→ notify_reminders, notify_system_email│
│ agenda_eventos ──→ sessões com patient_id, inicio_em │
│ patients ──→ nome_completo, telefone, email │
├─────────────────────────────────────────────────────────────────┤
│ NOVAS TABELAS │
│ │
│ notification_channels ──→ config WhatsApp/SMS por tenant │
│ notification_templates ──→ templates multi-canal (wpp/sms) │
│ notification_queue ──→ fila de envio │
│ notification_logs ──→ histórico completo │
│ notification_preferences ──→ opt-in/opt-out por paciente │
│ notification_schedules ──→ regras de quando disparar │
└─────────────────────────────────────────────────────────────────┘
```
---
## 3. Lógica de Agendamento
### Fluxo Completo
```
pg_cron (a cada 5 min)
├──→ populate_notification_queue() ← PL/pgSQL function
│ Busca agenda_eventos com inicio_em futuro,
│ cruza com notification_schedules ativas,
│ verifica notification_preferences do paciente,
│ insere na notification_queue com idempotency_key
└──→ HTTP call → Edge Function: process-notification-queue
├── Busca itens pendentes (status = 'pendente', scheduled_at <= now())
├── Marca como 'processando' (lock otimista via updated_at)
├── Resolve variáveis do template
├── Despacha para o provedor correto:
│ ├── WhatsApp → Evolution API ou Meta API
│ ├── Email → Resend / SendGrid / SMTP
│ └── SMS → Zenvia / Twilio
├── Atualiza status → 'enviado' ou 'falhou'
├── Insere em notification_logs
└── Em caso de falha: agenda retry exponencial
```
### Retry Exponencial
```
Tentativa 1: imediato
Tentativa 2: +5 minutos
Tentativa 3: +15 minutos
Tentativa 4: +60 minutos
Tentativa 5: +4 horas
Máximo: 5 tentativas → marca como 'falhou' definitivamente
```
### Prevenção de Duplicatas
1. **Idempotency key** = `{agenda_evento_id}:{schedule_key}:{canal}:{data_sessao}`
2. **UNIQUE constraint** na notification_queue sobre idempotency_key
3. **Lock otimista** no processamento: `UPDATE ... WHERE status = 'pendente' AND updated_at = ?`
4. **pg_cron não overlap**: usa `pg_try_advisory_lock()` no populate
---
## 4. Integração Evolution API (MVP)
### Setup Docker
```yaml
# docker-compose.evolution.yml
version: '3.8'
services:
evolution-api:
image: atendai/evolution-api:latest
ports:
- "8080:8080"
environment:
- AUTHENTICATION_API_KEY=sua_chave_global_aqui
- DATABASE_PROVIDER=postgresql
- DATABASE_CONNECTION_URI=postgresql://user:pass@host:5432/evolution
- WEBHOOK_GLOBAL_URL=https://seu-dominio.com/api/webhooks/evolution
- WEBHOOK_GLOBAL_ENABLED=true
- WEBHOOK_EVENTS_STATUS_INSTANCE=true
- WEBHOOK_EVENTS_MESSAGES_UPSERT=true
- WEBHOOK_EVENTS_SEND_MESSAGE=true
volumes:
- evolution_data:/evolution/store
restart: unless-stopped
volumes:
evolution_data:
```
### Endpoints Necessários
```
Base URL: https://evolution.seudominio.com
POST /instance/create → criar instância para o tenant
GET /instance/connect/{name} → obter QR Code para conectar número
GET /instance/connectionState/{name} → verificar status da conexão
POST /message/sendText/{name} → enviar mensagem de texto
POST /message/sendMedia/{name} → enviar com mídia (opcional)
DELETE /instance/delete/{name} → remover instância
```
### Payload de Envio
```json
// POST /message/sendText/{instance_name}
{
"number": "5516999887766",
"text": "Olá Ana Clara! 👋\n\nLembrete: você tem sessão amanhã, 21/03, às 14:00 com Dra. Beatriz Costa.\n\n📍 Online via Google Meet\n🔗 https://meet.google.com/abc-defg-hij\n\nPara confirmar, responda OK.\nPara cancelar, responda CANCELAR.\n\nAgência PSI"
}
```
### Webhook de Status
```json
// POST /api/webhooks/evolution (recebido do Evolution)
{
"event": "messages.update",
"instance": "clinica_abc",
"data": {
"key": {
"remoteJid": "5516999887766@s.whatsapp.net",
"id": "3EB0A0B6F..."
},
"update": {
"status": 3 // 1=pendente, 2=enviado ao servidor, 3=entregue, 4=lido
}
}
}
```
### Credenciais no Supabase
Armazenadas na tabela `notification_channels.credentials` como JSONB criptografado:
```json
{
"api_url": "https://evolution.seudominio.com",
"api_key": "chave_global_evolution",
"instance_name": "clinica_dr_beatriz",
"connected_number": "5516999887766",
"connection_status": "open"
}
```
> A criptografia das credenciais usa `pgcrypto` com chave armazenada como
> variável de ambiente do Supabase (Vault). Veja a função SQL `encrypt_credentials()`.
---
## 5. Integração API Oficial Meta
### Template de Lembrete para Aprovação
Nome: `session_reminder_v1`
Categoria: `UTILITY`
Idioma: `pt_BR`
```
HEADER: 📋 Lembrete de Sessão
BODY: Olá {{1}}! Sua sessão com {{2}} está agendada para {{3}} às {{4}}.
Modalidade: {{5}}
BUTTONS:
[quick_reply] ✅ Confirmar presença
[quick_reply] ❌ Preciso cancelar
FOOTER: Agência PSI — Tecnologia aplicada à escuta
```
### Envio via Graph API
```
POST https://graph.facebook.com/v19.0/{phone_number_id}/messages
Authorization: Bearer {access_token}
Content-Type: application/json
{
"messaging_product": "whatsapp",
"to": "5516999887766",
"type": "template",
"template": {
"name": "session_reminder_v1",
"language": { "code": "pt_BR" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Ana Clara" },
{ "type": "text", "text": "Dra. Beatriz Costa" },
{ "type": "text", "text": "21/03/2026" },
{ "type": "text", "text": "14:00" },
{ "type": "text", "text": "Online" }
]
}
]
}
}
```
### Webhook de Status (Meta)
```json
// POST /api/webhooks/meta-whatsapp
{
"entry": [{
"changes": [{
"value": {
"statuses": [{
"id": "wamid.HBgN...",
"status": "delivered", // sent, delivered, read, failed
"timestamp": "1711036800",
"recipient_id": "5516999887766",
"errors": []
}]
}
}]
}]
}
```
---
## 6. Frontend: Telas de Configuração
### 6.1 Configuração de Canal (`ConfiguracoesCanaisPage.vue`)
```
┌─────────────────────────────────────────────────┐
│ 📡 Canais de Notificação │
├─────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 💬 WhatsApp [Ativo ✅] │ │
│ │ Provedor: Evolution API │ │
│ │ Número: +55 16 99988-7766 │ │
│ │ Status: 🟢 Conectado │ │
│ │ │ │
│ │ [Reconectar] [Ver QR Code] [Testar] │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 📧 E-mail [Ativo ✅] │ │
│ │ Provedor: Resend │ │
│ │ Remetente: clinica@drbeat... │ │
│ │ Status: 🟢 Verificado │ │
│ │ │ │
│ │ [Configurar SMTP] [Testar] │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ 📱 SMS [Inativo ⬜] │ │
│ │ Não configurado │ │
│ │ │ │
│ │ [Ativar] │ │
│ └──────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
```
### 6.2 Templates (`ConfiguracoesNotifTemplatesPage.vue`)
- Lista de templates por domínio (Sessão, Triagem, Sistema)
- Preview ao vivo com variáveis mock (já existe no sistema)
- Cada template pode ser personalizado por canal (email / whatsapp / sms)
- Herança: se o tenant não customizou, mostra o template global com badge "Padrão"
### 6.3 Regras de Envio (`ConfiguracoesNotifRegrasPage.vue`)
```
┌──────────────────────────────────────────────┐
│ ⏰ Regras de Envio de Lembretes │
├──────────────────────────────────────────────┤
│ │
│ Lembrete de Sessão │
│ ├── 24 horas antes [WhatsApp ✅] [Email ✅] │
│ ├── 2 horas antes [WhatsApp ✅] [Email ⬜] │
│ └── 30 min antes [WhatsApp ⬜] [Email ⬜] │
│ │
│ Confirmação de Sessão │
│ ├── Imediata (ao criar) [Email ✅] │
│ └── Imediata [WhatsApp ✅] │
│ │
│ Cancelamento │
│ └── Imediata [WhatsApp ✅] [Email ✅]│
│ │
│ Boas-vindas (novo paciente) │
│ └── Imediata [WhatsApp ✅] [Email ✅]│
│ │
│ ⚙️ Horário permitido: 08:00 20:00 │
│ 📅 Não enviar: Domingos e feriados │
│ │
│ [Salvar configurações] │
└──────────────────────────────────────────────┘
```
### 6.4 Logs de Envio (`ConfiguracoesNotifLogsPage.vue`)
- Tabela paginada com filtros por canal, status, período
- Detalhes por envio: template usado, variáveis resolvidas, resposta do provedor
- Estatísticas: total enviado, entregue, lido, falhou (últimos 30 dias)
- Export CSV
### 6.5 Opt-out pelo Paciente
- **WhatsApp:** Paciente responde "SAIR" → webhook captura → atualiza `notification_preferences`
- **Email:** Link "Cancelar inscrição" no rodapé → página pública de opt-out
- **Portal do paciente (futuro):** Toggle na área logada do paciente
---
## 7. LGPD e Boas Práticas
### Coleta de Consentimento
1. **Cadastro externo** (CadastroPacienteExterno.vue): já tem checkbox LGPD
2. **Cadastro pelo terapeuta**: adicionar checkbox "Paciente autoriza receber lembretes por WhatsApp/E-mail"
3. **Primeiro lembrete**: incluir mensagem "Responda SAIR a qualquer momento para parar de receber mensagens"
### Armazenamento Seguro
- Telefone do paciente: armazenado na tabela `patients.telefone` (já existe)
- Credenciais do provedor: `notification_channels.credentials` criptografado com `pgcrypto`
- Chave de criptografia: Supabase Vault (variável de ambiente, nunca no código)
### Retenção de Logs
- `notification_logs`: reter por **2 anos** (exigência legal para comprovação de comunicação)
- `notification_queue`: limpar itens processados após **90 dias** (via pg_cron)
- `notification_preferences`: manter enquanto o paciente estiver ativo
### Opt-out Imediato
- Resposta "SAIR" no WhatsApp → webhook → `notification_preferences.whatsapp_opt_in = false`
- Efeito imediato: todas as mensagens pendentes na fila para aquele paciente são canceladas
- Trigger SQL: ao atualizar opt-out, cancela itens pendentes na queue
---
## 8. Diagrama do Fluxo Completo
```
┌──────────────┐
│ agenda_eventos│ ← terapeuta cria/edita sessão
└──────┬───────┘
┌──────────────────────────────────────┐
│ pg_cron: populate_notification_queue │ ← roda a cada 5 min
│ │
│ 1. Busca sessões com inicio_em │
│ entre agora e +48h │
│ 2. Cruza com notification_schedules │
│ (ex: 24h antes, 2h antes) │
│ 3. Verifica: │
│ - notification_preferences (opt-in)│
│ - notification_channels (canal ativo)│
│ - profiles.notify_reminders │
│ - agenda_eventos.status ≠ cancelado│
│ 4. Gera idempotency_key │
│ 5. INSERT INTO notification_queue │
│ ON CONFLICT DO NOTHING │
└──────────────┬───────────────────────┘
┌──────────────────────────────────────┐
│ pg_cron: chama Edge Function │ ← roda a cada 5 min (offset 2min)
│ POST /functions/v1/process-notif-queue│
└──────────────┬───────────────────────┘
┌──────────────────────────────────────┐
│ Edge Function: process-notif-queue │
│ │
│ 1. SELECT ... FROM notification_queue│
│ WHERE status = 'pendente' │
│ AND scheduled_at <= now() │
│ LIMIT 50 │
│ FOR UPDATE SKIP LOCKED │
│ │
│ 2. Para cada item: │
│ a. Marca 'processando' │
│ b. Resolve template + variáveis │
│ c. Despacha para provedor: │
│ ┌──────────────────────┐ │
│ │ channel = 'whatsapp' │ │
│ │ → Evolution API │ │
│ │ ou Meta Graph API │ │
│ ├──────────────────────┤ │
│ │ channel = 'email' │ │
│ │ → Resend / SMTP │ │
│ ├──────────────────────┤ │
│ │ channel = 'sms' │ │
│ │ → Zenvia / Twilio │ │
│ └──────────────────────┘ │
│ d. Atualiza status │
│ e. INSERT notification_logs │
│ │
│ 3. Itens com falha: │
│ attempts += 1 │
│ next_retry_at = exponential │
│ status = attempts >= 5 │
│ ? 'falhou' : 'pendente' │
└──────────────────────────────────────┘
┌──────────────────────────────────────┐
│ Webhook Handler │
│ POST /functions/v1/notif-webhook │
│ │
│ Recebe status do provedor: │
│ - Evolution: messages.update │
│ - Meta: webhook de status │
│ - Email: bounce/delivery events │
│ │
│ Atualiza notification_logs: │
│ - delivered_at, read_at, failed_at │
│ - provider_status, provider_response │
│ │
│ Se resposta = "SAIR": │
│ → UPDATE notification_preferences │
│ SET whatsapp_opt_in = false │
│ → CANCEL pendentes na queue │
└──────────────────────────────────────┘
```
---
## 9. Checklist de Produção
### Infraestrutura
- [ ] Subir Evolution API (Docker) em VPS dedicada
- [ ] Configurar domínio + SSL para Evolution API
- [ ] Configurar Supabase Vault com chave de criptografia
- [ ] Habilitar extensão `pgcrypto` no Supabase
- [ ] Habilitar extensão `pg_cron` no Supabase
- [ ] Configurar DNS para webhook (ex: `webhooks.agenciapsi.com.br`)
### Banco de Dados
- [ ] Rodar migration: tabelas de notificação
- [ ] Rodar migration: functions PL/pgSQL (populate queue, encrypt/decrypt)
- [ ] Configurar pg_cron jobs (populate + process)
- [ ] Verificar RLS policies em todas as tabelas
- [ ] Seed: notification_schedules padrão (24h, 2h)
- [ ] Seed: notification_templates padrão (whatsapp + sms)
### Edge Functions
- [ ] Deploy: `process-notif-queue`
- [ ] Deploy: `notif-webhook`
- [ ] Configurar secrets: `EVOLUTION_API_KEY`, `ENCRYPTION_KEY`
- [ ] Testar com payload simulado
### Frontend
- [ ] Tela de configuração de canais
- [ ] Tela de templates (estender ConfiguracoesEmailTemplatesPage existente)
- [ ] Tela de regras de envio
- [ ] Tela de logs
- [ ] Adicionar consentimento no cadastro de paciente
- [ ] Adicionar opt-out no rodapé de emails
### Testes
- [ ] Teste E2E: criar sessão → verificar queue populada → verificar envio
- [ ] Teste: opt-out WhatsApp → verificar cancelamento na queue
- [ ] Teste: retry após falha → verificar exponential backoff
- [ ] Teste: idempotency → rodar populate 2x → verificar sem duplicatas
- [ ] Teste: tenant isolation → verificar RLS
- [ ] Teste de carga: 1000 mensagens na queue → medir throughput
### Monitoramento
- [ ] Alerta se queue > 500 itens pendentes
- [ ] Alerta se taxa de falha > 10% em 1h
- [ ] Dashboard de métricas (envios/dia, taxa de entrega, tempo médio de processamento)
- [ ] Log de erros no Supabase Logs
---
*Documento gerado como parte da arquitetura do sistema Agência PSI.*
*As implementações SQL e JavaScript estão nos arquivos separados referenciados abaixo.*

View File

@@ -0,0 +1,672 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Documento Mestre — Billing (Plans, Pricing, Subscriptions, Entitlements) v2.0 | Agência PSI</title>
<style>
:root{
--bg0:#f6f8fc;
--bg1:#eef2f8;
--panel:rgba(255,255,255,.78);
--panel2:rgba(255,255,255,.92);
--border:rgba(15,23,42,.10);
--text:rgba(15,23,42,.92);
--muted:rgba(15,23,42,.70);
--muted2:rgba(15,23,42,.56);
--accent:#2563eb;
--accent2:#4f46e5;
--warn:#b45309;
--danger:#b91c1c;
--ok:#047857;
--shadow: 0 18px 60px rgba(2,6,23,.10);
--radius: 16px;
--radius2: 22px;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
*{box-sizing:border-box;}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
background: radial-gradient(1200px 600px at 10% 0%, rgba(79,70,229,.10), transparent 55%),
radial-gradient(1100px 500px at 90% 10%, rgba(37,99,235,.10), transparent 55%),
linear-gradient(180deg, var(--bg0), var(--bg1));
color:var(--text);
}
a{color:inherit; text-decoration:none;}
a:hover{text-decoration:underline;}
.layout{
display:grid;
grid-template-columns: 320px 1fr;
gap: 20px;
max-width: 1320px;
margin: 0 auto;
padding: 28px 18px 42px;
}
header{
grid-column: 1 / -1;
padding: 18px;
border: 1px solid var(--border);
background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(255,255,255,.72));
border-radius: var(--radius2);
box-shadow: var(--shadow);
}
.kicker{
font-size:12px;
letter-spacing:.08em;
text-transform:uppercase;
color:var(--muted2);
margin:0 0 8px;
}
h1{
margin:0 0 8px;
font-size:30px;
letter-spacing:-0.02em;
}
.subtitle{
margin:0;
color:var(--muted);
max-width:980px;
line-height:1.55;
font-size:14px;
}
.meta-row{
margin-top:12px;
display:flex;
flex-wrap:wrap;
gap:10px;
align-items:center;
}
.pill{
display:inline-flex;
align-items:center;
gap:8px;
padding:8px 12px;
border-radius:999px;
border:1px solid var(--border);
background:rgba(255,255,255,.72);
color:var(--muted);
font-size:12px;
}
.dot{
width:8px;height:8px;border-radius:50%;
background:var(--accent);
box-shadow:0 0 0 4px rgba(37,99,235,.12);
}
aside{
position:sticky;
top:18px;
align-self:start;
border:1px solid var(--border);
background:var(--panel2);
border-radius:var(--radius);
box-shadow:var(--shadow);
overflow:hidden;
}
.toc-head{
padding:14px;
border-bottom:1px solid var(--border);
background:rgba(15,23,42,.02);
}
.toc-title{ margin:0 0 6px; font-weight:700; font-size:14px; }
.toc-sub{ margin:0; color:var(--muted); font-size:12px; line-height:1.45; }
nav{ padding: 10px 6px 14px; }
nav a{
display:block;
padding:10px 12px;
margin:4px 6px;
border-radius:12px;
color:var(--muted);
font-size:13px;
border:1px solid transparent;
}
nav a:hover{
background:rgba(37,99,235,.06);
border-color:rgba(37,99,235,.12);
color:var(--text);
text-decoration:none;
}
.nav-sec{
margin:10px 12px 6px;
color:var(--muted2);
font-size:11px;
letter-spacing:.08em;
text-transform:uppercase;
}
main{
border:1px solid var(--border);
background:var(--panel);
border-radius:var(--radius2);
box-shadow:var(--shadow);
overflow:hidden;
}
.content{ padding: 18px 18px 22px; }
.section{
padding: 18px;
border:1px solid var(--border);
border-radius: var(--radius2);
background: rgba(255,255,255,.80);
margin-bottom: 16px;
}
.section h2{
margin:0 0 10px;
font-size:18px;
letter-spacing:-0.01em;
}
.section h3{
margin:14px 0 8px;
font-size:14px;
}
.section p, .section li{
color:var(--muted);
line-height:1.65;
font-size:13.5px;
}
.grid{
display:grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 980px){
.layout{ grid-template-columns: 1fr; }
aside{ position:relative; top:auto; }
.grid{ grid-template-columns: 1fr; }
}
.callout{
border-radius: 16px;
padding: 12px 12px 12px 14px;
border:1px solid var(--border);
background: rgba(15,23,42,.02);
margin-top: 10px;
}
.callout strong{color:var(--text);}
.callout.ok{ border-left: 4px solid var(--ok); background: rgba(4,120,87,.06); }
.callout.warn{ border-left: 4px solid var(--warn); background: rgba(180,83,9,.08); }
.callout.danger{ border-left: 4px solid var(--danger); background: rgba(185,28,28,.08); }
.callout.info{ border-left: 4px solid var(--accent); background: rgba(37,99,235,.08); }
pre{
margin: 10px 0 0;
padding: 14px;
background: #0b1220;
color:#e2e8f0;
border-radius: 16px;
overflow:auto;
border: 1px solid rgba(226,232,240,.08);
}
code{ font-family: var(--mono); font-size:12.5px; }
.kbd{
font-family: var(--mono);
font-size: 12px;
padding: 2px 6px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(255,255,255,.65);
color: var(--text);
}
.hr{
height:1px;
background: var(--border);
margin: 12px 0;
}
footer{
margin-top: 12px;
padding: 14px 18px 18px;
color: var(--muted2);
font-size: 12px;
border-top: 1px solid var(--border);
background: rgba(255,255,255,.70);
}
.small{ font-size:12px; color:var(--muted2); }
</style>
</head>
<body>
<div class="layout">
<header>
<p class="kicker">Documento Mestre • Billing • Agência PSI</p>
<h1>Plans, Pricing, Subscriptions & Entitlements — v2.0</h1>
<p class="subtitle">
Documento institucional do domínio <strong>Billing</strong>. Unifica: catálogo de planos, preços vigentes,
assinatura (clínica/terapeuta), guardrails e entitlements (features + limits). Este material é pensado para
reduzir regressões e orientar o operador/dev quando algo “parecer impossível” (ex.: corrigir plano core sem
desativar triggers).
</p>
<div class="meta-row">
<span class="pill"><span class="dot"></span><strong>Estado:</strong> operacional (MVP)</span>
<span class="pill"><strong>Atualizado:</strong> 2026-03-01 10:43:18 UTC</span>
<span class="pill"><strong>Padrão:</strong> Supabase + Postgres + Vue/PrimeVue</span>
</div>
</header>
<aside>
<div class="toc-head">
<div class="toc-title">Sumário</div>
<p class="toc-sub">Links rápidos para leitura e execução (SQL/Seeder/Views).</p>
</div>
<nav>
<div class="nav-sec">Visão geral</div>
<a href="#01">1. Contexto e objetivos</a>
<a href="#02">2. Entidades e conceitos</a>
<div class="nav-sec">Plans & Pricing</div>
<a href="#03">3. Tabela plans e planos core</a>
<a href="#04">4. Pricing e vigência (plan_prices + views)</a>
<div class="nav-sec">Subscriptions</div>
<a href="#05">5. Subscriptions: schema e regras</a>
<a href="#06">6. Views: active_subscription</a>
<a href="#07">7. Operações: change, cancel, reactivate</a>
<a href="#08">8. Auditoria: subscription_events</a>
<div class="nav-sec">Entitlements</div>
<a href="#09">9. Features + plan_features</a>
<a href="#10">10. Views de entitlements (com limits)</a>
<div class="nav-sec">Guardrails</div>
<a href="#11">11. Triggers de proteção</a>
<a href="#12">12. Correção segura de plano core (bypass controlado)</a>
<div class="nav-sec">Seeders</div>
<a href="#13">13. Seeder idempotente: features + plan_features</a>
<a href="#14">14. Seeder idempotente: subscription de teste</a>
<div class="nav-sec">Front-end</div>
<a href="#15">15. Padrões de UI e telas (Subscriptions / Eventos)</a>
<div class="nav-sec">Apêndices</div>
<a href="#16">16. Troubleshooting (erros reais)</a>
<a href="#17">17. Checklist de validação</a>
</nav>
</aside>
<main>
<div class="content">
<section class="section" id="01">
<h2>1. Contexto e objetivos</h2>
<p>
O MVP do SaaS precisa garantir que o sistema “respeite o plano”. Para isso, o domínio Billing opera em camadas:
<strong>Plans</strong> (catálogo), <strong>Pricing</strong> (vigência), <strong>Subscriptions</strong> (plano vigente por tenant/user)
e <strong>Entitlements</strong> (features + limites).
</p>
<div class="callout info">
<strong>Regra de ouro:</strong> o front nunca deve “inferir plano” por role. O plano vigente vem de <code>subscriptions</code>
e os limites/flags vêm de <code>plan_features</code>.
</div>
</section>
<section class="section" id="02">
<h2>2. Entidades e conceitos</h2>
<div class="grid">
<div class="callout">
<strong>plans</strong>
<p>Catálogo de planos (core e custom). Guarda <code>key</code>, <code>target</code>, preço base e metadados.</p>
</div>
<div class="callout">
<strong>plan_prices</strong>
<p>Preço com vigência. Preço vigente: <span class="kbd">is_active=true</span> e <span class="kbd">active_to is null</span>.</p>
</div>
<div class="callout">
<strong>subscriptions</strong>
<p>Assinatura ativa por tenant (clínica) ou por user (terapeuta). A view escolhe a mais recente por owner.</p>
</div>
<div class="callout">
<strong>features / plan_features</strong>
<p>Mapa de capabilities e limites (<code>limits jsonb</code>). É daqui que o front deve “gatear” menus/ações.</p>
</div>
</div>
</section>
<section class="section" id="03">
<h2>3. Tabela plans e planos core</h2>
<p><strong>Planos core do MVP</strong> (devem existir e permanecer): <code>clinic_free</code>, <code>clinic_pro</code>, <code>therapist_free</code>, <code>therapist_pro</code>.</p>
<pre><code>-- estrutura confirmada (resumo)
-- plans (public)
-- id, key, name, description, is_active, price_cents, currency, billing_interval, target</code></pre>
<div class="callout warn">
<strong>Observação importante:</strong> planos core têm guardrails: não podem ser deletados e sua <code>key</code> não pode ser alterada.
</div>
</section>
<section class="section" id="04">
<h2>4. Pricing e vigência</h2>
<p>
A UI pública de preços deve consumir <code>v_public_pricing</code>. A vigência de preço vem de <code>plan_prices</code>:
preço vigente é aquele com <code>is_active=true</code> e <code>active_to is null</code>. Para planos FREE, a UI pode exibir “Grátis”
mesmo sem registro em <code>plan_prices</code>.
</p>
<div class="callout info">
<strong>Boas práticas:</strong> a tela pública não deve depender do schema “cru”. Mantenha a view como contrato.
</div>
</section>
<section class="section" id="05">
<h2>5. Subscriptions: schema e regras</h2>
<p>Schema confirmado via <code>information_schema</code> e constraints:</p>
<pre><code>-- subscriptions (public) - colunas relevantes
id uuid primary key default gen_random_uuid()
tenant_id uuid null
user_id uuid null
plan_id uuid not null references plans(id) on delete restrict
status text not null default 'active'
"interval" text null check ("interval" in ('month','year'))
current_period_start timestamptz null
current_period_end timestamptz null
plan_key text null
provider text not null default 'manual'
source text not null default 'manual'
started_at timestamptz not null default now()
created_at timestamptz not null default now()
updated_at timestamptz not null default now()</code></pre>
<div class="callout ok">
<strong>Modelagem:</strong> clínica → usa <code>tenant_id</code>. Terapeuta → usa <code>user_id</code> (com <code>tenant_id</code> nulo).
</div>
</section>
<section class="section" id="06">
<h2>6. View: v_tenant_active_subscription</h2>
<p>
Esta view define “o plano vigente” do tenant. Regra: status <code>active</code> e período ainda válido.
Escolhe a assinatura mais recente por tenant (created_at DESC).
</p>
<pre><code>select distinct on (tenant_id)
tenant_id,
plan_id,
plan_key,
"interval",
status,
current_period_start,
current_period_end,
created_at
from subscriptions s
where tenant_id is not null
and status = 'active'
and (current_period_end is null or current_period_end &gt; now())
order by tenant_id, created_at desc;</code></pre>
<div class="callout info">
<strong>Diagnóstico rápido:</strong> se views de entitlements estiverem “vazias”, primeiro verifique se existe subscription ativa nesta view.
</div>
</section>
<section class="section" id="07">
<h2>7. Operações de assinatura: change / cancel / reactivate</h2>
<p>
O front chama RPCs, mantendo a regra de ouro: “a verdade vem do banco”.
Depois de operar, a tela recarrega para refletir o estado real.
</p>
<pre><code>-- RPCs usadas no front
-- change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid)
-- cancel_subscription(p_subscription_id uuid)
-- reactivate_subscription(p_subscription_id uuid)</code></pre>
<div class="callout warn">
<strong>Nota:</strong> se o RPC atualizar apenas <code>plan_id</code>, é recomendável manter <code>plan_key</code> e <code>interval</code>
consistentes (quando for relevante), para facilitar auditoria e debugging.
</div>
</section>
<section class="section" id="08">
<h2>8. Auditoria: subscription_events</h2>
<p>
Tela “Histórico de assinaturas” é read-only e mostra até 500 eventos mais recentes. Eventos típicos:
<code>plan_changed</code>, <code>canceled</code>, <code>reactivated</code>.
</p>
<div class="callout info">
<strong>UX operacional:</strong> o histórico deve permitir navegar de volta para o owner (Subscriptions) via query <code>?q=clinic:&lt;uuid&gt;</code>.
</div>
</section>
<section class="section" id="09">
<h2>9. Features e plan_features</h2>
<p>O MVP já possui <code>features</code> com keys (ex.: <code>online_scheduling</code>, <code>reports_basic</code>, etc.) e tabela <code>plan_features</code>:</p>
<pre><code>-- plan_features(plan_id uuid not null, feature_id uuid not null,
-- enabled boolean not null default true, limits jsonb null, created_at timestamptz default now())
-- PK: (plan_id, feature_id)
-- FK: feature_id → features(id) ON DELETE CASCADE
-- FK: plan_id → plans(id) ON DELETE CASCADE</code></pre>
<div class="callout ok">
<strong>Importante:</strong> <code>limits</code> é um contrato com o front. Ex.: <code>{"max_patients":30}</code>, <code>{"sessions_per_month":40}</code>.
</div>
</section>
<section class="section" id="10">
<h2>10. Views de entitlements (com limits)</h2>
<p>Para atender o front com 1 query, criamos views “full” e “json”.</p>
<h3>10.1 v_tenant_entitlements_full</h3>
<pre><code>create or replace view public.v_tenant_entitlements_full as
select
a.tenant_id,
f.key as feature_key,
(pf.enabled = true) as allowed,
pf.limits,
a.plan_id,
p.key as plan_key
from public.v_tenant_active_subscription a
join public.plan_features pf on pf.plan_id = a.plan_id
join public.features f on f.id = pf.feature_id
join public.plans p on p.id = a.plan_id;</code></pre>
<h3>10.2 v_tenant_entitlements_json</h3>
<pre><code>create or replace view public.v_tenant_entitlements_json as
select
tenant_id,
max(plan_key) as plan_key,
jsonb_object_agg(
feature_key,
jsonb_build_object(
'allowed', allowed,
'limits', coalesce(limits, '{}'::jsonb)
)
order by feature_key
) as entitlements
from public.v_tenant_entitlements_full
group by tenant_id;</code></pre>
<div class="callout info">
<strong>Uso no front:</strong> uma única consulta retorna <code>plan_key</code> + mapa de entitlements com limits.
</div>
</section>
<section class="section" id="11">
<h2>11. Triggers de proteção (Guardrails)</h2>
<p>Triggers confirmadas em <code>public.plans</code>:</p>
<pre><code>trg_no_delete_core_plans
trg_no_change_plan_target
trg_no_change_core_plan_key</code></pre>
<h3>11.1 Funções (versões base)</h3>
<pre><code>-- guard_no_delete_core_plans(): impede deletar planos core
-- guard_no_change_core_plan_key(): impede alterar key dos planos core
-- guard_no_change_plan_target(): impede alterar target de qualquer plano</code></pre>
<div class="callout warn">
<strong>Armadilha comum:</strong> tentar “corrigir plano core” via UPDATE direto. O trigger bloqueia e isso é desejável.
</div>
</section>
<section class="section" id="12">
<h2>12. Correção segura de plano core (bypass controlado)</h2>
<p>
Caso real desta sessão: <code>clinic_free</code> estava com <code>target</code> incorreto.
O objetivo foi corrigir sem “desligar guardrails”.
</p>
<h3>12.1 Patch do guardrail para bypass por transação</h3>
<pre><code>create or replace function public.guard_no_change_plan_target()
returns trigger
language plpgsql
as $$
declare
v_bypass text;
begin
v_bypass := current_setting('app.plan_migration_bypass', true);
if v_bypass = '1' then
return new;
end if;
if new.target is distinct from old.target then
raise exception 'Não é permitido alterar target do plano (%) de % para %.',
old.key, old.target, new.target
using errcode = 'P0001';
end if;
return new;
end
$$;</code></pre>
<h3>12.2 Função administrativa (SECURITY DEFINER)</h3>
<pre><code>create or replace function public.admin_fix_plan_target(
p_plan_key text,
p_new_target text
) returns void
language plpgsql
security definer
as $$
declare
v_plan_id uuid;
begin
if p_new_target not in ('clinic','therapist') then
raise exception 'Target inválido: %', p_new_target using errcode='P0001';
end if;
select id into v_plan_id
from public.plans
where key = p_plan_key
for update;
if v_plan_id is null then
raise exception 'Plano não encontrado: %', p_plan_key using errcode='P0001';
end if;
if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then
raise exception 'Plano % possui subscriptions. Migração bloqueada.', p_plan_key using errcode='P0001';
end if;
perform set_config('app.plan_migration_bypass', '1', true);
update public.plans
set target = p_new_target
where id = v_plan_id;
end
$$;</code></pre>
<h3>12.3 Execução (caso real)</h3>
<pre><code>select public.admin_fix_plan_target('clinic_free', 'clinic');</code></pre>
<div class="callout ok">
<strong>Resultado:</strong> plano core corrigido, guardrail permanece ativo. Bypass vale apenas na transação.
</div>
<h3>12.4 Hardening recomendado</h3>
<pre><code>revoke execute on function public.admin_fix_plan_target(text, text) from public;
-- depois conceder apenas ao role administrativo apropriado</code></pre>
</section>
<section class="section" id="13">
<h2>13. Seeder idempotente: features + plan_features</h2>
<p>
O banco já continha features. O mapeamento MVP de plan_features foi validado e segue a ideia:
PRO habilita tudo e limites “altos”; FREE habilita subset e limites menores.
</p>
<pre><code>-- padrão do seed (exemplo):
-- insert into features(key, descricao, description) values (...)
-- on conflict (key) do update set ...
-- insert into plan_features(plan_id, feature_id, enabled, limits) values (...)
-- on conflict (plan_id, feature_id) do update set enabled=excluded.enabled, limits=excluded.limits;</code></pre>
<div class="callout info">
<strong>Dica operacional:</strong> manter seed idempotente evita “duplicação” e reduz bugs em ambientes de teste.
</div>
</section>
<section class="section" id="14">
<h2>14. Seeder idempotente: subscription de teste</h2>
<p>Como as views dependem de uma subscription ativa, criamos uma assinatura manual de teste para um tenant real.</p>
<pre><code>insert into public.subscriptions (
tenant_id,
plan_id,
status,
plan_key,
"interval",
current_period_start,
current_period_end,
provider,
source
)
values (
'&lt;TENANT_UUID&gt;',
(select id from public.plans where key = 'clinic_free'),
'active',
'clinic_free',
'month',
now(),
null,
'manual',
'manual'
);</code></pre>
<div class="callout ok">
<strong>Validação:</strong> após inserir, <code>v_tenant_active_subscription</code> e <code>v_tenant_entitlements_json</code> devem retornar dados.
</div>
</section>
<section class="section" id="15">
<h2>15. Front-end: padrões e telas</h2>
<p><strong>Padrões adotados nesta sessão:</strong></p>
<ul>
<li>Em arquivos Vue: <strong>script</strong><strong>template</strong><strong>style</strong>.</li>
<li>Busca com <code>FloatLabel</code> + <code>IconField</code> + <code>InputIcon</code>.</li>
<li>Telas operacionais: DataTable com paginação, estados empty e UX “foco” via <code>?q=...</code>.</li>
</ul>
<div class="callout info">
<strong>Melhorias aplicadas:</strong> Cards para “foco”, botão voltar no topo, textos mais claros e layout mais estável.
</div>
</section>
<section class="section" id="16">
<h2>16. Troubleshooting (erros reais)</h2>
<h3>16.1 “Não é permitido alterar target do plano …”</h3>
<p>Causa: trigger <code>guard_no_change_plan_target</code>. Solução: bypass controlado + função admin (seção 12).</p>
<h3>16.2 “Não é permitido alterar a key do plano padrão …”</h3>
<p>Causa: trigger <code>guard_no_change_core_plan_key</code>. Solução: não renomear core; criar novo plano se necessário.</p>
<h3>16.3 Entitlements view vazia</h3>
<p>Causa: ausência de subscription ativa em <code>v_tenant_active_subscription</code>. Solução: inserir subscription de teste (seção 14).</p>
<div class="callout warn">
<strong>Lembrete:</strong> quando algo “não retorna nada”, primeiro verifique as views-base antes de mexer no front.
</div>
</section>
<section class="section" id="17">
<h2>17. Checklist de validação</h2>
<ul>
<li><strong>Plans:</strong> core existe e está ativo; targets corretos.</li>
<li><strong>Pricing:</strong> PRO tem preço vigente (active_to null); FREE pode ficar sem price.</li>
<li><strong>Subscriptions:</strong> existe ao menos 1 assinatura ativa para testar.</li>
<li><strong>Entitlements:</strong> <code>v_tenant_entitlements_json</code> retorna mapa com <code>allowed</code> + <code>limits</code>.</li>
<li><strong>Guardrails:</strong> triggers ativas; correção de core somente via função admin.</li>
<li><strong>Front:</strong> telas operacionais OK; foco via query; layout consistente.</li>
</ul>
<div class="callout ok">
<strong>Meta:</strong> com este checklist, qualquer dev/operador consegue diagnosticar Billing em minutos.
</div>
</section>
</div>
<footer>
<div><strong>Agência PSI — Documento Mestre Billing v2.0</strong></div>
<div class="small">Gerado em 2026-03-01 10:43:18 UTC. Estrutura inspirada no padrão interno com sidebar + anchors.</div>
</footer>
</main>
</div>
</body>
</html>

View File

@@ -0,0 +1,206 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Agência PSI — Billing & Subscriptions v1.2</title>
<style>
body{
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
margin:0;
padding:40px;
background:#f6f8fc;
color:#0f172a;
}
h1{font-size:28px;margin-bottom:8px;}
h2{margin-top:40px;font-size:20px;}
h3{margin-top:24px;font-size:16px;}
p{line-height:1.6;color:#334155;}
pre{
background:#0f172a;
color:#e2e8f0;
padding:16px;
border-radius:12px;
overflow:auto;
}
code{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;}
.section{margin-bottom:40px;}
.badge{
display:inline-block;
padding:4px 10px;
border-radius:999px;
background:#e2e8f0;
font-size:12px;
margin-right:6px;
}
.rule{
background:#e0f2fe;
padding:14px;
border-left:4px solid #0284c7;
border-radius:10px;
margin-top:12px;
}
.warn{
background:#fef3c7;
padding:14px;
border-left:4px solid #d97706;
border-radius:10px;
margin-top:12px;
}
.danger{
background:#fee2e2;
padding:14px;
border-left:4px solid #dc2626;
border-radius:10px;
margin-top:12px;
}
.ok{
background:#dcfce7;
padding:14px;
border-left:4px solid #16a34a;
border-radius:10px;
margin-top:12px;
}
footer{
margin-top:60px;
font-size:12px;
color:#64748b;
}
</style>
</head>
<body>
<h1>Billing & Subscriptions — v1.2</h1>
<p><strong>Agência PSI</strong> — Documento consolidado da sessão técnica sobre Subscriptions, Guardrails e Seeder.</p>
<div class="section">
<h2>1. Escopo desta versão</h2>
<p>Este documento consolida tudo o que foi tratado nesta sessão:</p>
<ul>
<li>Modelagem real da tabela <code>subscriptions</code></li>
<li>Histórico via <code>subscription_events</code></li>
<li>Triggers (guardrails) de proteção</li>
<li>Views oficiais</li>
<li>Seeder completo (planos + preços + metadata pública)</li>
<li>Erros reais encontrados e solução aplicada</li>
</ul>
</div>
<div class="section">
<h2>2. Estrutura confirmada — subscriptions</h2>
<pre><code>id uuid PK
tenant_id uuid NULL
user_id uuid NULL
plan_id uuid NOT NULL
plan_key text NULL
interval text CHECK ('month','year')
status text DEFAULT 'active'
current_period_start timestamptz
current_period_end timestamptz
provider text DEFAULT 'manual'
cancel_at_period_end boolean DEFAULT false
created_at timestamptz DEFAULT now()
updated_at timestamptz DEFAULT now()</code></pre>
<div class="rule">
Assinatura de clínica exige <strong>tenant_id</strong>.
Assinatura de terapeuta pode usar <strong>user_id</strong>.
</div>
</div>
<div class="section">
<h2>3. Guardrails (Proteções de Integridade)</h2>
<h3>3.1 Impedir deletar planos core</h3>
<pre><code>create or replace function guard_no_delete_core_plans()
returns trigger language plpgsql as $$
begin
if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then
raise exception 'Plano padrão (%) não pode ser removido.', old.key;
end if;
return old;
end $$;</code></pre>
<h3>3.2 Impedir alterar target</h3>
<pre><code>create or replace function guard_no_change_plan_target()
returns trigger language plpgsql as $$
begin
if new.target is distinct from old.target then
raise exception 'Não é permitido alterar target do plano.';
end if;
return new;
end $$;</code></pre>
<h3>3.3 Impedir alterar key core</h3>
<pre><code>create or replace function guard_no_change_core_plan_key()
returns trigger language plpgsql as $$
begin
if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro')
and new.key is distinct from old.key then
raise exception 'Não é permitido alterar a key do plano padrão.';
end if;
return new;
end $$;</code></pre>
<div class="warn">
Esses guardrails impediram alterações indevidas quando tentamos renomear planos core.
</div>
</div>
<div class="section">
<h2>4. Views Oficiais</h2>
<p><strong>v_public_pricing</strong> — Tela pública de preços.</p>
<p><strong>v_tenant_active_subscription</strong> — Plano vigente do tenant.</p>
<p><strong>v_subscription_health</strong> — Diagnóstico de inconsistências.</p>
</div>
<div class="section">
<h2>5. Seeder Oficial (MVP)</h2>
<pre><code>insert into plans (key,name,target,is_active)
values
('clinic_free','Clinic Free','clinic',true),
('clinic_pro','Clinic Pro','clinic',true),
('therapist_free','Therapist Free','therapist',true),
('therapist_pro','Therapist Pro','therapist',true)
on conflict (key) do update set
name=excluded.name,
target=excluded.target,
is_active=excluded.is_active;</code></pre>
<div class="ok">
Seeder é idempotente. Pode rodar múltiplas vezes sem duplicar.
</div>
</div>
<div class="section">
<h2>6. Incidentes reais resolvidos</h2>
<h3>6.1 Pricing retornando null</h3>
<p>Causa: não havia preço vigente (is_active=true e active_to is null).</p>
<h3>6.2 Erro ao alterar plano padrão</h3>
<p>Causa: trigger guard_no_change_core_plan_key bloqueando alteração.</p>
<h3>6.3 Assinatura sem tenant_id</h3>
<p>Causa: regra de negócio no banco impedindo clinic sem tenant.</p>
</div>
<div class="section">
<h2>7. Diretrizes finais</h2>
<ul>
<li>Plano nunca deve ser inferido do role.</li>
<li>UI deve consumir apenas views oficiais.</li>
<li>Plano core nunca deve ser renomeado.</li>
<li>Preço sempre deve ter vigência ativa.</li>
</ul>
</div>
<footer>
Agência PSI — Billing & Subscriptions v1.2<br>
Documento gerado automaticamente.
</footer>
</body>
</html>

View File

@@ -0,0 +1,308 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8"/>
<title>Arquitetura Técnica Completa — Billing & Assinaturas | Agência PSI</title>
<style>
body{font-family:Arial,Helvetica,sans-serif;margin:40px;line-height:1.65;color:#111}
h1,h2,h3{margin-top:36px}
code,pre{background:#f4f4f4;padding:12px;border-radius:8px;display:block;overflow:auto;font-size:13px}
.section{margin-bottom:40px}
.small{font-size:13px;color:#555}
ul{margin-left:20px}
.diagram{background:#fafafa;border:1px solid #ddd;padding:16px;border-radius:8px;font-family:monospace;font-size:13px}
</style>
</head>
<body>
<h1>Arquitetura Técnica Completa — Billing & Assinaturas</h1>
<p class="small">Projeto: Agência PSI • Documento estrutural definitivo do domínio de Billing.</p>
<div class="section">
<h2>1. Visão Arquitetural Geral</h2>
<div class="diagram">
USUÁRIO
subscription_intents (VIEW unificada)
▼ (RPC activate_subscription_from_intent)
subscriptions
subscription_events (auditoria)
entitlements (derivados do plano)
</div>
<p>Separação estrutural:</p>
<ul>
<li><strong>plans</strong> → catálogo</li>
<li><strong>plan_prices</strong> → preço versionado</li>
<li><strong>subscription_intents_*</strong> → intenção pré-pagamento</li>
<li><strong>subscriptions</strong> → assinatura ativa</li>
<li><strong>subscription_events</strong> → histórico</li>
</ul>
</div>
<div class="section">
<h2>2. Estados Oficiais da Assinatura</h2>
<pre>
pending
active
past_due
suspended
cancelled
expired
</pre>
</div>
<div class="section">
<h2>3. Índice de Integridade de Preços</h2>
<pre>
create unique index if not exists uq_plan_price_active
on plan_prices (plan_id, interval, currency)
where is_active = true and active_to is null;
</pre>
</div>
<div class="section">
<h2>4. Função Completa — activate_subscription_from_intent</h2>
<pre>
CREATE OR REPLACE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid)
RETURNS subscriptions
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
declare
v_intent record;
v_sub public.subscriptions;
v_days int;
v_user_id uuid;
v_plan_id uuid;
v_target text;
begin
select * into v_intent
from public.subscription_intents
where id = p_intent_id;
if not found then
raise exception 'Intent não encontrada';
end if;
if v_intent.status <> 'paid' then
raise exception 'Intent precisa estar paid';
end if;
select p.id, p.target
into v_plan_id, v_target
from public.plans p
where p.key = v_intent.plan_key
limit 1;
if v_plan_id is null then
raise exception 'Plano não encontrado';
end if;
v_target := lower(coalesce(v_target,''));
if v_target = 'clinic' and v_intent.tenant_id is null then
raise exception 'Intent clinic exige tenant_id';
end if;
if v_target = 'therapist' and v_intent.tenant_id is not null then
raise exception 'Intent therapist não deve ter tenant_id';
end if;
v_days := case when v_intent.interval = 'year' then 365 else 30 end;
v_user_id := coalesce(v_intent.created_by_user_id, v_intent.user_id);
if v_user_id is null then
raise exception 'user_id obrigatório';
end if;
if v_target = 'clinic' then
update subscriptions
set status = 'cancelled',
cancelled_at = now()
where tenant_id = v_intent.tenant_id
and status = 'active';
else
update subscriptions
set status = 'cancelled',
cancelled_at = now()
where user_id = v_user_id
and tenant_id is null
and status = 'active';
end if;
insert into subscriptions (
user_id,
plan_id,
status,
current_period_start,
current_period_end,
tenant_id,
plan_key,
interval,
provider,
started_at,
activated_at
)
values (
v_user_id,
v_plan_id,
'active',
now(),
now() + make_interval(days => v_days),
case when v_target='clinic' then v_intent.tenant_id else null end,
v_intent.plan_key,
v_intent.interval,
'manual',
now(),
now()
)
returning * into v_sub;
return v_sub;
end;
$function$;
</pre>
</div>
<div class="section">
<h2>5. Função Completa — transition_subscription (segura)</h2>
<pre>
create or replace function public.transition_subscription(
p_subscription_id uuid,
p_to_status text,
p_reason text default null,
p_metadata jsonb default null
)
returns subscriptions
language plpgsql
security definer
as $$
declare
v_sub subscriptions;
v_uid uuid;
v_allowed boolean := false;
begin
v_uid := auth.uid();
select * into v_sub
from subscriptions
where id = p_subscription_id;
if not found then
raise exception 'Assinatura não encontrada';
end if;
if is_saas_admin() then
v_allowed := true;
end if;
if not v_allowed
and v_sub.tenant_id is null
and v_sub.user_id = v_uid then
v_allowed := true;
end if;
if not v_allowed
and v_sub.tenant_id is not null then
if exists (
select 1 from tenant_members tm
where tm.tenant_id = v_sub.tenant_id
and tm.user_id = v_uid
and tm.status = 'active'
and tm.role = 'tenant_admin'
) then
v_allowed := true;
end if;
end if;
if not v_allowed then
raise exception 'Sem permissão';
end if;
update subscriptions
set status = p_to_status,
updated_at = now()
where id = p_subscription_id
returning * into v_sub;
insert into subscription_events (
subscription_id,
owner_id,
event_type,
created_at,
created_by,
source,
reason,
metadata
)
values (
v_sub.id,
coalesce(v_sub.tenant_id, v_sub.user_id),
'status_changed',
now(),
v_uid,
'manual_transition',
p_reason,
p_metadata
);
return v_sub;
end;
$$;
</pre>
</div>
<div class="section">
<h2>6. Máquina de Estados Recomendada</h2>
<div class="diagram">
pending → active → past_due → suspended → cancelled
expired
</div>
<p>Recomendação futura: validar allowed_transitions em tabela dedicada.</p>
</div>
<div class="section">
<h2>7. Checklist de Validação Estrutural</h2>
<ul>
<li>Intent paga gera subscription ativa</li>
<li>subscription_id vinculado corretamente</li>
<li>Cancelamento gera evento</li>
<li>Reativação preserva histórico</li>
<li>Tenant isolation validado</li>
</ul>
</div>
<div class="section">
<h2>8. Roadmap Estrutural Futuro</h2>
<ul>
<li>State machine formal com allowed_transitions</li>
<li>Automação de expiração por cron</li>
<li>Integração Stripe mantendo arquitetura</li>
<li>Health monitor automatizado</li>
</ul>
</div>
<hr/>
<p class="small">Documento técnico estrutural consolidado após implementação real validada.</p>
</body>
</html>

15
docs/comandos.txt Normal file
View File

@@ -0,0 +1,15 @@
para gerar o sql
supabase db dump --local -f full_dump.sql
para exportar todo o banco:
docker exec -i supabase_db_agenciapsi-primesakai pg_dump -U postgres postgres > backup.sql
para exportar o schema.sql:
docker exec -i supabase_db_agenciapsi-primesakai pg_dump -U postgres --schema-only postgres > schema.sql
para restaurar o banco:
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres postgres < backup.sql
para exportar sem ownership e ACLs (deixa o dump portável para outro ambiente):
docker exec -i supabase_db_agenciapsi-primesakai pg_dump -U postgres --no-owner --no-acl postgres > full_dump.sql

View File

@@ -0,0 +1,51 @@
📌 Padrões da Agenda Clínica
Base 24h por padrão
Função principal: adicionar pacientes manualmente
Exibir automaticamente agendamentos vindos da Agenda Pública
Permitir adicionar tarefas pessoais
Permitir adicionar bloqueios
Unificar tudo em uma única linha do tempo
📐 Estrutura e Funcionamento
Estrutura padrão: Agenda aberta (24h visível)
Jornada de trabalho configurável por dia
Pausas são estruturais (não viram compromissos)
Disponibilidade padrão: atender em toda a jornada
⏱ Ciclo de Atendimento (Padrão)
Duração padrão: 50 minutos
Intervalo padrão: 10 minutos
Início padrão: hora cheia (:00)
⚠️ Regras Importantes
Nunca bloquear atendimento fora da jornada
Sempre avisar quando ultrapassar
Classificar internamente níveis de extrapolação
👁 Preview do Wizard
Mostrar apenas um dia
Sempre o primeiro dia ativo da jornada
Simulação completa e visual
Não operacional
Agora sim você tem o “Estado Default Oficial da Agenda Clínica”.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,429 @@
%PDF-1.4
%“Œ‹ž ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 6 0 R /F4 23 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/BaseFont /Helvetica-BoldOblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 31 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 32 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 33 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/Contents 34 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 35 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 36 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/Contents 37 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/Contents 38 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/Contents 39 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/Contents 40 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
18 0 obj
<<
/Contents 41 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
19 0 obj
<<
/Contents 42 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
20 0 obj
<<
/Contents 43 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
21 0 obj
<<
/Contents 44 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
22 0 obj
<<
/Contents 45 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
23 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
24 0 obj
<<
/Contents 46 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 27 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
25 0 obj
<<
/PageMode /UseNone /Pages 27 0 R /Type /Catalog
>>
endobj
26 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260304134538+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260304134538+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
27 0 obj
<<
/Count 19 /Kids [ 4 0 R 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R
15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 24 0 R ] /Type /Pages
>>
endobj
28 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1167
>>
stream
Gatn%9lJcG&;KZQ'm&.^C+a/oIIqEJ!_'EL2JQ$u#dP)QLo&UeZ2=-:8:<OZ>Qh/4-^6S7+247jB`uNoVSC([i6Mi2\3`H4llJt2%G<V?Zc2Zm5`*<$#kV!u!^RtnJGmB[ig2eX@C/p?Je@(&"nFuoX#Zo-q_nm(PV8%l;\8^`6P-\qJ=g;k@]Zeb!eMdGdKnM9If"B'b#VUDI(ib"]/ABHmZ\o5goUP$<nG)Rp'n;dh*>r`o>@We7<$tO%f`[aNk;T3C02RHIGWa.K+H_2L-$RaJ(<*JG#MG\2Lh7@-%`DY.M0F2-S>@Bgq[503JsN;OD(0K`*fPU*PZ!WQ7SmR`9d8i0PL\,j?XF((iDb@YN>b&VmNH)Kl!GSP%e--%Q/('VVG61"7Z/q9g)cO^ST)8d/eR)EO.u(#:]R2%XBA%;Z7@X9oE`Za[Da\O8AT%Fj%#G#]#[`r0n"s@fY\c,;b]0]gChTp*tMP=<-llD/7:**T9RZFEBusU;6`\k#kIPSBKBSK`*YBL'cHg^IJXg;@od+;7`Tc:3\@;MO@^USq^P/O@l2.m$SO6pbC]>"XF_:^.4ftR]Br+abt"ANV0S>I<U-'L0LpET!&B/ju_/3(bXSrJ@i?iroKd`Mr'VMl,qCnF`JOPIc$FNNjBJ\H6^<Pgs-O/Zj8];(^&h9n?\!E''+gY=;UYhYWXi)/r-1X4[#)^mSgjGiIMBGXmk>4FZ.dYaPTaUANJ&J\D#E8+qO$;??tFjQ^_KI1SF%Vl`/W=a*a(<)/m[tg?7fhq%NAKZ/Gipr250Qi$ZX,)U@+^"@5Rp[#Tn?mlrR@mJ/TC_AfI_aZ$=^b;A9n`@-rId;Td0:pDOqF!;BSW"u:m$2o0(SYLh$cJ/B;aHJH#rq&esag8gH%Xk@]d1>ds-b/fuN-ZmHX3N$J^cH0%C>R\lDiTTofHi;FF<0[?#Nud`qjX(--HSJ:aNu5H5#shEc)[O>eI1q#;37&@@`KJ7cV\'b[r[R0a`n(PIo0T3WaK2+&6J\$"P1'rmMRcm)(0>1I.R"UPe1%6CrXMh'ZW':)7VTM4lQQn^TeZ$XV`U,MDT,V5mGYfZ[F0Ym4)^,edUG)([]IiGV2c5b;!lIn^9+>&]`bB4`hnJDG0d:H\\)aS(L$T(*oC<Dn>@4l%6nS-^SF[~>endstream
endobj
29 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1306
>>
stream
Gb!$H95iQE&BF8='QZqUPfrg]kfjJem)F2'S:&`,[%T`#2j0[7qWW4"8:DgYp9^]),c4:n1S"9'r0(_Xn\s5^kn`4'"oNAXJ8n\mJOq%o0nJZ0k\3F%APP$(bYmCZ&FPu?T\PJ5I+U2?PS=1`;hQ*dJQj=oQIs'lqXXtS`>>t#9lR=r"\3ZI!5E*901loF\78pW+DhXI/R6LHj2LfUDE])YeMZ-CQU5XB9Wu2a`fj3&G_F'!Y+oS3mrpFT--f&&gAP#BG?\G(!NrK79^8HNCm1[rQCJo(%)k83q'+(Hr5]Xn8m<lt3%YTiphm=OqO#Vs0SC^9.AOI^@&Onupjd6kM8@^=@!;MgU=p3,&0AiJ;$m\jrsuiFQ9pH/N6iB=3T]P&oWnmd't#]QJ.CN4Z8rs9UJIjC>Y,16GDB_>('dIu>BdRl6jmF!h0F"5[=2ZM#BJ<YVg130UF`Yga\'#a@s2CI6CYC&2,5+JGamoTqk)?6TPXW#m_nDZm7L%8_6/KAYg'XSDignMVZp(YK:1V?3Sf;:N`bTuI`ULIh>VBq(li%Crf8!8g(%1nJaXO?NG)OE!._K;C*mCK!W2%3kWVrfV<ngDfnZTnZCTI#^,4D3XC&'eK!IRJ?VYCn`)#rajLlAZ=XrD'q`A,g'/(sZZB!XC?R@e7!mJgVkYL\qAY1tL\^5^#+&8[4Y`D=Jj(oe@Yn+]c`K:hknF$s'5rKF3gcDf[ET/o;:^n[<+@piW<4==u8GOhEg?@Lg^uM),=(WDd"V9'YB?pln@9PL^O<C<\T]g>c&<TLs(4j5@^J3X#k@*eZO?`VZW1orXU7oBaE);Y$:mh)79lOJ/;#+DI.'YUn,q:bo#6u7W#Nb;kE):OnQmCq!81Ia;4VqI&QIjeN_[f<Y,"sg;b^]hlN^LapI+i/>YBOR+WQ3OAP'XL_3045?O!`FIEM$6R,8mAj]["78P.uDd"ToPM"$RjlB]EKNa+#[A]0'';C="2VgVp:ul8'J/%(=1"M_6!.m_a$Jo=mX9h1.nhMZ^pL:[2I2aqk\f=^$R8-Y8DTRlE7oBJer3BZQr/ES_ucXF)'-'#+6;ZWs;/BQ"mu?Y@.!IIHYD%2fTa3?0cq,2G^0?i@HL`D'8+p9\i$.pkDor&JPB,`'?mMT,i>Nkf1fXsYmG:kVQ2Ctj$NoO.sI$NXJmU0<#'_h3fbK5e_*ROll5jZKSA<1/`;W3]4-D.n'p>H-4*fOui"i<W*ml'Cnb^+irJ]oG?G/k]QJ0m=*DN=IZc_&;LcErR(+Jh[SNVR`NqI`3I]TB@=(UON<:>X4/~>endstream
endobj
30 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2486
>>
stream
Gau0D?#SK-&q/*0(bDmVDNXMTqkJ)?9j4np2qc.(IE;jAdgcaWF=>7:gg>QW:`l!oVFb`N@3h]qMOZ\J3+p5pH2.1&Io-gVB0lRsi0,80@RY_k,$J6uq;UVs8GJdK-U%i$o>3U4&`e=lm/![#bF'H03!r$1o2l-0S5><(0M3P5:pbOiKLPFICpuhu)=NZFACpm+rP=dZ&P4&)5%R#.g^8ssNit'1(T/?-YJ6UUff@lYhM,Hh_3acN(8huKU;]&p]GVJo&"[)kbVPBZ;7aO?R45^>eZGP:VR<_C%:ami$U_BhOOAJSYOk`CKpLh:09O:HQ@pOS,YG`Qh^Jd/Ll&SC,E.<=a([Yd!N:B1`<uu^#r%PLbea9X`6O"-^uGYDNFNCLriaI&[p]shnqG[7*I'm*O8`f$l+0m'2D>.1l.4)7(df4=5oeuC>.4l?mc`Scm'/Q:(5a@GZ[HoM[\;H([aFR%dJ.#\$uVk\-F*cu.]T,iCpp<5/o#3+i3@-M$hU^e$i9_aju2kK9DWm^I<n1lQZR@uq>#DX\S'!E9o1Xam7bXc46CoS)Jp&U&EX[(LO[ntLG2dD-+V+&4MsTZ2c$QJAGL'?4b"Z2gKRHuH0"ejju`AfqrEd4o+B@1R28$_[VIo.Bb`3I%PLg\=LH+"d*:38;IQS"WEMlN.U@7P2N8tL)f*/:pB#K)jWJ#3q]1a$9%h@9!R/'9[pTIG?a.Xk&9d1j[%=U.A3'E)o=-/7=-4t+dqDk*9Bik(qi6hT/!ku33X'!`lF!?D(tqp!-uf[U=r6a_e?UU/J!:G(9\mPr\R^b?U&O?G__S:483:mQiT!O?F&P`f@.m&*)UfD?'q=?ojeFH/482!&c]eRscC:$kM9,#g3R2s%Nf`oG(1-/#a<GHd,tDM!+X4=1$MLPFM24%,i<K(:7O7X6TNF>1C*9a&JK143h5i>3Q&?C5=kW":BkpI7)Vf0ZZNl[I@R&/$baF#9D>L30;Jd`RH0AdqXd70d.QSUh1+Qp9TeXEI,HQKGlYUJQE@,hXW>Kqh4],TO7S&\>S>U7C\Q8B$3Z\Z4n,dMI2%k#;,NMmD5mt+5<$/C!#NEZVUL;Y3)o]qR\U_Ka1W49k(_p8jY8>,Ac>sSLjW+<rK[b(R/>>*rV[3l(^YYrtY,R\1,nmt*6#F<gAWM'I:X1$173(ess5f.qb;(,k7@jd4T1;aJKj1:IHi!=CeOR@U]1:V3gh0oE4N0UH6mM7R$N4Y&.4dOIq%ad[7!W\/SKJB#%An_bF;YJ^hWp=`'[N>&E*Uq)Diobc'&Q.=Q[qLp"gTlm`t>W-ko*f:)OXV&?!9aV.:!4FmROV.4SA&rS[n?\P='IoK-r5`X8(c]*;PQ.Da.0Y#_MWIan;/BeH%P!PaOLaU1/48cJ8WLRjL6[,QBui>XY).f5m+%V+UrcTatGR3<@%\:JZYkDZAB:IPu/?B>i!#n((:`8'9pC2Hd@oaI/($3tSN(\kuKYc3c1,E,+Xd*+tInO&os*N(0p240HO?&:QOI>;s4]<["joWSkV2YN$@m0r`\2&kWi`s"l"Q896injiYC(@_2(HRJ/88E27GC@n?T(<8^$lVUGKu!;/Ze'd'!agtSHe_J3d4>au9h(%,UXrutRXVdW0r(0&R\9(rLN<D3!%L#4u6'pUTQTTVR>+fgh+OdUlL.nd$"/6lffOfao\dH1VXL8joCF5Zu#MQ3?*&"RV1c\R*Tb8"Y_LJt#WCJ#miXm<A.?jRr49!imr^CBkg<n[1#]1snB.g<dLN6GB:]EB<u$=WXM=pr#WI/\US&qQ_!lVAVK^CY_8bJgB_BK3]T2&=LW)W\ugP9GF&)\Ja4N+[URW:oQsN5O@*"?Z]%2q=M&)e_:EefF<$RCbrAm>NbmD,jrkJ_6?N8cuI<C(4Z)=+BZK%`r3Hh:(:MGURB_i`DGJX8>.)ajjqfngmBMcJ7gg+1>eHAbrr.]c4#>9K_'lC5na$5qU2Y?b9\.*m5iJ+RPVF]g['/.p^?0E!"A:G*3@s]_,KFqiZ#Y](u$hpi.tFMo:PEXN0?8hUG1uBnuH[bHIP`hYK7S!r9d'Ni#PYWH]1%Y.RtKqpli3[&=:Rh\V_"?n/1eJAtfj6YUs8qdkoZr6u6^#C;?R@0+Mg_l;M8%89WKD+_,;4ns9R**STO%_DV[pJlJdkfR$,2$KB\aJ*1K/oVX\FgEgqUnP1VYTnu\/i@%W:mgitm%S"paSHs_7VCDIP>tgccGWF;DR'O0fb/D/)H1Mg2[HbhU8O2]!tQr):`\t]L:u2_CtX;bec7kZ(j$:n46KE<&0R'N:^k,4B7Np\$s5[=n4ofPiWr5#Z(9!:?NcDiZbAf23S7*;EU03W!m0.Qmg`PU]R(3qFD;s:$'9@5hiUEOmGjfUE/a6*4aIc3&._4.n+>b=>X+k2>P@>f%W3XKb0bR'MA@,)e@2]0\p)O"c&V[KrLr?(Fi2mdB/!%e&D\m,a#,`0FS%eb:pUW~>endstream
endobj
31 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2081
>>
stream
Gatm;gN)%,&:O:Sm%`<T;$%kbl'pBdR<G)]NKs4V0<b`(aRGqnioK]X(=\&&h8F.nR^"<'>TE)jHZg\a_FK?qX,?-(-2mUBP#SenPDhq(b:C//Sp`X516Qo,8]WI2\K[_f?D)I_<o`:8C11SRalGpie0LhFU#RE<'9:Z$T-5UHGWKZ7.,u=gOmG!AWn_#;M+DZP9?NJ7+i:OZo648ojl5t+qmQrZF\+NM>HJVmhHp2q>[2\IbE'%.IM[f9nB^'9Sr#Rhds:^ic"a1^h)BH/or[V"[?2ECC?S]tP^[?3[4bduo!Y(T-$#2[M'e?4E>g.uTAi3*EtTLWTjn[+PF>c4O=D\X]T@(bOj)mJbT6[^QuIY\Udlns)M8F;s/0YE>93$aUkhLe.T0lkqTq+\le]$l!rt<Qn!iPrp6=iAR5h*-\g(@'O@(;!a)G"T7A=o_LD.+U_sW_Jrd^%&mj2^m%&?R-$'_itK$:r`73:M.=CZ#"Ao&r+AEVS:c>HoZ7>$MAja>h0@V#qOW@G,4_t#&k&2?Ncj]N;Hl2O[=-6(ooPUN=WAN3"h\X'buREe#r!7as*f<,QDnf>9/dQ0k2D4?Rr,321u[,jZ^hU8F'BW1_"g39*+C0Qk%B\g%\"mtPF4]]!36C#g8g\k8"hHu13Vf?;M(.TJ:4Q?*fKdo','4/$Olau!\'L<Nn^%:nBIR)H;f)A')m==ZAmmOW7ER3,?!K8#2k=\8R`VE6WcHmu5<?cPud+AXh(US=%ja1>Z(EBG(L?%t26NuCPBVPi?nP7i'cF1-a2dnd0oM+WZMjph9P%r_`j1uNWfZdfoO([V034:&%)c1R$+RolfB_``<'?_alA$j@a38jZ[jKEdX(7B._+E$qRhDU('%kP0=gZjg`WCAeX!Jpq@9m\u/B<:OkDLp#t[^SOc2"H.5O#2-Rkl@H#CYm/]<p_=@^gdDM]%)WiL,+k-:/h#6i>(,@IdPfLq(@ad/W9#s$`(6A=<W$'CMc/bc$?T*JYgj3G$cO!G^^,YdKlDZ@DU0O&"#a+*r'K6HeNmI?quD6Euo$]E1lsW5@7]j:&XV@P29R&>[RGTq=a[IgK9GlKK2B@kVJh+m;.gapVnPDU?B<thnRi%R>TuL(Km&:peoPb5K6c>)IMHRP/G9DqBQYgWDicJg($S&K1O%X8jTXb]A\caB*AXEZo0IZh`c%MUf0\]X)=0CniTP8+IMK*;[tLf2bgV-*A'1P0'oN@)m2Q[^S&@0D;QqRg[Q%O@c661SF(o$VY9-Mq!L_a7%'3Q['MV0ZOSjMQq^eDnGoKTLL<Lk5#(I#b5-.nf3Q#;\"K3FP@g9)nZ`,&b#NXpZL^]a=1/k0r5Af4">,@GC(jD9L)O@#6`%C%Vde]WcVTJDiYPRN1hcDh+1qfFB&.sFR#/n4@9g%>NjpTDG$^)\_N/28YZ9fcGO',jHJEHk]:5JNa-HP[bBp"2,SqmDnr*Tkm!_[:[IsJH6dD0JBZb&<A!:2D7Wq(@>>@U)*q*.<6oH.6\r0sXc%+BS_.d<M4CS`tcrpp#dY6$7*V>14W6f!W]4LPfrcur>QpP.P3cN>N];F@p?s:(d^`g&aTFDW#V%"Kn2&\s*c&smB#-L-$IV2OQ(!fsP0:VB_kt*]01r'/Y=6[O`juclD4h$Li\hCP=dVWfE1$<$'YTK.SO[2&a$dM*Q/t'G1(8cq8#(u+h0?.CYDICXOPeAT%eU/W*$rtURV9Op`\hNAW`a!n6;SJeoK-1pn%Gpe@H\\TPI9F,/Kk\*i9M@<E,TGDPI^*7RH72'_FY\Bc=d1*;@l^?;LP8m#W^glr]lQ:KXa"TdmDWn4MnBL_0)?!hBj:!gp[B0bZ[91@8^!^L;.qe8Zcb[Ei,mUu'n1unF'!;:]m>]XU@cEXA+3.O$\[u0NT4\r<4`V%aREuEYhoX3NI!VT`;*gK^HNhK;@)Z`fQDiKCk@LmhqGOTCd&!Vm^SoMg!.>d`[3M+mD1J]]s/Y"*D_.4/s9>9ENU0""S6Ul&r;RPlQ<c#d/?cu&<*Y3]YdMHN:-%>1NT2^L[c[(E.\YQNUn+@a]p]BV;\\Ud+R:f!9jI~>endstream
endobj
32 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2052
>>
stream
Gatm<>>s99'Ro4HSB#F^[$'P:^9JA5gO[d>]!Dp[#[9mNZ)uplFT;;gZ=CoU!jn05K\2T<cDP`tF$gMZ7s'70K'9BurGa/TGVichn<m)DF#oj6g`8)b71or:,3CZNLj=i(5h5Fu`65EI/F]L3+:4DO"X/.GPCD_*LSi&&!sljN]/p`K`D/e;_R0G-'l,?daMPkg"N?sCSV28SZd7gToAd1#X,CJ6InJ>52c=t3\2aFD5<f+,k20nPDCrr#@(1BK`%6cCBNtC]S$n#')b[)O;lSnK#%D:E9/iFYd>9Fk-IZO@<'J^KOs#]N8Lt2HLkkbbE$5FTn;T/ucaX7T?u6=>8r*a+K[,l;AiL]MNB^l<GCgXT,<Q_F!A5AkIrn]J87,O5N;&u.mKNR>f@lB:)dS^E!,t-Nf]b*pQcdAWr.G8r)eUH;*B8tL_XV*W67Lr331\c0qaAgJn7[&"o[_'=JsaM#R$AF?dqkbC;s-"Cc/T;'f/\/sI7BR]`7<!"qb$ub:;:,aalTFabH!-)jn*j,bk#fOR.1WUQl`Kpj2$7u"`%10n3#!@ojR7QK)@<2ELatWam",<r%LmCnc_fumaL4Ra2djAIpRB540OIqGpSZ*+!e@FT\1V+\k5t7l*aKtagJ*!AX[AGBH@k4[o/Kc>2@!;GjNMSEWR1F)aV'.'\#ZJ*s=(D\ZR(AO:#!O11d<egMs,$5bg%(43a0F9^*7pC^"s,$]38j$j0csV$Iu<__8O!$pnW]>?^.R5>[YDHWrf1/\V5QT[YUZ<!JJ^MaVUs&<Q6&ZCloo^StZMb,eR(C_prVR7uRQBo]kmBi7BgYc*&QFCjscErer7g=?b/W/P;k1EqbZ-Bg-'*h.&'GfO$\Hr+G-n7n\>J`-k"A9iit8*R;5_tdi(c\JUlEk2kAZYs\P(>c5cNaEUh5J)VnVV5Rg20lm?WXQ<Mma+gL^hKX47)0B'I4g[XKGQD87+0I*6\X_P2>HcVC[m7*&SK<$8/o=:<^pIdJp5gm/L%'h1Ku`J*n>1IHD9:)""$DeIRQKf/<WE&eXrnL/J4a*/jb-oXP\Q\gA-[:#TWfb6o<tu$(fg_(UFpX<6L:"G?]Gj3\-SGLuLdRC8@dlH(be(1,kWWM.O'.#c"$0iY(]Y0OOjV9Jn:uLK#L^WR].&R?^AR!9rq1J7VcK['I5C<GBV;$e@ZA[1>?iCm*b:jQL=K2gB!hRf@OO`r_NZ$Cda`J8l@''4pGYdi2QPmDUs7n-A6Iaq5he_GKueW\hj%>ZBt84g's8\i-ubR<m7DHlkg=/ljuIDY=:4lp]f#RlBJ#f.667freX&;Ise$\p8p(idJ>tLbgZ/J.h+(pPFS-oFKQR'O>5,\eG]GX`W;Gf1mI"dj_rDkbCAQ="f4d4;@5$@%Z/"&>dQh\Qp-H\tetnp<96=K&RLZGNm=(XWsrff%SBld8U$NK'04(a+R"IM,G"cC0[33LX2c+*9.8)Kb2D\([)"Rlil2MCR;T/j"C#Rmiqrl!i82Ppq!N=ICSPERa`)d("/R$9Q&AJ)E=_mqk.Ocn@?Q+g0ua_5Oo6od9ORMchZQF^>BH''`/M+/;qpN"do7O*WY=@%0$iJb?A`94*TupS8ms7(AQHj51qpKZ9s^hH)F;_,L#hgP9kE7/pZ2EhI\4n>)>0GrmlZc/0o"'\CKPa"4.G,NcWD(c*71YfBD.f$fjT)+--ol"pn;IL:^LknRmS`36huJ]^LY-EeTG&^'et<<B%qB:Tr)(dL.B][Zgs#djCHDdLcYn[i[dmrVUOGK;5lXQ(.Ic[BNZ)W9d*FG3CboVUA1o=M&3FlLGm-Me#cPc'4#h;sW#"Kh@\t%;.2-]i>alo7`7QV(WA`poc)GNF(<GF'iYMeXt@)d+PS>51:ql@a[[;l-#*V'ALM9U$c87bA&F\S+FoBSX+K%E`TVdKIp-gJ>)q>HUO@[Skg4?j/>'Mj%,BWIeK'u*;s):emnuWF<^OSe"V>nD)\#(Fk3Fi+1Q[0QAm=tjQJc<31Fe]m^#co>O:4Foj'Y>a!J#oV=MZZ0cg0&SUB=-~>endstream
endobj
33 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2097
>>
stream
Gatm;gN)%,&:O:Slq;!>N.V`C_2I383(_qGS;WXrjTVM("YE:o]JscC^'>c,;+d'd`ZEGs'Fb,UkFCA7j:pR6LX2\=H9S7a??+tVk"F\gMeRW70BZtTc5;UeSoA.$Me"GrM;7!]Ap%/7iJ^F,@OXn1MMY:Y,Vg_,,fU0YbGT"*'Rs4*NY%HN'69Ct#N.3V@o3P*EfkFcKOeMHGstNc?-hZ>=)U=9RF1+h\t+84;W-QZbFA=oqH'-c7J!G;7QuIbgWM]hGoEUXgL%OdY>!b4HJ*J$mQO4]=13)?8ViU;\9_j*VCnr&-YK\h-!;FKj[kt3$,0>Cn-p_l%La5]LoXL7?iV\%;`p.d/-0@g"`oVeAL_6["*h=`WD^7qnc*<lp61H3,L3#,BFtP.>^^E/ft&t6m199gZ^mS_Z(Y-o6H(s%U>(fq#<>1'q_fFV8X;o8m=u@89u?.YKAJscS*(D=VNIm;AI4T$<'Q]%jNr\aCHaQeq:J(XqFRJ"?-ci)]U@SR&BBPBZcfh"FU==<]t2(Ag$N*.hiB22=L<u7UT[eka4VdRVFJ!mOH24_]H5b/-r+fKAU0"P-/BWu5>%gMo-hqZQk$kA1D]MB15HGio@0ZhR$9=\Rce&Bac(<N&b+5WL'h"a9kQP7Y<i#u.:#_em@(2]m6WI"M\UB^Qqk-_V)MACqK$#k]%$bKL32&5P1GVDkW^"i5dmhpoEMrVSCd&O[S'sYM/98*F?<#)'#UTC0)BnLHsr>L2AOcFD)L/8]$i2-1t,XWm,umVPK>r/4`oRgOMolW^fE]fgf"t=TLpc$>c.'2Ct2!LS-N^42-kt?LgSNQK;n1_H>!mUA)4X08dU2OP_`\s^/s+E[bkj3a0H8u1ron.J>;C-ag47Rc+mb'gWice&8YiWRQR\jpm5KDJWe.VHp_"Yn&c]iV#tuNLW+tdZ\DXo38E&JCOi[nK??!+Y#9de6`\4BE!\d57W/TM8$>YEGnmK`-QFUd50g.Enp8q$iVM3nSLoIRGGo7+?+mOL^Tb*u:QYoaW),>$@hMXA\m=er&ePrPatuhS#Ei8+,WUE1n1Xp2.+A/X$+/Z*N"8mDJ3K.$6WDc#e1,-L2`bZpo7ptQ?O0j!oKnab?K+W4[ao`Zl!m/IYOb1imV;r?TS<''2`?m)'^2"WI>)dS0hWK#`.6<)hi2A:G:EN?B'Yb)hmpnuds:0d0s/hEYcnH^6,ETrM#B3$m!5@1H\s#3bZH?&BL`d<CYLCb,Xoc^$ke7H]c()8;gRe2+_U8^Ws0]U!P?3+NS0TS'F"I,gu;:,6[tb*<2,P'n</"1^OMQAe*G]KZh0cZ?XD[n9S"qMUP;(jR%CHg)$<0Q)mVg,PObibShOF9VnXku;r9r0HDdq;+2YA9CqTBM=rG6f_`Oi5`!L&oY)[6"ZgDqP,AAF;TFYWHbS;b2/%[O'G3g'0$FD0;%>ee?'&j#VM?kZ.6e@]D>'N]``kX^PbeQ1BKYk1/g*P*^\JtDgZk!\!(#4a7#W:u3N)#.r_Rn3H%,_bHf)l&\bQ(!BXEHCRTTu\V7\:?9fEHD:bVs)+>Lj+2AVro+P=J32.bu_%,gt3H^"N'U\3dqg%BOKTens65&3\@;_**VBR.2H^"5E:kYC)#">BqMq"6R4qg2_WZ4Xf0Z5J4&c9Z+5Q;u#uO[<c[09U"LC%W`BLc,A.?%s1N61-*Vnlj_25:H`@n_edr7'">skqJ`>\+Ng&_o!gQ5/D;_$,R#HtL.,HC&4MJWD1;s_UF@%[d339V9a*/dG]CTJ.:P"@qLPN*A4e2D1o:^r41fc&ht"#DJJYa?*@NWcoL&1RGDG27jh*PASIZ(F_Au&-FT)'ljh>'n]#9b#J1;ICeuc#l$`;EP(RWJf:^:;q_L#2Gc.N?hP?A$,JQUsd<Mr@m6PYg<EPi?Mh#.27KTf`heSHc=]Z3`#F$C2%-Ya:>"!tc<EMRS^iO7X=n]p!V(9^2RBg'$;Dj"M7otoN>1?BE+*6EUmZu8/ULWAl>J"TK%VXaA$Vd&13s1g;cqotiQTA"XC)%KpFFV.;UqrS:"Q<q'.C9I'gVG!Fj?q!%_3bYrL."SFd.2@n%6>-<#QF7aE~>endstream
endobj
34 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1866
>>
stream
Gatm;968iG&AJ$Clqu$K-lrL_oVNm/VO=5T*4[G(?kQA!+UOSJ2q)-k"\;7M/_P"g,?pn``BM"aHn,C8?S&j&-,Ah(r;M]Wj07A?Mb"!:O)V?f]/q6r1)2Sf/>Y[Q+C+^]ctq#X4Ie/mA0_:K,md@X$p5g[MBe?>Y^(AG9(@V`_$#PC>>QRd%H(MMqBBD^/$9_b>`bUe?,GUma45M/mdfYl$Jo+)f@>il*%sK]r3]jZr!h67@Jsq?j<HXd?Ds*e,YAQs^NE[dR]:FJd<:N;DM2P)RlS";4FoGog=Y1[RFT:#(/Zh^@f]!:;*ndj9^mX.R*[u5+sN8n7#kXt</`,J+SJn]R.1[1dJnm@&A]%L0MW60n\Y2'[qXP8&IA]$d(sEk\K%cT>6hV%9N*rn!9:DVRti_p<$Y^<$7>68a\nfP-qPMm_c&j1+jr`%K%VDOT,IKWFYoYIq%uRF;j,.&=]tQBRE^[g#[-c%EU%u4+jgZ9'3JOg\c-YuZ+drh9"BW$=-mj/f9Z_K*;:[M$/PK@_ETI?iG<Oba>(^AVAt6#H9^Q%KIBkH9i['X$M\pF5<;<tEN56mnF6$XS3.\k`H6l#=lKPFBMYEDe+!r"<_sUDdi6o')LU@-k/uDcSVGiUO?;2`h71,d>T4_!``Kak8q=P1KNl%PD"15dTV41olFGHIJ_Wr'd9rqlH`SsQ=P#11TJhf'`RK*[UVVJ!ONuuLpBRDA9r@r8*d2oY6H.*b1)EJpW/:j+?;1KF:XuI@#K(054=3XO5b6)4p^iHuQqq0:&SE5K?m63#WL`sd_i"8u`pT-t79H!.`T,?V16-"&il?h/EZlo1PWpjHr$W![lpnmZGeP-9'1@'q8I&?-CF8Zr98Slg'g?h%Yg=N,@2KC;`c6riF_D?ap=6UB_;&PohIC$4311Br7jLr0%a#Q*$EQ$dfh>2fcZ'A$l&!gCUO7-F%WO\kZ!`s_<4pjn%\NKL^TUW\Lr-S<4T/fs`YCs%Q*%JVB4FL9k9lfh@.E]8odT/EqfJ3jdFb<.'q"s9QdsYd\IL0soGq(W;`cR>@k1hr`\r'Ip-iW?CL*E=3Ot)rJ-7VW12WlrNQo+/"865M,@Zf0Ku8q+>%\W<QmL<_5&Xs_2p'+M([/cV[]O\;[]#neCX_(nO[o,faUU6A@oGk]ZUt-BkV7qd(f/-elc)`!2U%J4;T"SPD/DBqL[h\Qm2S'P"=M6_+Oa'Fg=?sE7>mlEA%q9r^Iu*2U#lQa^XZ><<!n)?2'0n3(UB[/P:u=M.8/$D?DYO>ZrM!Y9X'9m>`[dKU2O]&0eDdL*Wb^3rdR-mfoTkaEmZ`"b/8Md$"r7kh#htBS'D.L,"E?<)O#a<%]#6ZjLF]M:qh9O?Y$a:+#p"nb:3G+Q%MmtjrX$4?@]AQ^m"\9I)#0Y\-Z@DmMBeHkl=lYOAh8jrH>ScFSS&V77th[RFu-8@!%iae)3IH6b$a5,_=-(fa\@u[2%oL[&nfDAD"s[P!(T#YU/'I"MVR3]"PJrI4/UomoKS'93r00/5AZg)>oo,?B:Z0\5%8AhT^?Zg-&AO-`-t:_qph;6tgsaH,/?W==bV2XKM86]eDZi<O/PH)77o-V=-Pq?;VPj]m^7h4SdM5>f(sK$CdKF+.##ekkWAfRsE6Ih4K2u'fNtRA2sKVh!TKL!C"_H$DJ9km-ltEmL'"+MZ%i$8s^LCBZ:9^@7"bC*R]8@"DSVm#-Vlu:2ljJP^kFTIJRkVAMu`qR:fqKIKKdj*8Ki]R0>#o1Uf8l&Hkuq"2QuD1o6ecEM*4:4Q_JaLtif_Nj-@\4ha"F&U]/V@Ia+tj1HeS@;&3/Ri[(.qPSm("0cq.(Ps&GmVqtD!Ik@WSG`][]am"~>endstream
endobj
35 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2265
>>
stream
Gau0DbAuCH'&Dk(HBY@c94eJ!SXlpYUn!TX72k?,["13(&TUHp+ushh?_>g*F!SpIk:3=KJ/f.!3dnT/JpdJ%pHL<frj/IDIMO1SXg]QB0;S7ESZbiW:H[To//I<'e.4<+T$6s627o%Z&#UW@U+cj$as'V.;+rb<JhR\>6dtbZ#nFobFup;!;O3DinK7gU`Loj<HZ:Ag6)b8TGJ^qee`Fd'qX!D>=7=GuVfTF5]*SOq`Oka!en5fVe+ujD^D[Sk$_BOGaAOZa_W:4]T5;4pFk'VUDnAJ]2aJ^4-F\(AAO:<O[5An/j[Ksm=^Dl_BcN5$`F7cHpe7n'UJLg@)+!K^,"eTG)/tiP#p+oUQZ-P!3HNP3nKMV^=55$rQUDE$XT'BRBlASWYTu?G='GGUl;o'29;^ur;ctLKqb1XhArIlB&fHB@NS;XnarDCK07Bp^9EQpXK(Mk[-T6+q(P7)@NlL6k3LGpsWL(Ui<aL4qAdb:r\YucJdV%C3$hVS;4F0fTDm.dU^ra2!([JQ60@)8s0+O6AZ5R0:q6LtgIDVR_:Ib.d1k[hS`*qEN011Og$KK?'dk&I,6.\o'+/dfOqu^.ik;*G:8-r(A,*B)e/eOXsE(ZGmiM4_28#m.HD/;iaZdk$;W+eg\pNtBRDVsq`ao>%;gSRmL!VS"C$e;5YD+[6UFL'Db[aVbABQDC-htGK"jhBs&lWViM+Ik4`dUuPthLS4C\_=N>5Ys;W%O[53Y<;YUYo^>.b,Llb.^oj$L-DNB055lY:hWY?.n&ee`>>r)%Ia#S]VN]YmEu2&iBXHo%A6sGqC^'q/SrmckpG!kQ+R32eHN=2Be0s9C(2,_.]o'HoI84">Nj@3)bUMSW\>BGA+B[37$+(SkFiV<OV_PJ`h?aW8:1[?OCI-kgi.c6h6#P%ME5laBSako82KoI[qK/QL>-e^VgR$E#VScuPh4!?fTQAYN`AW\4//B0qX5r7:;53-k)gK6'22lnZ:JRmVO+,f+Ms8iE9JS=2(j2=oA.5=HhCh1.Patad:=4Br,9c#:lA!>?p)u93c)`56/nX;[VF(T%L+%fN@KA[#L),j<-c[7/SV7LPH#H0KkWEp\o"Q!4M%aYHV,2oE6rr^JHK9`?CHDa,:Bp;m+/PGZ'?duDd(,Q_LeDt/Noen^U<sA!=0Lf0NX6J=!IdD<JMt,=4c/JWkhR"hBakAN;_"M6m@*W4K]C]4%jEJCi)iU9+tA?^4am@nb;4ukUco>IadFo$-i#A1nWjJ#j"%bl="_W)e+<NE;rV6qEfT1ij_2IY!0L:l$fg@*2\tX9\fAi-:`Vll6;pY@"3hA#ame#8rs1:FGV>M`!kSS-1gA^F?)VIH/W0I*]W/YRW'Z[L;!4..?crDA<b#(k[8SB!tcrsVF:k)8'K%(G;*h*T'kMVg^X<4bC%sn[ULFtF&aV8DsXg/59nEn%dJ\a5+7erqO)C:I&bmfPj@V0;&]ZWeqKY.pBY4;_/:nGNqFh"#jF*mZ$8f8A76b\Gl0<rE`.<nAfbYH!'A(=.PjZHNPu>=Ws_n%3@qa0#Cnj^gGm>Ke/[".(MX5TI1V=9Y+[=s=A5Qk>araAa$di>?TK*DVDA%]p`PkUQl^5YTp\+Ra$J$*iNrd&X*e8&oJNQ%fRR:c.<`6RCXmO(\-'ZFq[VUALS.[b]C[ct(7LM/:KD[7Ca@B6kN\E_"@;h2;7PK7\j6tTXl%UeAJW$g;C(38KsSlCn,kV^Xs?'A\<utaA_BH+:1;ct1Jf,CS\s&_Df/SS5N2u_cc)'^Fa<P^ln7j=.Hnl_qToP9M51R>-Yo.ZUM7XG>LTbIY#.qgYQ>`:WTk!f4B8s;oTm<[XbCRJpeH'pMrP#*)j)8#G&u#_,=L7j&[1P#r;&-jQpcZp6Z&:.%"a$N#+B%,0@JoQ'/4,"',rW69mXAg6?VE8)R3.<gmP/H>/-'U.>pCT`b^dDF#\D\B_:d0JSnM,XtG3KF7nt`q`4=NkidaU?L_!J&Z9(m];&&\T/QY?71Y^1Q9!E8$uc[%A)OM@p)9j/>&a-`ROk+/O8:*o>]8?2?`=u>XdX=s(o+rthl%=^+T-,#9HsqRj'd.FgCIoDD]8V.&H;eS(Ff%(R)A1-)0bmi%%6M90M>Ms-W')OmX\H_2[#Y@b_2!f#CJ9I@ksXOm*M-hpID_=l,c`8TM*QqIPuCg0:;V/?elt'lfi!(o+Yf5rdEE7j^OgSEe#%cjmf<)D/RDC</E*<bi?f:]gmB7rEB^YqN2HTjDPhQP%"~>endstream
endobj
36 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2121
>>
stream
Gau0Da`?/p&A@B[qC&sdL&eaKVJ(ldP,1]Zq_AtT`/Ke>"O0AYjm2L<c:Ap,2QP&dW\b\*fQb*Bf*sM+n]0GbX<dXj6,/sF@[n2a@Y#?BidsoAHXHM0k-HmRjKZW>.4:1Sp'*D?]a>8t9R'tg"GLRRnMlJ`-_:U<!%SbchX83.rik*n_'uWNUgCF]!-d*#9,MqOb#:b9(S7t;-u&_Xib3=RG'.kO6mnmSHaFK=j5TY%_rBn>%5%RV`S6@qlbVoL7@JI8*5oJO:JB?+Wp1"`[Tt,iV$PaXB82_Ba)l-tQ-NBpBj$Dgo$T3a,!fNR"gK0.1WBUQ`l*?8L'To6KlOU/#"$:2N!COG9*@%s#j)>@m"N`;+,#Dh0+L?S=T"djdk4'3$o-'M5]`QQ_pufYOKLXp<!E?\TC0qUNGV9VbekR"/\8u5V61ec06n%_-c1dp89Wt%D,[q@BVW==8)*2N^2YA-'/,2==]tR\1)k)qPKTDK`dNc)]0u,bM+<n*_8XO:MlU&C\Y@jQ[u8_/B$O1j(JE_FWuXS]@gWjpJ&ISu2lugrD[XPm2u/JrIQ2Xp^<(Rj6'Q[eOs]s%d-[n-&^-=8+3+<aT3:*"p79P!2\LCC+S1A1e9<<MSOq<t4Of"XbJ*34]5(QMiodI7U"FqYVhlMBe'Fr=!]FFR<P!R^fOB1406uJ3!<5OcB76&+E92s<P=q'<(UB5Te!/:UF4E7=;.8%eU;#&+Ga"U!_LIEjFV"1_%jS1'\1]pl^Z-rtAC@W\ASl._gOlW_^'+R-MM*>e;luKd[pVrFWg;4qXSP88,#k%d1'>gLn#Xc7XaBtZ[rJ9LeIH]mXBm;)+-HVM=_Lo7jVB@1mnIf*nKfuffg2oN_h1GA13XK8p0F$U!.4$t<7@5#jO[_e=uEU]*57*1,KYd@<Zs)kRmu3qlJjg':k`P&<D"*QJCJlYZHgorejl]Na\[0)&aV6gY87Dacf1<E>lNY:WHZ-V*725NlTub^X1gqTXkG6cWp;uG^us%'ms&&3%jgAR5,kEj0l*"@MjldColJ&[1,n2W5;+"p7]c[T%da!]<=#KSe&EN9j19=9SEmUAXbKJ\!oRa0^m@+.$hXsVEj=@sD2YR*JjcV\14W8@WA0s6'&$`2UdoinQIol;A=4Ts#.aqDJkdHXL0c"QErY.sqIKD6!_AH",)oJ.o&U$=HWVt/&)cXPCIjM]f7^41d4U;jEmKB)jIU]L9nGViC&>,f-X!,O).[&cg/PANgefQi+f_qd,T4HuJi`*WB`F',Tp1GiRP+hTN1?'q;WV*AOl>O>WlS=)g_Ec:XC\7.Pmt,ag5:diZ3TJj26KqhPIlFL]%E\2Yem]V!W/d.A0Bl/W+OF"4'((TZI9O/CFf?P(IMhc$e!&?C$G@c%?#)@RXggNNHnK/TQFW6Qt[r0R#bO"QHq\77qqr_As*'GEQ37n(hSU3F</uY8ktA#/d496HmcNAD.s'uG]'/jWr1;Dc=skj$k(JVp#S@JmLG'1O[Q-PEg=<Mdj]#W_V&EKX%X7t6;V>fE`<lCS&./83^Er19\K5qgX0m?g=_nf*Y?n4Slp`9J`KoK(3tNTY@O\i:C8M2]pM0[?^#\orkD_mT=g*"a2(2Q=E_UE+</oO*_oN8*pdA5("!g=Mf;pA$@HY)5Sh`)a.BFa2q^EQQ;d=UB&)]%_o.O=Ehj9Pp68`2h+9[qUBb:(XtnabC[D>]B2eFc$p(B(+K[/"!lD+;\lK<lf_.HolT#JG&XoHrcZF=X[tg>:bq]$;dT,92hD!,E7jqB;Aj)\?gn,PT-Q?unSP`kQ4XA=I^=0Og,15';,S.fU[)U%I\a_:+?^a[t>C-VsUaXi1e5>2]Im=&*#r<0Ok/MR,*mp,.I[:5]I=(=r=VenI/c-:d^`h?*.et?4B>NARs*0]1^l]bjh4X5(e04$_Zn5H/rKd$L1'+A14?lNXX0S-?p3.S(/"=?u>rTj@p8n@dJpM+5;1MU;PdAaN<50cI5)rfqkbRN6XgJi6XDi9fiO1E'g^,t_c:JP+rkPeaNWr>0613NjeYWG4F2+c\a(O!\HEFD%R8;^Q3!"@!/d'\TYb]fMX]h_5?%0)&eNGh?_uA[43)=!THiF-1r3\r~>endstream
endobj
37 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1330
>>
stream
Gatm:9p;>1'YO<E]VB\od3A*D2ojrMV.q@1b`J?Bd=BFh_2+Al43=\PPJC\LmUeIUXtjBMlL*9-T.,$[U$b!,!W8>$LH>j-6$<ALO8uS5fOq6\.mcCf7MH,&4?mCB=DE<c?*7pi3MAVC"5ekf%1.s1#ZsFk+:@TE!QXpb>SKEt[+g0u@Wt@71i.]WdK)uXOXOciKnT9?XcT)]rUG?+D=;q%I+Z='*hKeFhe.3)Yl?$(hS4^0:WVB'$X!\k?J\HkP/]dM?2dmdh+?+uK"YkUD\%:f9:7:i0G_Q$eONbu3g[Jtm7#GW8g5?J)?mA^iA&=cHI9[CMW"fYUL>,P@\?\b`qVl2[6uaE2_bR80aSkQs,i'l&r.X%=T!XSD%;$Q/"jiH5r4RUrRpX/JZN(LCP;bMT5<&F`hfJ[bebKV$bC,Y;J-5q,[\Ds#pn*!aV9ciQM*R548jn']"AlbJO^\+,`0lOq$Cu#QGu@!EN#q4o;-)NTG>qo$beUc&iZ\-%,4sn*7]jl71'&bQpI<5BkYI9(:oZAX@B?fE)c\Pa)=*#JJssJ$21Qt&!f]"j/5?aY]]6b:@eN?<0KsTF(lVCriC4X96#R$3ns$[crdBW"^O\E2Xo.cGV'A&H8gHW>FNC'-uj=qP^eh?_2nLp(]f?OASnVEMg-8-h9WPp;"'#ucl#s!`RWPFBiV/.;0"oj[:iNprR,Z0TiHe5LLBEfYVunZW6okG=fo)2ha\Y4Mn;&@1Fj$?H.<dX7SjF4Xa)/&QM1hq,7tE2Kddeg<pDK40pm!,]01qgi]Xk33Nor4el@tm"i`-#gRQ,q'R4@s1KRb<CLcq<<@=i\1XKQYjCg&%a5p0c&rbJm1AL\JN4qIEkrY;XH.hN*gPDYVV9M+Q_tq4?.Cr,;GQW/qk&i:%p4L.PnuQ,p`\XnH9W!oc-0Z*V$[PL5;UF'*@YA.jEU78/@=9No(N/U_F2Y!qFH=V3C>u(Z;/C!`/,9^A"\QFeAU1%*XU/SO/qnol)hjW+F!9eYIBPQZ+rARW^_8c:WA(ER(N).@Te1P,7rVbVIN[7f[\g*/X"$"JRA!k&O-Eio-gY2j''"3l=3eIq&@4ZGPtH1Zd:hr9TeJ8Vqb(Gu8W;V'H`%G(,!(jI&dRBOb7)a>C!cU[rlaCSg8?mF8,8%5PB5RR7*_1L6VLb<frulM.mER>0c.9QF#,0ZJok`):G$KH1M)*>D?WdXf1%^GLq9fB@[j2n)rPCRK6)JGgj!PCO.tS;6Zh[T@JYuGLK,-h,"L-'VF3U)K73_MWYr;_n&IpqnOk"?nJ\Y\\G8p\c%$Vgs)!M\mY1hFrWO5U8oF~>endstream
endobj
38 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2364
>>
stream
Gau0D9lo&K'#"0DoOY&8W<>l`S%3eX-^uP4nN*,lM_?ajbEsR^)C1N8q=XV'Ua4*C@ni4d:2Ef0dBC/h^MZkGJ04$*QOV?W<qO^^P%7+a>EZ7IBlu`)X-<Do+TklCP(_&3W!GtJ"b/dTIF@-6>XfooUPP'W&Rh!$']pTsdhjSlh7bkbVYIuBD^)!I/JKD"%H(/F@nBR\(1A@8%jU%W=>b2HH#^8RHh[@$iu,NnV_;]7;&&$Lj8#V'fu60ho`qU*bT!el:N%<i\L=aCgY`"4/]U>iK;J0gE,)nC'"KosV<]hZk`'Ft9r6UmW-VlF'M4ak&W%]kZQR6E$l<ptP@,2c-l.A-Z=FFL67$2t#?$R.JsgM/3TJFRi]['&G[r5.,f'ZEs'<893f5.T;?RZs0F3m,lF5W5Ba=$/$NT\B%!LmT-DaQT-?2<jFLu&uSdp=m+2\D+.:a^qI5p)bPhu>TdXONXc%B^iqiN28aAYp9CjsU7L"1^$g)+$20Y#JBqD9T'm5*kaLGr;,0@%nH5(34uILjTq_8NkiG$o@eL,H`;Cf#puc?+fZ_o8\NF/Bn6dmE,@OlN=902Lu-B&R5!4cbbr?$[6A+:J;XJ%h&hfIL<khFe!LaFj98?alo]1[8^4W*AR+7?saiPg7i`\^n-'C+"$UX_DUMF1)uQp0O(q>N%[.N"u++3O@ea`3Gl7Sp&D0qOOXcZ%I23]=K(YDt%-mD/A#H(L_)O2*\djbjc1qR7IHPOMO,`:Fii+X'tXLe5Vi?m<k=!CO<:oZb7fID:MUT0Ke"1p4RplD+j3!HI0?uoCdW4]L:A_hk\qpNC=JQ]TUC<mW;[t`Wo&Fs65)ee#>2u.Ao^:nB2\>-l1@tEDZU,Na3R>TJNTVgDmn2e+\g%-KkM[CGrh<pU!r<kZ#gJ&[0=eP>d@sU[nsEnN7sn<(Bo`"VYLY+:ESE`6alZ]AW\LQQ*Ur/Ih!Beu87^TX$K3NhjcJdnJ3W9q(\0oLFcicZ?fiNMRh`kQSJRWUsEEF@Z@Z61)cILuT^9U6)I,&Af\D@MEgEa,u<>Jr!.12IGET'#VM']T^'d2V`oHf!B?6)p.%,WC2tH1(eAHoeI,:=Mh>[Vs-eu+/k+EdnVRH?bN=dO#LE,44%-05astc4]UF#5oo1'['eWN;J"Ek!$NihJ;#aM;6A\cK:9,gCCQIE#"R<=\0@;l\f+6'^p6Ek(q$S+pA!R.#pBGCDb09erH(Q5T!7JGJY75P00Sj7d=!ea^/ro_IJ/15?llK`JuPWjRW3==IugMBir:)b'#kdBq6q-K96rK,kkV$K->5Y)Xr\A"DF#7Z(Yh7(m(UqU^cLm:JaY6BBBhR=X6,gXcou\*;\!k9Vg1H['q"Q>>pmHPWi(Mu+&s.RYdRQk)h]WV/<>5Mhl)E(]R[j=GaRTD(!pD(?q5AHpWmj)Dio*sl<-p#m-m,B<4^]K*GkA>4qEIIC@JSc:(M);4f2l6LmEqoP53<*d#X'!&:.;ki*dYjD>dK3YOpK=6]dX(9j3T`D?'J.^r?O:RQPnZ%rn%S(Ei8lfH?TGEIsRAJ7(fKoI0:PQ4J!`,cC+?Z"$7>lXd)u9CsiS*OT2-rW(mH'%au[L+CTial[$\^pVC2CumF-4<%%oo)\<&5NgHBLBh&&_a1.05StO]iJ1H6VWmB*rk;uD*]&%k2QWhRdbZt'mi+1nVI>&RlGHnf[64M9E,=_Smk<R]l>#>cY,p7GYoYmb(oEZ3*=-&8I<.YiU\raE0-(aAS7ao):Bn2:1^623lZq@XiuhfHenU,Te$Z#2mk,-$SGj5>@['ioq.o+C[%V&&K*.u*Xm_4NIF[6.D[.CIH:`PE0b.)^qHc19DOQ1+"ENeB0p#E$PJ=)G;WH\F&-!,)*T\IRU\%+]Mf9Bb/GSH:VBFQh+4L;uLA&?sFc:9h7a2Hmki!aQA_K4_S=NYhl8LkhT]sb1f$.Ig]hj7:?8N@BQtlN;OEZ4"fs)QCkOe__SMCfB8V]HCd$AOf<EJEVkpAQj<d>>R>DX=>0NEeJZ%6h"N2YQ8YF.q5F'G]gWLY0u*cJS3d**O0@+ZG,IgeM%2L)DQ^1$M.bi.Wp%oG>0)a0,Wf[2;GIK3]"CqO,p::m]ADU:@#DGq'K`d(\:1\FTS36'Ck#e=p[+N:Q&j\LrY1.sO_s(jj7q-'I7%o<q-/3k4#\pMdX!dEB:M#6m&e^&.V"s/aiR@U],J2]MEhCDQlfllkO<3)/=I/r?gRbY1E!2/o(5X-AtCUZ]aYhF)9[U2f/`^fg?=lak<"f(/qCWDVBVl,lldp",X:;Zi]h(eW+<it1FHbc,gj29qMD_T+Tja&mn0#VND25eSXUqNV88GpT`lN*RS+7_!$n,~>endstream
endobj
39 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1849
>>
stream
Gau0C9lncS&A@7.o[-L"<hjSQ$RAPJ,nL\BVd.6ifF1:hB-_%er;(h)[UaYN(:3=k;itmUAmW6$r?]P*8%ARf:%W$mJ%QF?!h(c66s`:Q$TaoPgmr],(O74G':Bs)!I6uV5EK0A1jD"oTbg4-89#\t5oM*bA6p*!&QU;`^-'6/pgCKk,+sljaLu,2!.GYe/90YqH&rEVK$\R#b\NYQ?(B29hgN)M4JfB*\iIG5_Z2W^k9&lb(GBJ$NTMn')mKQBD]e,0Q`!]l^\sQLqH>bIU%>_!ZF^QUZ-n?"gE9&OX_F*4<)'&t2*&k'j!78ao("%ur<1^d2e(^`hiSL?ShK([hN@o>KLkW"_H_]:A0c5S5P8Ju_8">A!\]OCgbA^\b-ncf'H.UU^B#AM6+jc*ZSePJ:>*Ve!:81*R@5k?1e`N]b%X,MF&q_ii-X-e3'elR&W1BHN]S"+ip5[$cOeOo@M3ME8<_9@&kK(%=_@c.LkJ(4I\)Z<Kd'4./Jp5-2<JrsRh-("oCVBA>"NdHj]_>JCoLBU\hLRW/(M$!!?A3!>^\9[4]FYd'hr[B%Yq;a1n-:,]_IEd+d-Jeo]U$k+@N"VUY8OQlFtZ"Zt/B,<lS$OW)YuHFphH$27-s?pekcR;B:9E3&]28WsZNC8n]^1kR(:tWeJNW=LD+$h,7gQ'hF(TNNHc)>*9W$N#=O/0^Kl#pCP=XgdQp!NgXlb29DC-RdG1FgiI"q"t'9eF-8\I.M.r#ZE+OhC,n'<aDN!?[]bdYP0drb#gC*U6P2,uT.c"qEPYMHmd8):9:(1/f@DKqgE'%)3!FC\@QE9;?#O#]=gaQ4$bbgp2MKQpc^PD)q00*$@q0otS7A(o5"j"$5Utu>oi:!U$hZgib6lAg!/PWDHo`4LN"."M6*4=.l'o]FPfr=lSE[Ai]`:#bI16sc7C[i)YqW2#fg+BdH@8&1NK_jZIEnIDVDo/o*2$n-\H'moYi+pc'mCI3aV\n-Ri_>WgtiLn(62e#W*=(:*fq)1/=qCEk8IXIIj*Pel4CQ"Za\@dK+P(g]r9:7[BI&TL-91V2#p7r8A&_@E=';UEok]&\lZ)V+qhgtQQ)o,;t`G_KsFqrXL7`b6`KPj(WsSZ)'0urR23`MQ*pJ3f^Gqd!%UqGgoS`M6T7S*V0U;,.0=o$n3q9K#-J'n6.P9.?e17!%ddrqd2P2f*Q$(s-W6GgM@E&ZTt>bQo5&9o;H]s2j3nM^6A3ZZ`91J,fX.BU7sI_F[6cO_V/MP'@0;DG%kr`b/q.2_P@;`1AR?;cRF?0p>01]'e%dI`Z+d'c/<H=?YaA-D_dD<pbUTXW!Rud:qV9SJiUg)!9H:C87#_Ok?T]>hi!(2=H:QZ+nJVT^fPOWdR^)JSkXISkOG8^YCPsN%UA)TQPQ#H$<G&jlNa8Ja>)/2m/=FI[pgb!GK7$qnJ!8e0fef(Akh:-JG*BP6)r5ZE3?N'r"ngKWK/D55op"M1@7=XXCN,;))4/-0(Pi'Ys2%USe^95UPjR(VOg]AVrXS%)5ktbpaq]<QE6lkV(=#7P>6ButoKmI9>3rOYWk`;Bc_h,WV`>8uN1eaU23j?5dERP-Fe0oIJhR<n1>=,m+_c<&aeMXY"ajFVk(*M$F4%Idd:EAf1M!LXmGU=C8\B^$,j2h?G':\nbGp&2$6BDC"/H\$/2k*'8DT4T(X>*UA?;f,)ZnZ`0_37UU#m*G0N&ql2)mMD1[\DC@bL3]C8uCII@(qnbimPUWU>iJn;e%SUUf9K8?#faRo/(<2eXJ,c[@Bdqg8:9aGu8Q6IcIhS3t+p=M:.u?ahian<tud?_+l^(Q&P^mnLbCY&@\F"#leK%DX4U)u~>endstream
endobj
40 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2285
>>
stream
Gau0D968iI%)1n+i95=s<YY3hHIF_":(jIaG7QdLYS-?-,DhF%bt`ceK%)ljgc4Vj9G13qL_%9+&0qDeJ#N/^V#YuriPR#Me;!''W'_P,hS@\4m-0I*MhW8V63E."2@qt"0-K/BAb^71)0$\:^^XRYqlU0I5a^Rl3((LXr>@5F5<.DrYT_@36K0j0n1AL]\/t0,4/<G?N0k?G.Eq3uk0*Ru`OFS^I(Y&dq2-mG+)\:MQLgSiYl"OFpWZ%YK'DB"(m**gr:0>"<E,e=.hu;(A^8I6F6Q.`$<FV&_kkN,1)="<Gr!`86IuT-"sig\ht$9ONt8VjM:F\?3Q.Ypml&HV=&J8C#G1W83n*Dbg#2U]CFhI'*blAGFn%cXs%DFlME+?2,utK/[VWF%qQj!@ZVokP63(\k/Ci++2PeR+>pk:@>p$/pd(ppB/fZsGKJFI)i>Ate)>+)+?]dnHLS!>SiO]*s4JZ#B*p2-@Ws'hd)OkSXE41Vq$o,WdioAS66F5b@l?^EcG56CaaHX["eai=h80#gn?a^g1g5jGaLt*^Xi#1^*3GTcM[gB[-/3nQs>rQ1f_=9i)"CZh5"<OGkpO[@o5./8CEo?'ZQ^0_?2B<ZC9W,T_SImMo4;VST0O$fB@Z`'p"_;)K@NLFQEZ%FX&"Gl"CA+=OjYKCfncCd5M#=00`9J@2.YW\V4Q`lnKh,d?&t":gA-L"b>N]-m+)sWfp-PN8<LG8s090T8CI51h`"*3;4mGJF4QjKnM5)%)7**D>esV<A>XUoEgn5JlF28NG'l7s!Q:2rseu,5C1!=P+KU7(gns#?q8([?<_Erp8hVUPKq;$_%hdjhM@T(5Xdk\kk<M_1G>l,$!':u14&ts63HuQm+g_#'h("gM?E87;t?Q^@re6P4mL>cO#bM$uY\;jG,g\963jcI/a@E0:6KS0+GbIF6!Kal5mDC]K#'VKN\g,Hq*AWS?X.-gZJoEs(oe.5hlP:":d7>IUs`5Pig)jm9)bT$10MdZ)YX]gjjA]N\dO`3fB7D5!/phr"@V$mH(l[=j`.8iOMCJ8H+TL8#MF*D"GA0[tiS%S1Kp4o,jgVC=a;6CPJo1Wbc:B,ReY5ZclT'tEPEWa;OLkWUoDlR;cpdhuk)##pQ4L.Gc-$k)MG)[6D)K3;&r1]d,S3FUklmf[2Oi66.i'XPc[M1aZM@*"XbTsVQ.%Q%?na8eak%=Y?q7LQp($?Sk^(FMllWX</GtAD1^*]Or>bjpBUS'>W+#Ig'9!Q7U*d9YXfJ2J;Y"HBSk-BKOaYg_/:)ED[WMe_"Ve.$Be>Je>,9sQsdJ]ZMo`.SJOfmTfMN3f^'o^J09Vp2@kdbD1V]\6o.L!m]VV$L=C-YO13jQ'>@7MO*k%'s;;YBUgb]nl59<QRF;i5h:&!3Q]mV2SXO$lk;TR&7XWFF]='n?aCnotF<a4Ctj64RR3G<6f`9^I?Y+*^g,TN2'lOo"h#pbKZHj'm;o^F-:1,^]5(ghYDZk`+..,q7q6;&FtPkL4G78<'Or'6*fV6JWHKhK/%e[_+$,?J=!'[hEI:n\!AaM=Jbg=)Og2oGV22#(=C)I9u,6+f!GKIHuj!9#'No>3V(#Vht!d@W,6Vg8WbVI9j<UVr779-8eNa,@dt8?9GVp0fL*UZk[Vk570NG(&S'%60igu#Mdi78sr+><P5%d3/J0ClDr-<Z,N4FLK^X:J#@iQ<EJqYs+8!McA3Q%%,!lI4FandBFSa*hlb:]"4,f>1$oK\HDp9`\uUcj@#\JsK8G40'+=&Wk<PhuS]D\Jf>CXBLWY/N8$0QE.<8U&h8*6P]0VL1i]G%I/ainWg^a:O3*P/JH8uB"3gs7/%9iSZWJ^sC_NOtl-8-:)^&`!rr7?j])r7Z7oM-*A^kQa%;mai>4%#S'mC1-R]E+UT;EUZlXjl.7onW-Q>/P/FI305$'j^im)3^PL;LuYnCbY2!7Ra1]Ig2sfm9[K,D`,Ku>EPE0+V)Dj+"0Ma\L\rtMG#']:MhR^ksD3Jdbu&[2U0nc"<*jpMF`9.bJNk)W@d7=X3+lhF$,q5du!)%Q>_$I$8_&<e7O*1GSVh%-.G[i(#k(G#5:9I\[J6Jrh;CmSaSt0QguNU2Ic9?BN)p!r[>39RQkn945Ul.XlX?$nSJ5T1>U_LMI"E">T.gZCOY8D<$du^'tWTthmm6+[7(kp)qd7_9mlX,dqana:S3bH<KuX#Qp4TVOSsn#C3DVP`%3G3mY81K&p`;g%?e5OVb5I,N82rbbn3`_p8IU(D:CKo05Z?JIfY+S5um~>endstream
endobj
41 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2319
>>
stream
Gb!;dbAu>q']&X:m\Vd8CU4/ZVft`YVs2inA#o%;)jl4fB)%HRde37uHN93AV-$@=;;CDLClnjVHZ1(i*f,r:&$u;"SET@)IQhD_3)/SS4NF5]"\kjaO%0\$=WB,9=QG@,PJ^o%JFqj"^rm<'Npd9?IdB-YoE4*8,J:h0$q9b0O0C#rQu,Q:MN!8A#s&05auV)&hukcq)+@diG(jX*a&a(Pl0/'*p#V"L'=o(;$,LV$3VdMQ\p-TkcSYU*>KH/=p[TMgS2g/Xc/luWdc@F,9:AhfQM$g<fcj-_]&PoKG@`L)B<"L#R13smYocAb+=@Z3cjfIbie$heES&JIT&VRjoK-^$<K)WV+Y^W`1P:rK48bf4gE#X#&*$FKqm\?TDj@KnKI9B$Nn]0J0)+,$hlDpgU/niR!<E!ilZ0Lp5,9C`^)'_/DB5h2VM"2&?c(b6F-p@_-6-feH.Mo/dO-;<s'idUI>!26;1R.go)3hlYj.D0`fL5A?%\0l:`hLUd,\eb4dt46`nl3LflB9fRG?_p7rg_`ge<1/rHN>Z_;rq_%>6BEjP4b,(aJkjF+-:;_)Zt2!$Un64267%,sY43&3E`'8%?\)B%QlF+GT2<2i_.([_K;"i3Ci<a9Cl3()e8;!B^s6`9L,Zr^\hFWT'o$T$fO39kI>'(U?U3P-^<N:61C9F\-XWWJ]1Zs(-=>M4@.PE_/ij;gJCGe`g"hk?(HZ"d4m/W>+go??D08k4g)1@**r?c"b<\/-e.622S>pP"@!C=ZJ==3n%@==6d&q[>%\&]Mc+t2-05`kmU@(HHE!a`QW5:/o)SuFZ+1YVo?<u\]dX-r7k7/Xs>U(<Zn!_>WQ5Q4X^C%7?o&T_[3#Pe(,`SajEn/4EBiY\rs\Pn.h.ij2YWc#CL.f(2^4$I`JXi\7nOq*ue[DD69?P#nKY-Y9YI]$>h)WU*6to4V@.+s"B\P]Y-WkDInQ1,.=2)mnQMBKMEM4!\C_!P.+Eu[WCuc&KIr0\M/YAA[77<HNMTa45HF5ZMnPG7GAt[XS.jlM<R''k3cHKKn!Nt(P<`[@;VoW?+=3L2ckN`=W#R]>1EtVOZ-F6ShB,&AMe=%?=b99KV:/tJ4P2.Nq%K.&X*!q8S_<b>%VtsFp?#1p7fGJ*\C4?Z=PP*c8&>c"_B0uf;_:KT2J8G1mb2p_&j2nY"$Ur?@^fm*5Hc+`6'b\=D'(H'M*es+FGpa<?t:>'&"pbh:2#&T<]9*lQ-PnPqJ!+1!H`JeoS,FlRoDmDjWF?DJe&D>eIfO@23$h0QcJl+cAabnDCpq=@$Pi!Th"\L?V&sZDl%&%+T#/2V(so1X^>D8QkFV7/qe&F7K6E9\*&f(D1_]eEO7k(L#*_M9]3=kcK!^=ANU5Ag[Ek[)i`I4sBJQ3SPKuI_a*OX(q8lPrtGRDW8rH6Zu9sX)+lQ7qf"$&07uQcF$5k>2GsXh.N9gnhea*VPGim@W=l*6l.KlW2,-AT3r+#+a];IXQc&-mZIL+^X#_bi*RSN15aI2,7q-ET"YeDNdsC$C`Gi4KCsV.DN,"06_:bAK\P#gG;k8d],H:R)iW^X*roj_#&r0)GT-q$2#t@!T6i):\?X-raoIoAh&cCC6@?J/NGZ0EO57Xj_3>`Og?sVE@0@hck9AP-N#5N25N/)b&SKo]%`d_"&_N\9e0;pH9SDE?(In>7#buB#=5$:(FVB4:8HlLE?VM\hL,>XnV3"A5f24>Js$@mJ#Acqp.ZkU)EG7I2B&o%:q:HoJW88RTNj!p-F;gduqpG0q4JB7E%sMqC_^;52C*8q!X_Vjm5GZ:<bNe'#P^6s*<T116X:_->oE)r4?>'FZNic?HQ,OrL)pn%T>/ESuh5,,]b\3dQ[POAK]jef)*h'ak-WEa@k2m6OPZA7=$\&Tm"1+e'MC(B6N4pK4&#oT3<G#"IJH/dlI*obKEs=MYL"'F<&5aSC]I\1g!0!-8ebK'm<8bR:C;;$+"-IoXHdKiMWGBt,B+Qt)/S.qU.T]O8[D_#?dWM\k':CMsp@jE`*(c+4iF.oDV4M*;q.o63an&CGeZ-3hjGYeRl$">KlF.o1QIeD[TI^GH+nMB%m4U>(81lBBHd*BR:':3Gpe%"E'&GUSWBLli&:`,uU*-HX)I4Ja14?U!li6I\q>H,ajM5[FK(4RZ1UemBpV5=94%.Ln,Ap]C!u(PhibIH_NS'6Ze;2=]:)*N9,pJ9EnS_<]/#goBREpi3J5#YsP2O+,LZ5r0_eWi[42'+C9%m5,GL;'b4LV?u@mb$J+::%S]c[ZogtWL76#MR>WXsP."]l4@cG4>-[7a#>!usC%.f~>endstream
endobj
42 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1722
>>
stream
Gb!;c9lo&I&A@C2m*SB=Q2n[J;f*i@:7OA%g640D$-C8M"N(bJdpLu;O:k.*eg),-AidA3JkKP0/p?tP132p&lt9K5rP"u_hCrE/2HsY)j4!-2XZ).k$\u8D#oP`SG.iQm_\7*DlJ82QQXf/<Ja[ju,_<k.+=&!iSfdfR6IPSh!h80S:KnX*@cJVO_SH9@Z2mT03aol=7PXWW]>LM=Z^Z0>c0EQG+0Saf\Y`0'>L2?>\6cr-XPUX`c"GbC*&]Pa4MSRr`(gs@d<tgaST]]<>>=F\X[RU&"`1)79t-W_FJ6m[b%-niO\^?b8J&n87ns]nOW3;0KF>67M*!'%o,X/$;(s6tWF@Eq(IF<Y,VqP.F2/kXJW8SYH5Lk#+M`]Is0o)!>L3%#0urZIF-d.1eR+:_<OT1c2#na_e`picq]kbX*r$'/d>`pHfQ(r%pff^?Ae+E74GRG&ZJPPudg$T8rehAs$Ua&$8_dXG!+C&QGt\?^X`&Y:6oHPu;jk[%JKZd5(om28=nQFsh2YlN&at-B!_:>?FI928E<e<m'"e-%s6C]R_P2i=<@q'TTiWa=R3\;\W+=M;k+!o`.TE*X#fWZ6<+8-nXgb<EPV.Z)W"`4mU(pm<(Li]a&!&FAn`_`WPQ6SJTFur.mtL-DTL:MWD-)Wh^t'^91UK[BEW:.UY"_*d0"2.(\k:NaC?UF07kY+D8d2'&+HaeNm9l-YF-11:FA8`JoMMiqHTJAi[GKkMKu'e3%*3rU)UJQFSLimRJ5-hW>_uWXC1R*/VU<K*&>O_N#5AN?$,gnLDmeH`OtK-Tb=pD2BiS/s)oo_t-Z#^T\lIKP%`8:[;g$>J@Fc0'<Tlec*Q,ilfJ;cB.T$[5B[:WK`.j.%Dq2M*DpPEfKTk_m&kg+7QVIjGC+Qg)e=t,cDnLL</;-q[Q#+FMi=cq*U]<ia*d"^k^V$#TZm<2I+;Ro+[2BeC3Rm:,@/n%"kCjRZ>R-pk3!%pTR+m@F!rD\8iHY"&+5UF"`AkTV18dp]anKEp/.X)qc:h@.9d&,G"fo<;'7OE+C2`TS#lG87TD@Z`q[\a?9SVU4bch^>2W+km"NcB8%G%,JqOJ.$c@\YW82GnqaDZRl+WjZM!.GE&57O3[?Z5MN&(.R@N1PQJ8>uRllj^4S!A=*DlV6Wqqf2<e;UN(D.``3UB1gol'^"'hClt@'=aaJ;s(n=GHjK^![Pl#W=$+np2gQ$VDr%35J"DQ]0636.8$s@Yk4d.2RBXi'8'kkh*pfATc<1"%$JCAjs7Q;@KBpS.nD.7S7lo$i=T*nR3i28S=q-.flW0%p7\A_t%cX6*YBT7a=5[&ZR%XRSkndEf9<(FEG3-CX9(G:2h8j$eR0W]Z09[fb>h3M7Rdm<p]NO$H7UePJ6p]jiX<,FjghNRk$HqqUG>d$a(!;_n&(t*#MnafhigDp,e4b2;#$)6eBa;ob'5WBMK_F9+dO;/c.h[%Q`BDu8,F,:[&CmMT/F5]8WZ?^q:5$!^Vd8-BUS@%<]&`uqX`2DYUdC3@GrH!5o3+tkr4WD:W"`E8MRldKmT8Y>)kfllg[]tl0qu3"1>AMWVOCM,k&nZsFY@Hg[$J-cNZS0+>AG^rqY+5O$rg6`""n-?BA'.@oSf:qHKtcPf'Pl>UfFtC]>0T4[>@KiG7\-P8Y,MVdcLM&:S-_:M*0bb%'3SQd359X)'=60d-U/G.MiU.KuHV:>\NXLRIu!B~>endstream
endobj
43 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2151
>>
stream
Gatm=>>s9;&:Vs/fZ1?4I!"oO0l;L+)-SGam<Qd2i[@*pD5Fr\VSE,3HoE<9W.u7p]X%7[m\`i26%Rr)/UrO1^q0dJF%1^9=DWNAQ48`[3*f4,9e8nMi)6eX:O'[j@Y&e*l#r6Ja$/UN@ri,Ti)n3E>[Y:bD:ccK&iOYU3%4c6n'V@AhfoDX[s!:?BoU)O/N7&<"eTe8*+9Gso]fo"R%IYjJ""91;n/5K3G8-Vl1FRVIdmUX5@F#i*luR`nF/R0Xh_9ViU@q2=Muu:24S9(.e4m0kZ3BUQAg+Y?7-]"Q:\#CeFR5TNpF:sXMD5,io4;U+(u<$dKP9X(86cOeA8ps;<m*FY)+G/dqs_?j7b+&SA>ltU!3'I#9OBRD_fS'gu]NR74QZC0W06nRV>Si0%335!1]\(Fak<(9"Dt^4+IN@Q`QjU"0qdEE0sEQb,IUY;nAfCl+Z?F$PKH7ct2l44NFpY(TA.h:\1[NPsC`qK8gHFNg6$(+p`FCIJN^0r-E7JEXAAP?,:R(OXN/d2p4,^0`0h]^-t(B529S=IjY[8bGfRM@HqiqfB7:ZA$Fe)o)6AE7KbXTa!YbSHqFI4f^SD!_s&qgRP1POS1jQZHn"o%eBIk$VO%jk3ml\]_ueN$SG>W)=NaAb[fYIR6n4jc+]kq%:rffV%s")>6,H6I5L!+R9s:tMB!=pn:=UGKXBW,pdM&dI<,P,/^Bt9ORE?@qMb'a:/eM)i[.ubK.b.@+"Yu;$@$$c-if?Q&^d%mT(r9e7o3&oe((]397'5@_,:.PMi%A_*4KDM]*FLt:@\$mB+)"X,LH)t2%W@[$dqbO8rK1s&De.)E.n6C2Yj\X/a:pj#IUqfSk9Eko\5T2lGHBU4RM>P4nSK0;^;tNR[@=!raTsgMTKCAuiu(=g;;Gf@MX0JdBd^e>O-sG.b]o+gf0\.7]QSa)YCb'm_#DD474t#k^V[=0@Mu\SXd/'neY6h"&'^.TF-NPB(O=8qW>4p<VR>U9C'f)_*gg`h[JE+MH02BlFNirLV.2Z)dnc?YQ2tj\9Q*a*-uL(YHQWdDOg1_dF+WG6EO$,.6:Rnp-s)PfWal=-=:<nLorNk)NDPu`40b!s$ki(L",i'"Q2>RUD^@/a;!/`hNJ)UajL7V:GEL,B%0$4>R"<rbMnDRieeV5b,FKEP?ATrGT!;J&,C(Y5!d_P'16#;"%H_`!S0N%5CU\/6W=h1],T"gYWo=HK?=[M`.Q3RZUW_*pVFO,R#R5@f@/JI_*X<QTlUKj9N8&Eu:h9(_8rL(,SZ,5G.;d7'-I20G5Fgr'X5i--?[Xc3VcP1_7?jj:$>^))PM<IZY08h?;l@8:FN;[H:nQV)U/33$k"sE[g`kShB[R3A<FSs>MCfQ,P(a_DP^6j1P`;WKe9/f0>*Dpj`?!>?YarYR'E_S#@V52D;-Oa>4qHA-CR+:/U/iaA8<)f_FsQ-+K<n,3=>_<rdrNWF@9#*5:R<k9-mQ5/^?`j+7l.bJ]nF?LZ!l$J[L;F(eAq0F*?!<NGkuW18oa(0ch7[M#beEgQRAK5W4:G"CTABW'S-JoVO[u-RUf.N2!tsK=%Uml!)H!cAUC#bUWdHK=m'Op_EKQ2WlgN-.,6N#h8)#*;q$-/'mV^pm?#nrHr%VRRs@*[PBQo=JCii*kGi]2Vkjd+Lf_)rUb\/b)%Y;(Q^$+#ZZd\]-V;WS'PEbkVTg'F@$-78HIUnXQ/tONSsh18Sq;+7SO&R"]GTke:2HsWngpubJc5*>Ddf4DPih*"9&2/XbfnVV*`@n?f<M;DE`EkJTjFQ>0cbU7\s'1!Xa_BuU+N:`iPp3Z7_6comZFt.o:`-BHIk#-YC_gopC[?%443akfq9"L&%C=t@km@6D>6t=H"HsNh[C\;Ue8(DnsVoX*g&d1C(b7/ZQ:C_./:"-Dj@_1035qYb=B'%^\TY2[n?G>Gr4(;U8<(c-cQcJfBG(AYq)23TuK/s()\N5Usq3k\@OK-/0:A?I[.CG'Ri_,caPo'a1gpQ4==?!"lT6)@bG_P?L5YeD3en)X4[-#[P<l[CXVP$lZ-AUF"u<+@C%\VN;$#Cfp_-O6p$UCg$>&"ZS`>=@)?McEd6JnXif#iHbeQX)]aCmgEeS6\U!VEga6%PNO`m_p+PM<9`>:3i[kr~>endstream
endobj
44 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2180
>>
stream
GauHL968iG&AJ$Cm&b"M7L4P&j/=e0`gO>[CpbWr+;0jbOG![uEi6[d$]6UjZ>@(?N"V&q!nbK[\)/f/_8?e#r['Y@LDdc)7"I+\/V3c:LY5b=R%=18,LiK.5_7;'j"UYQ@eTuWeQIH],Kbl43<p-(_Enh7M[9%Z+(m!(rcX$ciWj*T.MtQK6VN)4!WG\q>=:`.R)Ka``_!MS/;M?lfBI,Jl2(*g$6LqZDE?sNiPX3b+1(4`?X`Xeb^O=dE[Q*\HQG[&%fL?jqi<)'F&c0D0$;>ZC<cugl,:B-Yn2@C`DWV68@6T1\k"B(.,[AM^^n*Y^I*CASPQo7);HS\'qo<3JZb_(`!(_`,s!jGS_;^='%*6-J9r[hTb."Wm/K`3EmB)NJ.cd92"R*e55eV&cpadI@F,!:T<?E5(gT[XaFkX&BAF-#\:K9&U8R.XL_@jEZtiF3QA1CqOf!%&Ui'VVg2hok,#Nm;9(KkZ)enu<$(J8\H-SD#7!aG5K\O\^hllJKZ#Me9r/<o2_Du8gibleurMK?%nHMo9Gec=Hc8lo#B]C(Af^XL$TA)?gi!1unU,^"U#'Z.%&&%+U5/Mi2pHP0G4:]1DD-EM-hW%"=k(rPtTM)^0JE`(TX:fp<cAY9jk.IoSNPn#-G'9`>IY5mY_&d[1`kt0tciso=^097U64a/._l_\69Jk\Ln*m^'3d3ZiQuKlH9P5eJDKVB>Blh!5H\*[2KnN;+jVtQ>-DH=E<pO>2QH>5kkMD6oWFAOJ`WHE-n.-tfgjH+>1H>lBa'."Peh#lKB0,Fe@msdWMH2?.eXJu2nJQAeI]F=WLkm;U/aeED$#/JtXjS_*l'4Z:$QH\C%G76F_ubl^*RZR#4<GVo5;n7C>ANHTR`X(YW-+r:g<*1b,d4OHT<I?[AWRE;@Zm3[AMTOs'$gOk*Zesa^Xoo1LY&.c`^t8X\I+bikOnL&B?5tNl/VXThPuPfV$-s\>7IA8;ONsd,aPme%22ZJEN?L$+19=AeB/8pR.Gj+2(k)l::)H\7c:V[\@.g.i0Pd>^OSB^e2^&NPD^=uK![c&Ub6FN*OXV6K48&&lWcb&(Dpd5eJ^A?-,G$2Xg)QDHK`tLKgjp#c44(iKdo_%M9Oe-B:Tk?o>N.dH\jpifcb9AaPFsAY%6#YoY6X`!o!jK^C%S(4>O1uL8Q*-VpT]B<sJ#_`r!6F<-EoP>s$u=Y7\/NnWM'^3ohJ1dFBO&r4p)uqJjjpPJablZ6V-A!9&ZtE>a<,Q<F0I.E>kF:lB[1^;#;sbjM=NO$:q;j@Y$R5&>ZZK\hi/kfV)O)a61E>,hX.<?X7mQNQ5H%Z/a%b2@i;qDGLPa3r,b.J#C-5"PXu.kjJu'g+q)kNbgPBHp-hSd"bn<GPOnV4bZZg7"c$GccW"5dE(DMiU+[$sOp3gf;4F%nRI^D7,W..Z:%/dH-ZU'5>^9(Mg9q_T3s6nRCU]0tFoO?l^ih,17+gH(e7cdE[]1aprubMBi)k$lDtn1L8d#U;!JqL+:XG$^lC_n!ASrK-QT?J+DK2oY$:bh"a]A]0E<l0!NW1`GY.tA*QB1E`_[`]jQ/-$&'Aj!cfQ(4eLLmaONo9pOAP;lhC<Y!h\1K3Fo.!A$X>Z]BuKJ.jX"FLXWi]?/^E5gfkE:p6Y#rPrUnR2>^fA:!LKBRIa*N,R.l!CHFF$FX>4tTbtg7N=o$]lLdk^i10q)*u);1WKMV(USaDVDpDJ#4;b`d[?C\=AC==SfE,YJk94&?3^H*E/f$q1<#7(*WWI3H@)n(J]>6AB#"ZC,mAOp!$=uqg/$F&(9r=^<gu=&#'8OJF!LF6_%d5TiKb(9#B&O:RO6l</=-!a%l`SV^X3(R+4jL)J$V%:(hP!M.'5?RC#@K>mFFN44kGQ_g8R/tUaW]Hj<S<X4Z#qkI%7N.5VRi8;G$#TkqWk$!_rP$Ko0L44@3(JCQ)jFjK+0&AUu#.#"0TFNB%>^J7b<2\>co[PT6LgFh,W^ZR^>g>\:-[G2lX.'4,tihYYis-k4&iYs8.b.Oj>KQ,4\)s`'E=a:MrCh&K`(j2dQ7KHH-KN9=4]S=srf_IeH5E7oQ#4aLs#AMo6`YYJWgR2s`sqg(E;J'=h1M,EWr=+r]p+E7g!M+%pR['u]W#4e2?(o3a]rhA4O7g^aTL:>b'Xann@Os42SUlp(=N\'G~>endstream
endobj
45 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1927
>>
stream
Gatm<9lo&I&A@sBm*X(f/AZ8uVDrV9*P4p[V7l'oOcYNh,T)&R1AUTk!3%V!:6gdX@nS"3i4XR9G#r;:j+k1[78<E#:4HM'@KF_T`$-k/q$g']B:mI3k-I0YjKZo>o74Ir[hhT=cPep9Z-\E(0Vg[h]c`:J$8O8nOpEQ@X^7Q,;uC%24!L?ASre<@GQ=nP&PJX=8KQrOZ68ep%eH.7hq^ugPH3Bmm=a=il$foD_Z4UmVgm#?2o!c<,L'ST)d*@uD]e,0Q`!],\G[@HoX/?n78qZLhCZU3X_"2>&HjOgWb*PMd1haFHY7VLS7G2^?CjQaTAVTG)n(T[^C*LJGs7/8GROdC`E8!&&fXIH1YDtR`1M@\A+5-?YeWol(iLu)ItT_<0FAOLcQ>CWILm%Y4r?_ol%Epg!.^^.e38kOUgY1[;,/'P40`gm%_e9X$Ra#_a$Q(4_K!WUU4(iZkrfYno3`2i4OQ4\Z37.,e3D6b]i0?"Z<;!pDnuTPl#WXO:%.7hPPHD5m-V2RQH>WL,4nh3L,5:Hjk]S.o%GG9li>c6_*sdTn%`N,)/K44gS",s;>Co_!6TM(8*+NDP2+6d4Mur3mU+`k;;L",LIurL4Gt2)hiXPY<StlT8jSY:]eg?.Oh.@`J"B4Im,JA%Jm!Csm"9m+Ke,p8DfEX!*6`!8+MqBkMFp;fLLt%h:+X2\49eFm*Q/'dr.prZ50":(!uMlmAHYXX)!2!A_En8"9]rmjd&I^`2mQT8)E.'C"QD,J,hRaYU@5UcAT%q4J,sSrBM+t4%G/*\H?;BBOf5830sUge<bb?\@'/^F)fJ*lGNZ\mksT%3hujEo2l>Up]uC.5Z?[/GFbBX-.V41[Z.nt3.OnDl\A8XCLqp:VA0Tk;q-:l&QW@d6X!I_%b_jm9A82;ibO[h(LT"5n_;)&("'EZKFuF`-d)nVT=gM(4QWA!;(/lfS^/JX"2D:>R[eM'3[^)rX8T6)NfJ+c6`33CIC2$LH"[,/GJ=_"^1i[&[gU-'XY)gp\Nr>/U#5abEESf0A2]RC'"Xau,Y"#a?!IWcd5P5bmeoG7[WJt#ig`@kh6/9%eMjeH8V?St:Y7<spO''1/mFJkumUW-X7*S:ClA(R-Cl^Gk0=QFd<7p:e_.Q?L50de<(7e.*44+M6@H;/\H`%Sm/1!&N*.]G/:8_7]"+'<c2FS4Z:lfcGaBW,#W9eJfKNc5TJf^^*5@R[QR%a?`[\?)]Pda7"O2&km.^?EdFL$':.sf[TU1q-eUEUGUZq>i5"Y"L@hVcj7$@D-:3]>dU:<-F.f1-.fZBGTMC<.@#VqQ9'lh=1S`\@![f]U[jgZOY_[_6W!@sNmqcTV"2\)7GR_r81QM:etVo)S$@.%r.1nNoRK99`)S&os(.Mc*eV_96IkEjmi0rcR7'>;[RHceW[9q;FS[KO/($S8:7?TBfcn?4.lub0:>GL[m(F1u.Zh-u];WJ%lcciP>)DaW=!-I+\i"ZI"u.=c+A#'H2aBW]<]$(r.MLh@'c.W0Mcm1p>KO_AX!W#;2[.rqjJm1ML'\o84>nWRTtI5o(`Nf^_(A"aV#Pa+t=<0/H=OM<Jb%Zm[V7,ueA@1><T&L^[S*ejB"3W>]*r*o:U23;Imm?M%^pY1$"!Elj50H;G'nK0"`_Eb[d^JlhLnKL=^?@sI2Z9H;V=E)t#/a,g;1@(s"r+m2<AAgDO.qB.kBhT_?UkU4/I[[Y,c:FH@-5]):T$aabTU,tFCe[4=C5seC(<'bmr7U&=?fdme-9([[OZ3euh!H/*b3IM</s(.M2VU5VNJk12ugG%GN0\]r<XuZ0RR9GkV-:EB67&(Zt\Xc"GJ;8],%hnp2?\?pY'j+LM:IhWufr8-!20tDNJl'e/:"=m5Q]D#T1Y+^`??5iFK^DL'P5BAC5nM\BZ.aS(&ErIFI;uB2~>endstream
endobj
46 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2037
>>
stream
Gatm;9lJcG&A@7.b[l?AO`EmJPc2:+D-F;;Rr%EX!fL;sR<D\'Ua+Q1d9jYL[/?1ID2JdS`3_i?BBHmQ1ZXeZLSqn09D!$oJ%Xj\"c[05-'L1"T_%]qm0VejY6n$`lR]4B0i>mpeBLcO?Ih5O9'/.58hj2CJqRaH/0oC@RQWZ^;>agG3^Spg-WI/a%_u",pnA*r<2/BJ7F%'MED*32(SgNajlQBK,C4?)+%Fk-0!KSIrbaj0pmoEsB`)Bp'I1-FHsUMfQ*NM1s!#snf(rsNo7#A\j`Ra$gsptYKdc22n2!#2^36A63/^e:cp^^2.V+q^pai].gRMgOAE8nb>a9%p##`EcM&2&:9-gjr-$/RjA1C%;KCj-!8u8bJrdV:\J"=bF&80\9Bor_0=oI.]Edh^a+5d.YF3AM>`YR"8'M;E5okk:@Q0=;arN/.JF2i:(O53CJngYZ0E7/)9de:XH$uqM(1L5N7Fo,[?*LrQqJh?gGZKRfL8=sqAki:t>f6NZh7\3ji^3auo=78ZZ?66ZII7L)Fg"m<8T<[S(ajpQk#*_c2Npto?)Gk<C()><0deM8;LO8E$`M%^O<B?=cp[`9q6>dtNi$hQ1`^i*gR4`QBj>3?u\"q+?d%?Go7:VWq(:r)De?1Cr[KG`9n2Ie!;aY\@"1C3SlV-BVC:WW6ePL1e6%Gr2=9k^s8S<A$=GQo'3-UV/d\3P/3,Gr94Pj4_@HRFC"`IXAB$jaDD9iKq1bm77_6OQ1R=rkmA>obBCYQ.eK7^ihYDOeVm;PF\-.5?NPe.C6-biCBgj*(l8Rl8_9<&r?fE7rSR>O8s^(`VWgO*s@C9?M7*%RE7cpFfZe]C3H2tYE0ngD0q^i,6A'or\%+]+nr+E=5hcK/(<@!$VT;gd8S6'_rJ97MIpm%ehE1(<^>f;^+J)tN&oX/SdS>GWK)&]TlLcjbNX"@!EIdX2LRdKf8R@/ta!.#1sXe^EeE&b,R=AO3,)9aL.)G&rP03P9B#'l^S/H-XlaXmKjZ%5`._pc;PMloH>.[-Xp0=ga%'C8d1`F=r):^:/1MEifB!VE<r&Q?=5#&%XX\HUN45hbQJ:D@$@=V9T"$?gSk_h(&j:6q_$4Qf5Oc8sd.!J6i4p*ds+WMEqXZbKKf&kg`]V3Su'2H?H.r+Q1IeaJjudNP)5hRX\Y.kRrXlr@]H19U6<27,0Do2(iu-Fe(r/0(OfUr7hmg-W&u/,%20"ciZI%($Z+]3HtPmVr<XS/RU:_cd^/^8]ON0rPHRW%V;0a1K6W!oiEQ8/2q-k&iK)MaqdAs4qA0#/LC1e6$>FteHZT'.%O]_./&4ns4ep"o1-\l6Kg7\SWq"S[Tg>2g\P##J.XO%*%EWo>??$BZ)7-<cO[\n2RWoKBEFqZe((mJGTO+a7GcgQrYVqNE%S3)>@[M$#keeE7On(EfXr7kpea<!,Q8Np^B3/Up&SLho$QC2aJYk"$]O<f+Z93RF_:CN#Eqh7Jid"R%sNesG>G@tpcleqYj2R/C0Xtm!%f7$&jA^2ZhFr#B%lX_d(+Te8je\;7g\'l/FI4+_3:^`#EKG1/)#3QR-X&-lKl^2pn8S&/iM(^Ve4IL0q&9\kMd#";Z+q(`U9g,S'H9_@]ebs_DafND@DVO)^5?'&rRm1"R_fR-4Y2OjkoJn4,_!Z=Bi7.8r8jOqS\Vs.lL+PG6Y`/^U4SHR"I_t@"bH#`8hbl5CF):,(I7gCSCR6VtD4uFrXQ[<>W>PgBVgQU"(BRSZKtYogpVMRua&?ghpj^_hE+LkF&$]!&IPX0Ri#9#Jd!=`?e\^qooQ;O1O.%/o;S_E+U,%1qkk^dQ3]]-:\S37MIj5Gu`]_HjJNQ`39:B`jKou(#<qFr8h.FO;PNJ2nn1%XP.O$rQ3Uc!&Cg19HI!DOpN^_FD3j+anJXYG?=f$^olK3S`n&llEk__6AJB6BfJXp;o]))Mm\1HW10IrcCgPIkg0E0Fm\8lo/\D@E9u_5PUrY^Aig[dL^7?.8+MX]6Esg3G;qeJ!a!GOktZs;=R"m2@bckd~>endstream
endobj
xref
0 47
0000000000 65535 f
0000000061 00000 n
0000000123 00000 n
0000000230 00000 n
0000000342 00000 n
0000000547 00000 n
0000000752 00000 n
0000000871 00000 n
0000001076 00000 n
0000001281 00000 n
0000001486 00000 n
0000001692 00000 n
0000001898 00000 n
0000002104 00000 n
0000002310 00000 n
0000002516 00000 n
0000002722 00000 n
0000002928 00000 n
0000003134 00000 n
0000003340 00000 n
0000003546 00000 n
0000003752 00000 n
0000003958 00000 n
0000004164 00000 n
0000004280 00000 n
0000004486 00000 n
0000004556 00000 n
0000004837 00000 n
0000005023 00000 n
0000006282 00000 n
0000007680 00000 n
0000010258 00000 n
0000012431 00000 n
0000014575 00000 n
0000016764 00000 n
0000018722 00000 n
0000021079 00000 n
0000023292 00000 n
0000024714 00000 n
0000027170 00000 n
0000029111 00000 n
0000031488 00000 n
0000033899 00000 n
0000035713 00000 n
0000037956 00000 n
0000040228 00000 n
0000042247 00000 n
trailer
<<
/ID
[<e9856c676444c494d18feb53bf5d2892><e9856c676444c494d18feb53bf5d2892>]
% ReportLab generated PDF document -- digest (opensource)
/Info 26 0 R
/Root 25 0 R
/Size 47
>>
startxref
44376
%%EOF

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,710 @@
%PDF-1.4
%“Œ‹ž ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 6 0 R /F4 10 0 R /F5 13 0 R /F6 15 0 R
/F7 22 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 40 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/ExtGState <<
/gRLs0 <<
/ca .3
>> /gRLs1 <<
/ca .2925
>> /gRLs10 <<
/ca .225
>> /gRLs11 <<
/ca .2175
>> /gRLs12 <<
/ca .21
>> /gRLs13 <<
/ca .2025
>>
/gRLs14 <<
/ca .195
>> /gRLs15 <<
/ca .1875
>> /gRLs16 <<
/ca .18
>> /gRLs17 <<
/ca .1725
>> /gRLs18 <<
/ca .165
>> /gRLs19 <<
/ca .1575
>>
/gRLs2 <<
/ca .285
>> /gRLs20 <<
/ca .15
>> /gRLs21 <<
/ca .1425
>> /gRLs22 <<
/ca .135
>> /gRLs23 <<
/ca .1275
>> /gRLs24 <<
/ca .12
>>
/gRLs25 <<
/ca .1125
>> /gRLs26 <<
/ca .105
>> /gRLs27 <<
/ca .0975
>> /gRLs28 <<
/ca .09
>> /gRLs29 <<
/ca .0825
>> /gRLs3 <<
/ca .2775
>>
/gRLs30 <<
/ca .075
>> /gRLs31 <<
/ca .0675
>> /gRLs32 <<
/ca .06
>> /gRLs33 <<
/ca .0525
>> /gRLs34 <<
/ca .045
>> /gRLs35 <<
/ca .0375
>>
/gRLs36 <<
/ca .03
>> /gRLs37 <<
/ca .0225
>> /gRLs38 <<
/ca .015
>> /gRLs39 <<
/ca .0075
>> /gRLs4 <<
/ca .27
>> /gRLs40 <<
/ca 1
>>
/gRLs5 <<
/ca .2625
>> /gRLs6 <<
/ca .255
>> /gRLs7 <<
/ca .2475
>> /gRLs8 <<
/ca .24
>> /gRLs9 <<
/ca .2325
>>
>> /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 41 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 42 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 43 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 44 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/BaseFont /Helvetica-BoldOblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
11 0 obj
<<
/Contents 45 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
12 0 obj
<<
/Contents 46 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/BaseFont /Symbol /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
14 0 obj
<<
/Contents 47 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
15 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F6 /Subtype /Type1 /Type /Font
>>
endobj
16 0 obj
<<
/Contents 48 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/Contents 49 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
18 0 obj
<<
/Contents 50 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
19 0 obj
<<
/Contents 51 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
20 0 obj
<<
/Contents 52 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
21 0 obj
<<
/Contents 53 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
22 0 obj
<<
/BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F7 /Subtype /Type1 /Type /Font
>>
endobj
23 0 obj
<<
/Contents 54 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
24 0 obj
<<
/Contents 55 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
25 0 obj
<<
/Contents 56 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
26 0 obj
<<
/Contents 57 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
27 0 obj
<<
/Contents 58 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
28 0 obj
<<
/Contents 59 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
29 0 obj
<<
/Contents 60 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
30 0 obj
<<
/Contents 61 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
31 0 obj
<<
/Contents 62 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
32 0 obj
<<
/Contents 63 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
33 0 obj
<<
/Contents 64 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
34 0 obj
<<
/Contents 65 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
35 0 obj
<<
/Contents 66 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
36 0 obj
<<
/Contents 67 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 39 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
37 0 obj
<<
/PageMode /UseNone /Pages 39 0 R /Type /Catalog
>>
endobj
38 0 obj
<<
/Author (Uso interno restrito) /CreationDate (D:20260322013413+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260322013413+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (Ecossistema de Sa\372de Mental Brasileira) /Title (Plataforma de Sa\372de Mental \204 Documento Estrat\351gico v2.0) /Trapped /False
>>
endobj
39 0 obj
<<
/Count 28 /Kids [ 4 0 R 5 0 R 7 0 R 8 0 R 9 0 R 11 0 R 12 0 R 14 0 R 16 0 R 17 0 R
18 0 R 19 0 R 20 0 R 21 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R
29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R ] /Type /Pages
>>
endobj
40 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1874
>>
stream
Gb!So95iQS&AIa;bb[QC1`PlLP=NTXK+q(NfM$Y^7A1SfJKBgs\,H,nI?7WiEh!1s%kGOEGJBR%R;[ku!Bf@<dLZ)%Rh``>)I?`hi!B!gi0$[104A$#h[3C6CJb/naH`oY+`->\g2!]\0f2LTQ(1u]=kL1Pq0Bf$aXbV-[PV"l@"Jc=$Ed_P9YfO_HUY]_iY$-B>(h/U!d"G7#cIf$N<"t>ES"\k77`C])^.:pO9`j4O!,Aa3JL4\bR%oDDoY[E`0^iCW=WPl6mP-YfoV=*3G(WJ2WU57);1*]2H43SNS.-j%5h`b75iY_%:+-I4G';&(MmYI/I(h+!9aHV%mVuPWWV1r,XL4iLXZl5$"SgQ4[:Vn'$(N/8.Hf25\::ieCc'b'Bk7.KF'B/66ofl+W2J46Oqe2#odp+L.p6]6T/JlL)R@k&iS7$%@+/kL289%T((,>-d[qaS]8WG?mKRP+WEIN_[P3P#odKtL.U#O6T.WTL)mR.'/o3=%@aS1LMYVa#3-Jd!bRZibV//2Qb*mm]4,'=ngDO/.4\NrU#LZ(Jjcl>A^Do'[UOjVHsHDuVPkarL+@829@_LVRt=*2%BC0Mkk.aq;juP[DW-&k'`H>U*2`Ql>hZqe7Lo"E*Q6_R'Y-:RF*CIBm\%k]6'Jp52KoD,g`>JOQSR7G1-GGCL1al<nEe=9Sp`c>ZS-!:p(\%_^80`@f//kC9nV-N1mi\cKA"j5R!h\]g"/'JI8,@If//kC9nV-N1a;gc7V#lTM!SmqOaTqKZEIt1p,.iN^8;Lqf/+]KRRUghe!-Rn@'Xf3jJo:R4T%-&e2ON<8!qYmG3F")?&p(bDZu2MQ/(cQdIe)cVKc[F=Zms:AM<PT?m9bhC:]Wt]'H!Ldb4/jL/+.qLO/&D2Q^oM7+\T`9?ji>q\&0UG]nOYp@7PB?$un*S,.G/h[7Y9^H&oXMi[>P-YIA#hG#VPL&*=Lcf)i>G?U34Vl_i<pYA[^'6aX='Bcc=KQK^#2j1o$o]67.#`e\Pm2V_2^RWf;MlZ0Q'0)V11S('N)2:I43-[6i'`u^Zi4Ma2Odo!I4m@jPnd/C9UTP_`Y#n=odr!$GE.o(cc5!54a3Y,"pAnm>?diVngF[UGFu=naZasMPq[M?,[PO;[]D\jn@KXmG<[b@^1pc(ccX-sTX&>3,3oAdW1c#b';.[)j0@gZ?],@tnd/F^@p[*-I:TNHlSsJ[kU6q]82c!9rbp1'J9;[EJ+B;%]0;$to@L`4fVe&NI7'e7:(AFtm1pj+Uk0G>ZOd>_b+H-Fu0lF,.Q-f:V"u3]GP._2p^C3.pkq:.S+5J9rY=qSGr]++BMh=R5Phqu9^;-Ae@9rfZY4"h<1RAO68/YtA*Y'lI4juS!i<@Ba?>"/_1p6"EQ*(_Q8Xj+,RECkf_sfecBIOHfY]%&>8>_.ok(H6AnVs]IHH8<E\lNU([`Z'r;tP@AIP?7*mFuD^I/VH1G9MX$Pt@E(LlB3*ih<^R4.MhJqUDkB[)&!N];P,Nfk12.ES%@_`GF4sRg=t*SC5dqRTo$K@]lBbhWM'(Mou)Z&,3D+ZX!rgEPlm:G9T&Q)qFs_1X_\81r%r7p@E-Jo7=cto_'UE,C<]PndjGImsq$=0*NdXAMb5s<,).$87LfOo_XoT$]bXYn^T/,(EV@u(6njIJjH$L_G_IYDb6YPSKgf2^I[C<n:08R'28U+9!_&c]V5^*oa7@"8l;F<E@=0uCq?a+RtGaooX[^g-PZhk*qYbi<X\K2P>;m^OCOXVAOrn#<euZD(`f%%'Mm.G`ilu?ma?Sieu/GUB]m!snB7]PpRE=31i\BCYX1%Lf8ir:p#B2a;VWs(i87+]g[Y.jDV48tCn*?770F>)!qoW)j8~>endstream
endobj
41 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1546
>>
stream
GauaA?Z4p`&A[3!'_Bs_WF#UaP=Gtel3[k'Blss!jFZ>gKt'iD:YJUZG(9S)Ks#NW6Nmh5p5J/n[V$p5JB]1853"N99aKWo#nRI&&dUuI_BB?)c>*YniVSiA,*W?d!(7u;FY_EA"rR0-7mI1Y;(4U="C]G8dmCnACBkFnZ[`,:J\;%/H%7Pi-i8@aI7_nZfbYdgp=ZDnXbM;,TXd^=EsVn9$2)(](eO(`L24;f%(fsBG!$dfTg+'1GKq$_(Sef6fH#Eln/9*H]5d3$*YNXDBgeO7+&bQ^hi?UGnReo/;T#.Uo;J085upRY@8<eTT$EYD6=k-LV8!C"58E;e1\oLf4K5u\1X7gRKX\it9Z?_jZ-\SCV91T2N+b%>8pquS4@FQN57<S9$dAX,?JW-ZDCkOsn58(!,(smjO%;`C2='WlX%=AWk:bk_qgJ[+/Jfii&gWRYU!GQ'IbiqP@im.QiL3pj^>2\^;me<X<VpP)31;WsVYB*aQ/7j!acT6us-`WUNck9B6ieWc=TU#0KLcWNG<73g?p7)GJMErS4$,P75+",QQQ(1cZ%>M_'g\F&Dr;Q8^3,9>/AnoNg$p>tZHHVk::oJT&TW*Q4_ks-OiO7.`W/M\EV8Y6:[F0iK3.rs5rRW"HIb;V2!'(;5rBdc1r\OWHIT,%I-_u'Dhd;gT9A"k]i03@_Eu-cf?`_m6(Z6[j<4(*!PBeJ@MAs:q(Z-i(#)Bn5/)FjqR_#;?h-V'N4862OUHQtM=:#rU-5*T6([+!=fF8,)!Nf$M[WfmnTB_Q43'p<HT5/l3:1ba-WTI<?8i4gWmL"">3j$S:Z7fkB'o-LNUe'g4H@WDX.W:Y_?O;Nc>sY`.CVu6f1^logLD]OA3,\/9bnJJ:c7$T'1S>11,3,[[ofIWXE@2`,ek>+$'0R:co.;PqiB_38@J4L(:<7??A#uCc!ND$WQIpVm:*KB`c8*B45W#Rh]>JEk[?/<MUJ*`UJ4o=f9>DeV)EFr^4e%pZI12U^bo::aS+!W%AG^?\&T8R<#9_GmP11Ei_C5bPCT//6]g`OOue<05/,o"=kR9$*&L30P"'#ieG0mC.#E#9W[Q2VCulE]F,j5,QST<1jC6XIQ"l6K6*9LjN>#T/d$ZAI+7=15;ng*`h`=To>[EHO30TsLak_%\H=L1@mW\QgfnTSK-g`2jUriJf_>!dQE@A7!nlbmo4YEVsU_*"1kG7E,Kq/p"*HQ)'%QDHelb[H^@Ig_%3`Sh<c^CI"$&_CQf.X*Q,H$&@FUhO5p&I$SRCh9cgOm<UWds?0[tQrOkJt^jrkBF#R>.1ZMqssJPWM)sQq&g%+iaOHm1IY$KAPXY@06P:O,F'u[[1fl-B!c%%s'89]8[.teYDu@:t_=5hid90Y?U2]SF8>F]ps^3=^g[Y3n(,l>$".bdF/6\<t%P&DDD1N(96gA.eR#i3gqV]9p6dP2FJB.A[71'LP]6.JTIJjHdCtJPNBI7[c-X@f=@[8gri\0\$-H#ll7`un*qE4ZE..-[tLG:eChuFi1hBSEAh(FK?d74E`s:~>endstream
endobj
42 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2890
>>
stream
Gau0DBi!e\')g+Z#`UZ=FV!OmICm8!I7Yr8RKdBt$dpME,t0""eNSlDq=XV/drla2d4f7tL2tt8EP@AJ@7_O>8c(G"M>hWd^OD=kICd!9)2aNYcKO<=dJ"mE7;;%B,c!)g3$[B\ZT+th0?H>=`j)<fMhI]j0h"ubILp"CVVARM6De(Nq'<h901[?sjuotTNr*;b8[(D:*tg$k),*`V_K7OEGNIaUQ3gpp0r<;3M3h[[8[J#pe#Uqn?SB-`dYEm*H$UJ64taQsr9`eAlj[7Ve(*2=%SMjO`:=S?4=oC]L$s^jreEQ'93lZtI;HMbFk8o)(FH?Mg@l8,?WX^KY,g?Vij#f,Xor_IkI3VM6T=CT&-b#f_K4^bc]?l3mqkdrl?#tT#U1fS?+ls$E"]j,pk)8YC#juob20LMg5a7f.Xcaa6Ud4pr+$H8Jb@`C(K`%Dr:pP)Nr'Gr`'KPa&kEVV/-2@4e?_s&HDP8:m8(6Xdg6:f\_SJcRDd1IaJ(YR@<dO""juP>9OT0:i5>BdC_\[YK7\_!h2$\R5'tQf;_-4)JMU)>q<oNsDuAH6+jc&f70n!uL4P$6Z%jj@pj<4r*_,0_2BSOp>n3jCGJ($shKjm([^kYWUj#5+XW@_*`0O>aF34`$fdd&h^&#VTR/X0+<RACUo;97R>MsWrGEkfSElTp)I?@3K#M:)6@(<XI'P<r\@Lc!YO^8O?k\H97_ID/egtO&<k-I%hhsr4PKQTq4^l?:o"%hep-SKP;7/5=5'#AN*6I&;Ups9Eq6c8)lK$8A,2gU0^^fT?VMXn>`(:mG?WAIVIS%;U!e'c3kfC`6WA&Y,^<6ibseL;kSY/a(ZeiXQA;&Y.94hF\F!"O[0Jbl'*D-/uCV55h:kB5Mt(s<!*_I0XN2N($JA)DWm'>^Cm9a_XWQdNH2/W;0CX(0TL5)6EtZ&c=ERcS\B_)F5a0C_L2l1W"AS27[$CS8>CP\IC1WH*V=)V6:)B%:q8<TT3g+SHe=SW$$@ZAFs$$/,Ea-_g-=%M<a'7SIAE_q99V8uo'TNLXnJfMm.P/F,cf6I8\?k%`2fksjV<B,!;iQL5n_E]?n=HOf,LI^^%NOef6U1TRl]2nDg;$T'H0?1hc%L7uLW=l(mhp6A99*$KPCkO1E]M!hpJnj8;JS55qdURLYbl#bN9OLhPUb#&#,lFKCG>+/q9U/)QD\m!Y'b#j].=d2u_"4JK[S4_j['DWD>Rhf2og#Y;?dHsgJG;\1pD$pQc:]JQ#,X$Rb+I!"%E+^\YcbW,_54SLd"Ig^r,ug*-L^7h!p-BNCdXN=aCk+?ce1l4B\7UIo]mcF7,*,(gT&B?s</4[ue0-Cmi*:PH'&4$ZG'_)Me%#gocP=U6LH`nj=o[`q!MXG/8t:qcVhXdN.(/_bpRiF/3i<U$U%`5C]iE>p/NWse-ohISCWA2E%oYbQNG;1]9iG!@=1P%82-^!#OKf9N=+lTF+3qld-4JoDR/ME-(T&CHon)LHhP%fT-1VdT1(-Ii@pta\5fV%(S3r*)"5dG)PhXM0j(5_kICF7NJhtY\n<%3<5)O>EY1N!ALn%RV6'I2b_mB'tF@Z@V9@_G93.[-,*k>B`)>]%E$jZR4)%=8:H3O4)0U>TN4,f_TkZ$OI/Icc0i2@RN%9+p7;\=Tl]G/G0h$d6T=,W<KT@Z5+MH6\Cmj>nFIn]NW3]B&dMMjP@;D#F#Fa\fo;BE1/RS<kFK,VbX\0fIC0)uEiq&P*S$"ShJI2EqcH@JU<f<`YLWGY\9Im(!iDUI[kDr@fe`7JpfR@4J,6r;3>mhZmVAZ]pn-fLp&h]>]t4'-S7n=h=SB6Fdr_++c92UH$$9EU>h8^Q)6i/j:u(G_rY93'5rXl^o7YVSrMKkm=j0[GaoM'(IlcR^C1aok19VWrI=n6]oA$H)Er[F/+@Z;>_/@N2L`r2g^)C)4C-LE<8O<WaMT6eAn;,roRBUCB6?Xs@l!eBQ.CF`[Ln+7BX*qZ5>hqp+$ab>q^24_GlHKHCE,m2'IbELoP!"mOs_IZ<,#cc7if[3H<(P_Q=P,XU5NEEeekJYV0$k]3eBpm@;&(k_.QC9.a-)E$aKenTpKUQRq"IN<=nTZPs<P1*7b:dmLE__"@FZ>+Q7j5fZDj_C5EY6Q@YNs7K=9Bi*cg,J<O)mocqJB(I[*?hr`DtP,8S3Me;,PXd_%]&B$/O;)9MGh%[;0Gu&2[(&4V,)NYjU@L%*tj^G]qJ.*Wq@UI&R!4[Vm+*!$j#RJ04)dkp`n79PG?/NXr/Xor3:0tVO;jhUhlGuoR<7/f&I\;jY0B3$q)rACWu$4]Fu*"Q?K5HUNu&_k,sMq6oT^:"[)[0rW*bFeI1g-S\k%"3HHHtT"8JP<n_MhCp@&])5"+Cl>VgOA+"hp6n\,oe,f_92(%3%04,cfpH4#D&'ND]>fP5Pg;U'E_%R9fVHd;*i5oJdb@\Y:4S4;PA$Ffb3aDj1`Ph>sj?`!Y?5Jfd\(\>`$IR7N1Bu`_`T*=CHY;oh1p4u2pS`7f,?CsHZ,Xg^@B@^6N',n[6FT_miUoBQ]iJMHf5unD[5G,l6]H*8[f&+[V9,4<d&O'=[BQ3p69#C7g'`&,BH71_AYg>-#]Is"Ae*0MM1AYLMR,rZed[&SX]\VarT;rq[>ikG,&gCDcl(bKd&#%b`cV6UKlSt5qessM=tekO"p(].\#n@uJ99+&iofP$\6j<*.*qUh/chTZP^<Lo2a'*/:BTae+o9V'SN46$<aY(8='dOHa!mZ^o[Oe/s-icU`W8i.0h*[Ms'u)!2n&<Gc#0E`ETVjegMJd'`=Rd,me4P/B`8LVe.gm:VAD'h>/oR&c$-dIn'TnHBoRJN]i,(6J5@al:3YfGG;Le+`g')eh9V_RIRisNoR?kCS]L~>endstream
endobj
43 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2762
>>
stream
Gb!Sl9lo(1&\[L3oPKjF)8Q`q?'U%ObH=RmCTX`3?@3V&6Y[S@6Xp+A/&+9O#65'l;U"e`LI<b!1cWsVY?uK@:l%?Qs1>:frdI4aI3hET\lk5QK:$Q5d+-CoQa7f.nm'0K#UtqDTKlDe3GWRe/&EE);aF!;">WVJ8jj65.)cKelQ=FSObZdW.>SB25X?/%iIA?iVL'clA2k\$%g=uN),8?-_L,u=Sr1p^U'RQV.HM`^Z7s>8Ra/+o>i&`"HM@F+-,$sc&!ro_8E.PAX.O;_hu&1G51@c;UE#Sk"prK!T^m*uTaYHK&+X)]<F>'hZZP<?<ct%/6u:)idO-J"H=<;N.<VsPW]F2S\1:FF_"jfS+*Hm57<)j0#2p\2L,'W1rTYr%VUKBl&O]`13]g:8GS7oBpnK$(o-B8jb22RUmQ)QdVpZsa'Io99i?tp%7c%qL?0n,Pq==uE3ShkhO@EAJ=X8Se'H-D!DaXQl?EXVtZK2SJ@TNRB/GF@?6=_P9)AGc3V_GbKd%Y&k=lcfLT&J;ZkK,2O3%ub*1o.H;KI#5lQC*#M+U^CCjhh$@!rp'ncY!N*-BeR`cLNq-YWE<aM;H!2K54gAj(gOp^FT.7VVXND(D"C]$3/iZdP!\q'1tE74A$4oVinkF$.,tZ!%rWkXu./(PGYgt1\V7Oc90+-Hp,t2_ITUb9ZeL4V&`\8lJob2i^$\"U.@9kYs<Im-BkK-*X<b'iQf%?jJQ7?Hhl+5UECJ[*HS$O,B'>&8>[pR"/cFn0iBW8/El4HS>U4>5AP4nle@E`WDmDHSSi(kT[L3Y;cIMsCHd5Pkun+=/).Mj*W;KMY)RMB2Te!Ck\r\KV4N8UZF1muo7EqEMOC\J7B5VeJ(1i'>I?5W.]oIGAK%ln^:9h>iJF\GQ(H9eg21'-<NSL1^M0HIdk8"u>>f@>=7AqL2$A5G2"qcb?CODA.rAJUNU<EJgVR+@6*1M9JGDK'P$EH+9%(aX!b#!F@IENsKL\b9CZ@e]in_YI?9T#+WKjW.CO<p.ZU(B:o<3G&O@&$5*QT']GIf6JckgMNJ1bmdK-X6h(c4.ZX]_1]T/d%Ep!tf_d2);gk\:K0o^CPWQ@OdGoS1h!lQL`#m&r\R/):F<FX"nb3!\?F\ng&@O/enqd9=#]:FeVq`bd:q-cs&V,W$.4=<YefO.B7/"4c0Mj/?)C@+DrmqL''"I@rJb$'&cRbT8Jf<N-(HYFVf`;>?6a6)_F*G!MPM+<o1*J4-i#_77"[a3V^;F7\.P(srcE;ZJ,%)5&$%j<-m@_GU$MULj_*`t?C;WgCq!.i?g2qD\j2XTk'<S!FM$D:o"F?[>K<'`:kdUUh-a!SK@.k`1RT<+Fa*OFfM!H6JGB`'_m%eYtFdb7!EF!8F7M%kZUi>J's5k0]fBPkqKea^F0Damn03n+-o?C7_B__HImS=j-HE\f/JJ-JHif9Q-0-TYVaVp8_>\oSaIGo#D?`1la#@:G>sJag&p13I^H_>g,M'#Nge[2L"&.,t41AjCts<%"d^t%ZJ[R2aRp!PCDDBJ\hs&TGPYd1iUs5VFE,T0^K=0R')!pMOEg7[0&Kt!e:QhX>/%mhN6bAq*q$Pn*-cBpfL&*_NAKF1=(HPM47CW5\to7#*J/h>jg7^WU5#9.dg^q?Xk4gZ-,n,>(P2E=4#?.13QYWOq-k!-asR8._B-/VU%VtG.mGuf%/E%&("BkLZ#Nik"n1T"ek=Gh`"e<pLZhi%/]pZ=P+/gBbBLd^pEA/<WKHlo2)dk?-lW][HU?es4+nBZi7r;_+HpNq#B>KCFcO@`\\VmE`pQm7+HT=#G>(>6N'?2VYr3h7Tc]f6%>,/?[/2)?#S[K)Nu3taq3F`;Ra8#ZpPU6O'!O?#gk99lnit?ndMt)c%+Y&dk)j=gAu)-FiT]uOC_*RpDdoLDX?t)7YSq'"eBBe\td$f%snt=3+>lQkEqAhH=#cdIj0C"]K[U<1L'12Bl!VTk%ha5K<YAbfA0(U,Nj4,p$G>g=Hpf9QM\%T7F?>-lMnq/(B%UCATU(P)Vgg[iG/t%Dmt9G$B_juhHgtOl!1q>0bQi'7X7gep\9jjaos9?76n%[[IJn=JeVSZkMpJ^JkWJ\7=TJZPR(8/8\&1CpJ%VWj*5G.VUBSNkN?7a>KkO1h5S\*DR[Mb+MOG:aNhK\m#2^CVaW=7NK'q*7[^SsRsq\\3BW!Q9ud]2["a>nrN'%sRT/+OMHM5-Isr5DRES`l,q&LMQj&>9cU"1mY5fpq!3po$8B?<6HcMAr@g$<#j4'2uloQslE;D7"WM<+.(ioaR7%QRg:Ai$`Z4SIES6^>G(%smDNGKNe\g&8Ir)PZ39RS_XaQ)$pbuYV&gP8(%Vk8_lIcWOk1b`NRj*$3$AD9k?.%L"L5mp#'YU>b6jU>AW[[P+^+*T58+e\QHPV?Pd!X;kRX#KrG_><-9[jm]c,L7qaMDD,+J;Z\UH7HEm!W<.]**p:Me`mHa/=H&s4NPS*@;PhGR+C\pC!#rX\5asCRR^Oo.dNDRV1WW)N>,Bij0`m'C,=u#nJ^(%XEGG,onI\$BeDE7?2'1Rj:bY\@HspRA[D<\H1$nBLA@#Z0h*,?G@GC@e\O:MdqcJ_n@Qg&GEO"Ld/,X;NA1/R;-o27W0Z`T/_5r?EQ-jh@a!-27m$==2lbmAa"KROGCGtAGETjFI>8Q/La5U5pG]+AN`+>-MY1kaOC1gi2Q1VoFR#/.H+btK>])3N$eeD8f\eK(f%Xr6fS/TFZul"[3POp<~>endstream
endobj
44 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 804
>>
stream
GatUobAu&c'Sc@-MR*_^NmPbNXC\%RS+Hb!<nRaKG1C<.<3ek5T-HEhRmMW+HcDiNSBH-%s,-osQOVhe%IgNR\!IL?LU%,MOUTA9bfe,gKMYec49NG[PIUn7+Z,Auo9?,JaE%P`9>Zt<5r(d;a>(OuI5W7MTL*]BR8*u0.b"#Kl"luYVak1.8.Ue+jm3OY+tO4Spm:A[d@,$oF!qkOMA@&=L?\)7<mE=95$cFk@Lf#C9-H89Y3+?tD"._PHRR3`F].n&aAcMTbL?,5-_+dYr<m!*CYO"Rk-5;k>7/Ij=*[ngY2I!3CU^TdjikWgbta<DEf%[U:\*%j\.kA#^^2%)F3<Thq@Ys$[^2\#\3qT^SU]&V[#"@"rBc^\O>Qhboa\fnFPPZ$aiB;Z>KA#AO"%oa0_I-U396!C5,'e+H&TmC(R6^!R9""bjBJc9dtf;n'H'n>-?^g(i.%./?Opt!'VEJM2t]=5^:gk<L"Mk#r`a?$5m>aNjfoD4(G"Mi=BJ^.I(6j47teWhAGRSs@k'tnOUOfqJZPV/Y8R.,aKa"ja9Ot%@/dO<_4g%:Z5BZ*gmZBZ*A(ONk.rp-oiC5$@(E$3YWQo?H]2X@;%P.j_t4-kH`D0N+Pd]`XHd.>gg08ORk@IUK(BftUYGW%9=/-nd_\.uld>S&a[IaZgCR&iFp9"7]:Y+\DN*L%.MkHbgG\3NJq"!?&%XUO(e5=e_'$b;T[<JCLV&$&_`<HgSV>>l>GLh8YKS>1\aNdgXO&&sS-I^j<Z!?'JoC,c`\\U`5-,30*BNEC[67pg".&C^4T~>endstream
endobj
45 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2957
>>
stream
Gb!SmgN)%>%Xn(Qi6aW%1M$*kDbabSZ^'jpRG%mIV:oHQAsQsX]I;IJhh,g0U23E+Z^R5#aq<GT!Anb7)Lsqjn]s#kB,/hcCO10cOXK@=6!iIPFT$qHY>;m'3,Y*/+s=o>?5TO"#\oYQ2;X6if1D^XUG*,t&VgsPT&U/B_@qeB^R[>jiqfVs-V4&bS2a'q"0[(3HP-:rGi%U*EY<h60JR/IPK?(kHGa(;e.J:...LOiFqZu?dHQEF(Y9I)msfh65qanUe&@=8$XC?XI!:#/k&5A?Q*,TUjKS9t$!D]cQ2B[T_@^ZnjhK;J?`CWcFP1hslu)aJ>8EX()^/[:U$G.0X#_bAq,#cDXnYbLc#<(3&(S$GIc+uSNbcTA=+p;4F/u:=44Vh!:4so^Ve6A2#LVA;hbTdk$/ON:d@22K?AI_CYOIAoJr26jpj'+tI'[Up]?"f/[\&T\k4i76SB-a)MLEeW]efqbES!'l2gRX=+BqOLp8/8f@C^[(8\I)M>b*^1OpHf?E1.l/>P8]^8E]cPqg>c.Lk7q/?C/t8i0*#hZc'cfFBRP$)f4p!HJi0*rH\N8a!(rNkVrlr%sCGqY3>U]-eR/IdJ/UDa(Iu9L<uD)GAG%N4CR[&?hubH*DpS(@kUDiD(;o^&caNP$Gbb/]r/?>a^hPG;60PZ1FCRFK8`^qK+6]s^iMl4P-GQn4N/@lqehqAr*kbT\m?;[@fd/aB#fl-oom0G3-M2OmZ8N15&TEh,EEgo!-l<Ip)e,(&"RYJASR(@1Y[))"9MK4rluR`"qTKIZ7S[K1n&H06SH%>&kH6K7T(aT&7[7If>s,:insm!<(.\q4Y&&ga\7u"JZpr1jQ&Ol(&7i8RKfH_.Z6?SPj>?J=n>6Ecr]oP);fsk<6JH2>".c(Hi28*TWf11UfAlD$R2<=h_n;[MdKT3[^9GU/WHY`L]#$9@^1YZ:rDp-d6a.CZD(MhZ<m0G?K;`Gb=^P"lQVNX")b+\/(G>9'25&QQhm"boU8*.40mgocu9ta6/+gF6'0<C\sFUplVeA+F=p%;o2s7*$S70Rq]dg<s,oo,m$LP\#<WX/pKlL<#=`s$]I5`b%Z[d57o,s5cMN/1-]-UQ*`Le`P\2cJK)s'mk]jij<Wh52?0Pr;0hc3c%aA@Cr^LLJ/\sUZd'*TRpZmA.@JVf)#dt%FfH"tK]Tom0rleZg;:iU9A+lE)["8?1CS7bbr3FE:@D<"BUd$NdA]Nt`;_C/"b;0]Y7<5fq`*UQe@*U]hUD]t0OYs^LZDib.GpjfP4+`5oD!:'%G>Z>ELVn-HV6@FC>Ca2^dQ,H/\7G\=Xtr6H%`cJ8+tRoXitI#p"%8H*s+Z&.)kBHLQ4]HQ9eg(r0&!b\23*:k0hM9fGitf)PrRLgK7Q">VRM!,($DT&.B_4GH#uN8^?s\CE#@R^U0r0Z?>/\)rSWmS1<1pfqETd?WP4RD3$W(B\M:!Al.>JkcLRrV"NS1@f)UCGX&:SYGq]q&d<MM85!e?=3Xmn0V.Mh*m%VDQmlqBk]7Y+RUYZ!b/IYea":JhC+XuOW=2'q:H%,fFkYR;tS6&lI919YO^?P*>j0gR:9cXYc;&#d_ld8^aT4CrdL"q/kRC"pXoPD._2'_c/%1]fUn$n/t'V<t=oq==uhZ>e:fGhfb*-%7^=(=2-A_O@,CjIA0cA#>(NN4JDlln(%2&';=%2\UGMjLNXWB&uGF*JI%]-#ldBU`Z$h<d-u[iX8R5,"HYk!K)QIs@R8`1IuC+puREIu^RV)o!M7GLlQ<"+EB$gn$27#lUCglt6-ne`kj&O/oBqOH44E5@F1nCWd1O@tkV8Ot#jpn>YM4QFoN(cE>\0+j[[4%mB6b.t;AE*RF@M888r)6-Btp\O3\kcK_j'ga.,aTpnZK;63fu;bZC<s!^%;e.(-B>-I$Pa<^2[X'h0?W\R+1dti(l*e<&Goc9+CGeg_'`\VOFd&BC!os\8Yh0'%g7%Wdi2)hV+_#gaT)),ahYu0U6D+j8Ki9X`uB?'SM\u.Z#C$LJMVg6n*h>aLfc_Ts-c%@F[Mn6qp@OV_W+h?"DJ")3sorXjThAWKMaRWcOqsi_p1D,*pZe3h-qQn]I/'c>T^Z)nBCfCGfEH(V4#<n%Ln<`V->5,qPpCP)(De(2XF>!TUFb"H*NQa^B'tt]V?').uOY)Jl%K'+G`EE=UCK9YkoL_&J?Ou6;B7?U:GjS_=W)iR3(DCCS6-(Z],.Ei;Zb(SK$QiR[CLpSS$_H`U5+'Ne:GuXD\I_pS+^s??6LtCjW=96<5tL4G\\FS]2'<tq\e7k&GFgbS=TB8L$(P[+ZR6:%<b;f&_#oF60%aO:^6<(*8fntb"OFKEV?U/O+%=r-"9%\!W0dEKPkPYW4\5&U^lA<mc:2P2$qqVOPbo(Xn8_*9A#+*,fG\s"VeSB>0f_Y.d/n(WG5),-DP%Q@lR-+KFAC.AA5BL+hPsH`j,u(2QPi/=28`L_A#>a6WAbL:`"$I&4W4NHog9g@s)\8)fZp1^]p7t=l9uKGh.kX(J<m@)7G$]+pY([Y%=EYAfEc9LQ"h3>)*.>dRQc[Z21_GsB)=\PIY=7XHq`&b+S7A+S*\_P'igd&H!bHET;.Pc_Z5XH%rgdq$k[C,!o(R*G:NL9Gu;&Z]3+UY3]"BKpWA@i`/.J!+T>c!ZA)Q3F#1^Xk[F\?$8QRAKFEIHk-LWT<]+6Ar/=[V6$HJ)LG;%8c#$>UE0bBOL-+mnCP$#tc1l[dZQ=`/&KVl3^.$*(0L)jA!qnhL>YD8UK'g?1Fn6?.JSt+uRFY\<#U_l[)VQ<^puf]hoSd*^]KD8$kkWER#e'/`\a;6MW[CEJ(WBpr!KO23!0*MbBc@Zd2+6d8p3\W<k(\L6Rb!@kcY.@_rPWA0*uH$=^K1.HVu[GZ!u=oDjK1[)8:J3%.X6.6$_:4C3(Za`k&e^='l*:QV<W^%8Ef,aXJjP/~>endstream
endobj
46 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1340
>>
stream
Gatm:gMZ%0&:Ml+%.`IbUg.4o>mg5?h)L;rP3aLO4rFVCM0>+eUe46nd')n7<9/;5BarQa*%2G4k005L1M/oBLCJaV#2n)Z]XqRe3%W[s&sC'$&f/ZS5>DKLH-R'GN&-k;5WH9uM*\bTTHm<3#-s>DB/0Y4#j!8L',oG?:C,.C-od<a#WG#SUnI`scCXFOE+O9#KF:<lCds4`5aY=InL5Du.13p0:EaA8A2M>Z%Eq"CcBn-\:LC<fE5$jlq+:pQ2ckq'jo"ZY/0A9qG4ZH\VaLah)2<Op5NRRI,l0a7A`%1M=]kIEWGX"-gXN'R($?II[q8oIg6B#!rYY"p<(&2jq>F!9e'>-b+tR\"UO,bHN-F0lL"^Y&)2faaP.ItQ!kqu/\t`UH,g@W0%]\B-GVVeOr`Ao?Smhtn#PkVuE%tldA=!`s^<#IF$M2r2;&S\_,mf^8aAC5L[u=lI(5X<<88/Q%`7-"G2X-ih0^gVRqAHZd"1$UV.XRp5M<o]APm7.lQr5fL\Q;/;r>qV]KTmZ@0-Ke:+rGtg&sLZn:W*d*KFB>H#/5sC81"G?JJh3]=T/W5V08lhIA]37:7&@i%HqGT%U)l`Lf38;jV3r&X#J07p]Pi;S]!gH0-[_r'ZK"=<F)j#AF`aRgX\F4@)7&elTLajG4/2b!EI6,5LmKs%3TGW]$jju#b+'jR.!J6"c`4]mtP=)n/FCn6aY"2QG)/I6-nTlN\b\AZB%_&[1]I;qQ12DDu*MHa*MR<pKN;$9jOb$!:o3<+s9R:,`fpUf&j\b^^np>n]629J_%QcAUq<$o4UsO+-Wo]PiUPMYlibB4duBZm8b*#3or92gFF'b8M7>+$L\cjh>'&o_l-$._>IW/$MIi$BVNgB4l&:df;Qeg1VSch2RYrk<4eHR+u-%8a._/r&d$A?,7>ZS6<I38W=WbZd3?aU-KkQ8$Iqe;dg)g;8o[N^i%!^ia"85)RKp8_[C34UqPpMfLOH@rWQccedQ1V*\?1#RGA'$::fX^,MgGf'A_[F1Pd\C=Orp9C?_!`:TbPOp3k"VfXa0F0%X5a$+pkN=;obEIH%dViS=[RYLX5.Vg+>Lj!hBXOet]RRh:)dS['!8;iB5DtC4CWU<0)%I`7W'NTG&d,Z#pZq'RZF3PJXn+mq.7ODbNaJO1H`1'!=2>DYh%5gspW'g2<Eaf3rg[A62kDO%o=7ik&qKohKeVnp[goO*99uF/d[:rmC_!HY'B+#n[>8V&TID$O>0NRnDN%a'j0)rIeOEDV);Q<ZY;J#B.lCl:ond5&Mbj_AO<dgU&L]23*^qqBQ!dL(19rr!J*<*o$8Gr-LH>akr<1g%5~>endstream
endobj
47 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2607
>>
stream
Gb!ktgN)>a&UjCToV9']3<XTQS*-5VH$2<Y2Z'3u(JI+MAgp'abBT5ScN/8&AVm^_9IcIQgrppm#5[uVmK5.`nXJ3]dl#P=)81@BLiJk8M><0-ZR^>\48X?d\3V*UT,bjR&16IW.TU1Bk!b-m`*p_88-4Q:5tD#t>jd:_+_gc$RDWLc9/nq\@NNUIr5:`RSTXNjo..\&chnt72KRS(7V7Eq^HoMDXY39*E5k8&RHm7YN.Gc5>[Ld^0&M/iq05JLhpLiQh"*KH5/-o*G'S4*ZS+04WO66;LJD7D7p'fe^<7PbqMkH_3j%!hY;EpCX$Td,046;qC792bd2SW0eO&Dt'&!7nj0!W8!9\Phq3rqX\`\=.\DoL0\]]Wfl.8gsat$WU)B`K:WmZM'a+NSniV*s4N!%Zr.=1sNIQESf\&-2G&5*/2Tc]1TmTH`qGHl:2i6Q$IZ^VbN-hO.#,*+_b9-WjC?X<PO].S.VZ['l4en9MgS_H/"_P.T]Fcml+6YnjcBO9&rb[Tq$0(>Y+oAh2ao%O=;+W]M!&iZC$Mm5/')M8\^h!r-Fi+MudEX&0]*WuI(MLCOohk>rU](p:Lf,hQKYKce21S`\_o_+9Vo>9=DYht2!554Ad/@F#<Zq0OC'f8[*OOeX[iYo!QMm'OgA5sFt(7MB6]r6.BgT:0lA`a1k/k2?m;'0m6mEqY;$B(`gij'gdM"hC`22H@7HElp@!kZ-a#OI5N41*>94`\3$ftP;UgCKtC"C2G"VP5d10:3K-&`WZnf4gG*8h>+i(1bPlV0]i&AL2#b$_X1cg,+&=p/<;4Tb0h6qH?Ph8edQ5*lOKMX_#$Y*H8/eiEhb3(6[sg7c@a/dcD0C@T`t8+_hD]D7pO)S0V%3.^]fq4)$:(<;cOlkFNk3L-7kYdP>og&7h(U5gVq,J1uF>ff$gdO>rVIJY-3[C/`OcmV[u/%DhCU1N[QTa]Z`5oCcb7!_'EfA$!ZZb"B^)BTU3b2kSt+3r;<]'NiX`[g4e%*4*hFZ)1Z._mcYI,`%,pI6_nD7e%f-YC^#U@WXWfg\Q"A]Aq`D`1'G,aa>8#<X<Fa)Q>=rXNsW8Ri3d><QE!+/QFFo<2uZgp5,2[Xo<Qf=UG2(Y3W/e(E9WAfp$$8MG,-'HB6:+\JKRM3n$WchTA<`$Cd,Um%@cB<aMf3<&tgd.7n*aOi9902UecskLm/Y;7$:LSDi@p1Y:CX".29P[*Y3T7BZ43J"A="_Gm\dQC?dT[eRsb,E6<@eS%RS-jj7G1;i<>cm?CuYQ,-n+&i#JU:?:mf"K.G80rj#.bgb\,S9u\R#9MA=8>1#^fa#?dPVAARan5go2(fqI>eYQh];/@IG[J!>*C&\LLHf4YF]poIRs'MBAR;,$ki;BrS7s>L+Vs"]'*;E@W_hZ*A/0nXniNP&N2DB"aE8`+L[kY)5l!'8u05N9W#AG9Y#[3(N^mGjS1ML"(Lb5L[u-:lu8^tRmiI!kQUF>EhBH,F249k?7.G#j5Q.>I[M7up@Kb<gmtZZ%n(f-3K2]5Ikk"H&;0&>o$\9QAg?oeW1D^Shmgbss#&8Dd4Pc[qB"<.5AD:k]b_G?s%k<57FZ_U)K\>DR@bRF$3g;qbHgWa]%9c:X$YZl^&kj8E(ZI>oe<5s8f(rj\p8@>41/V&e64p*(QG`3&<LFPU.X36dnh4!bOe)<V;l)Ba(aNs_\mX4!cEQ^K("5Zfl!uYNrg*30MI8lqY@U'X%emVd192j`6'6SD)eq,bU<"'P&%^:!#b\8IV;6%6HRZL/%ni+e"/2C6=mOk73AmCAb$[E_$c;8L[d.B#UI"tX%$fgcg]/.@?_8>p/g>=WXsSBd5NB8;;ONNrB&:Q!lkT12KW7^nObl"ZeWEQ^RIsSQ*RF:fLoDuU0*5&hTM7#<Hj28!!aHqY're=5)(@V^.6Id"nOCY;G6Y9m66P^qSGcE`\_VcX<:1)E1Eu2bcqLt5*:S+;u5lI<9<4RK8feS4$T@#hGb:C"Zr+[D3I:Ijn5Lk'm/#B,IFM6*.D5-GhoJ&H;YtTbYtE?9]KjEii.8DF\4%8.NL0[hg+aJqUo1OTKBSOnar>Ng5[%cV=#p]m]l_O)XYT3)#[4$1V^2/IWdV>22;upa&S\D+oK)TFaFi0WIV,9P2;XCPP,o/5E3_R%bWX_qp>E46T85]Y`)Y=rLm_pTZJ14[SE2Oglq"49Q%&*#)I]5(IX]IgVi6<VWX6,TO\j`ofh"G5Nb>bIb6O-*2bTLejCXH,u..FHu`G&05XSC.s%s1n\^.JI>D#["u.V,rt`W/%1TRfh&\]e?(nKPe+O=[/b*e`dJ6?4U$a'W$o$,fZEV,-3W@;ONATtE\W,5^NkcRlb3k^.07>PYhH3/^ZU\:Oio]DOi62G6ji>:WAC_-&mX8Ve<]<b%4;^uMF1c7tWC]4k(]$qV^!,Tdg_chZgNL/o8e3QkN3U!k1cGW#IOAMAYTFq8EiY#mRl(:!Yc]d'do+:m5gQk<#LnLMJ6(%Kg'F$VSYtV9XgtkhTk=REYF,e<lS^StTZWE>*n<Qb)e<%Kjm5;DmN+qPCAb,fQ^(\uXm8k%IV'+`WRqH$GkG4,Y\a`thg@T+R*P[;.F]UE~>endstream
endobj
48 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3022
>>
stream
Gb!l!=``@V&q8H9kVnQ$&f(K9<:,=h<a<6_a0IkbI8Q7;k$TG]]L$Z*XFL1l"$AHY3P;Yg<91=h!eEO"1qK/$JE&k&I`E>A5F1fJT*,E+b;BQl\Zi`$X6&#QB8h1G-s^4I$I=c`8Z;(&EnhI!S;"[1^l(<%"4%3J0J4@f=(k_9E5k;eSO`g',8pOigkbFP0"QHpr<0XeTc[Ycm:eut8A_SI&8+G49,'*0U^s0W/Dq[(b[>F3IC[#gl.)8iQgjn[hHo&"Nl8cWX.P`Ff2#iN$S6d@kn&!0Gsjk^QLjaZA<OQjrpq>jA3+W9\>l#j27JSnmZP)1>:BZ!<oiRWV=QZDkr,kn<?0m`jenRF>?CL"j#R?7`[uJXV.W<el2m-]d8aMG&Q+^e.7Oa/S]D'-3t_'OO1Ro-Y@$('8'Cj<mZX?*/A.)&-NZe!7Vec)A<\(uo,<25m0b32SKNU$&^1ncJ9])3gcQVP$>tqWHX)L."?7g?fu#!GpukcpH%+tPi"6mPnN)q(LbcgNITSaoB&SBs170r`a:918Bhf@06]_tt"eY'u/atFks2u8u3foqgL(6Q%&Lb-7E<L2dS=s\i3oMk-+$W\KgUc7cnAteT<g[O[9s8oX*D0&uQjjZAV*(fC7*q)t#[GgC'td9+7V<'7DUKlB\5B(t6+;=qFft:I9CIXV#k$B0)+_l$*Wh/0:W>j<Adfe,/g?qSisPE8fr%d'$!h0$b^3'.d.][eU-DY=1S^Ye,b@Ubf^][]UE_cSZ=/GY,69Kr4W,mJa['.Ii`ubhU4u[Res-OY8I_]rD3Hfffi7TF7_b+f/?/oVU2d;e10'<;(DRjM+qGAa\+X(kc>K._@D_1/+brF*67^D,1NkC1eWDP%l4(`M;'cCa7J7nHe]Nid/o1`IN7;#bpfW`(7?o9r79nnn;5#$ZjuI7`W@4>kI+P:T@`6U8^B"aP.*e\eoLGn?KW$T7ariA&HE8l!`bg:N.H$4WQ,Q@Jr`rU1XsU4d)(e?QaqU[?[>?>9[8'7WQ"rgn>bC$_?`1J,En"FS+"Buc$>IL/DX`aY@QR?F"13;=0LEqh!QfBmKaVsr-&.Zm+chON,I%#&^pLss+"+N^ebeC#mt'eE\F@11"!UKsNe]<^"-3RT,r("]2MO@'+-+Cq#i]s1ZcDB`1X753Xpamj5P$qH]5Jf]2c_F,&RLIde>V^93BVr%q@"i$\>2]uU""3jW0G,@EEAZ_::3ON"'@fk($hZuP9O)mp&X1p"Z7!g<eN5]$ggh+c8lg&-:DuT'baIRV<j4Z=m7oF$$16gP=AQ^]AAo2O$geaC=AnsMJT))j$9aq?4b^lUS_$(URm%Y*4A01qG9`%B\nAdpB"%b^'Ng*n&-"&c;t$,hW6*dYe.Z'YW9#&^M::CUBh'^p,5#3b4==qVgZH@X)Z*<L^;_ggVuN(ir9B)S:1MAmmNLNL3QT7j)f!/0M5hm/oRnqJW&4N.Cn[<*6p$WHf_+fDR3$,BM`;JOH[%4Le)c+i7H#L025VaK_)gUU,@K^qPgWcMW8002t*79Q5]`4:J00!fM?+up7fA]T$:]2:U6WIJVEQYJTrOH@]C0\0k+DEko;L8)M?0aef/^.X4Th-HNZ&u3>af7EtaCfHjDY=?,nEpFKa`Y!]-/1g811;7g(OLI8"A_=.f@'Z+r>rf;bDQ7D/jZnc,a/Q!Y9$bO/R1PC^*O-c$B<h[$)4g2n0r`TqK2eqD;[KZ`^(>psmQFEnu1\kFh[K-%9Y_fQg'dgq_Sr<%`jmJ"'QYB6dJp_QH9FHjsr(R-sf'rB7QK_J7Hdl:&ul>M[08U8]2Sd7G0LbS-f3u%FV_pYPa<Kj6QC8K3mV=Bs-=K38QdkqnKH)qZQ(^HRCP\r;pS<&Z(h\DdO=2j@W9rTYUA;]X_MU,8`28_NE:,`=!\sN.qh=:bYq7Qi_$hY<03FQHkg;=]lfe`mK4^;%lK,[e+p&NOZK[5_$.,Gm@1A1PANiLZW;;$,ojuY?Bm5e8X2.e8QV9ueC7<;[=X+$%V3o.oJQ71=ap'D5_/f0h9VTjgeh=B68h;u8TomVH*7XsB3BUP)#@^^8h>LU#n<3oqqI!'gBjFlAb96]2u&WBq6oDNNHX6EO'Y"LLOTd"@r4'bpS72Qpg6'FWhfP1J7o"g&04g`.!oFUYAaPlLXl-Y;R$Z-W<]00hsbed]\pZeN.gBIE<p??s_97.V&_T2Rsi-ZEikEt(*8`/QqV,*Hj/R2obYZ$#=Zk75X]]^$erT8A>?@@q9Di=7<l+H4:L;Mio;/I]hG4QoadJm-BrE<_!9l2RVO%[]X)8+.cR<7[0e4-$O\+Y*cE?;qg2V39t(F3IQhsGX3UTba/@X*'XX`9Ca&J)M0o5]&,BcMeK\tW\Bgsp&&<!?!;:-@:B`#7=X4Mb6apNXu`FKEXi=mgF`UMq4KMTk^I3$/VHjL"DEW9j-o25m@"gL44)W&-sSeb,jqhoE%rXt:g@)[GCVqc<N$Rs4]1@rP\Y,O671k66q0\b.:DpH%\=Qq\&m<i11=UI:PdnhH0V$Vd0FHH.)99Rh_1JsZ(K9fDQMESd=k[Wjm+FqT*3n+W&+0oGEV?@B=5Z<-fGnG5q1/R[GeQ",T1eqk6smB4Y!6habqKh,@o915-@"]"C*l1)4tcDUa3.NN1qUg;h;>d\YW"soM%8_Rk-eL6Y>[^.sYB/*P*B5uGG,!e;IK5Ss@gGrpCqOXS<$E&6u:C44Q5bm.nLU(VrA"QM?@N)pUX#ZolRPE3nCC_R>-i+fBagMt\cYdp7j:K&\?ebt>r<jEcDtI>3Cirffd6D0sD;W*X%sE>_-X;,9imOIt_F57P`eN1<"cKLZF3]<TXU+9[D<RHf7g1:dfuk%g$fYVq4SV[*2LT3OMIr?ElUpIj[X+fp\/<J0*EjMfSqI.[AoPe<!?j4;oqNPY_pJG(1>a5$fRC$D`ksj@8FPdH;`23a^KpK8c#0.K^HR71L6iYMd+B"[TX(m%[RlWZNT5nW3ZU7h-/I32W0Z>,U#G?f2&=(]~>endstream
endobj
49 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 804
>>
stream
Gb!;`9lo&3&A@sBm*Wps.%h)=DJnI(S2O*+?E-bTaOm=6JW!@&5JN+e4&/[t1=K0tOUPjrmG#ZK$n0tKGh4O_LD&HN112*E'sNpu:bH-^:4>qDc&.+N5T"-:/qJ!2l\WSC+Xnl2S3\JX_F9bX`X(ZtNY*11V\?oSRRdo6Q#QdlUlt^%SaH"=GR9=)iWZ-63Eq@b1+`,%nR&`l77tE7c_4a2#[1VD$e+F\XINRF]"+9G`>ic.<K_;@>g5L!Zc*685G1-pi^EF/A.`0ValE/\'2I!gs2*<7X)2f0mt+W78i!h5[=a?X!U%b2bYm3LW9.Zrj/jSPL<VsMjGW_;Q)'6=:?Vu0.F6#]?0B#UTY5Ir+u1mh*>CWMM=a[E7$>4(B_nabgB[2r;7Z_\5$k$Q86J@X0p1s9k^)HQNVsnToGO4194Tju2b*:gA5!>P1bKk8j!,f&?u\50S4<X69?JMX:UWqq.Z-&K\h?F3WIYbO(o]rtO!5Y96-)P_E]eBIaRlWh;L>h!I8I.f9+A9fb(K#Re*tQ5\o2PfTPkR82e]YDRTe[-JtT6OiP*k0"$-UcWHX=cgM&S%/urQ!Cr?_=@j0<Tl@_83p92p,14T2Lp0E^aLjA$PX5',ODh>8u`NtMEmGV7!AshMfl[sD[n1JEojiue.YO5S>2(#K>?7RQO`j6Rg:sEbYcI4^)n?pugO.YZEh7*]D^F7m7;*h=S8%Vf#rVI#q'#.+(0"%R:%TV@(]<4n:KgorG\$5C?AK'BeQlL29@pJd$gUk/S`\-pRisR^dlAY.[XtrcN!$CF-S,~>endstream
endobj
50 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2630
>>
stream
Gb!Sn>BAQ/'n5n\3,_Z;AVL3RgZa?[[56XD]3/@oIAf1FR"T$e8:gk-V<R?-mf@ZqA4p'KRdj?m`thD/G!:<:SF:tOi8Zb9^[hfj0+3#G7=\3:49,]$&/Nl:\N'J2k4qlCdL\N-8JGgq;,HSb)Z(hKn"k"t+Nbmh-;b0`nU)7nfRh#Y2eO1'r?.lnKpfUS0*sH7$f/qO0=G3o[L_^@GcC)Z0/9^^9ZI=r?..JjAKE-SL6VI>ihtG5K%^%eMoEZcO1.%anGOZg>qCVYil01(h\I$[Y@6rO/ACAhZ(o?GAB.Jbb2_OZe>%KFNW6]9gPXTP_PK/JR:Qql[E'XF.8Aa&X<0jHlWg[bq..N?$/u5Mhm_ZZY4OIT!t!KK]A]_bU[IF9>(W[^=1(F-E@&KCSI(?qU;)CHaLjKlr,C*PNK)!HLtr$;I5DDH,g0(Y5EZ.+E)o'd#]Wo5)"\IsF6Juf0+O-ZP(7chaV#jU55!a\mDQqee>=Iqalid,0&?3j16TScZ!J3bBoFRg&@EW!CaLS`'$g$B1N`1%0nfK.7krg#_?XgM[j.bf5i3&2o@#l"3s:#JT3onUl%DM<,+YN<5]GPaO&p;tmL2he1b?AHbK%pAmeXV@GPo,nrjIN-[bL&EESc8D/pnXu&pm):SP^)(CTTqc>,uP,5o^SU.5_ePmY!L2@Sh#gLFdUjIqJB&<=hC[*&F;<91At*m63O`Cm8peb^>Sm(7gQF5$N+5UBBSKA$\DR\K*m\*P^TUDtp([Km3b8cKRS-gl4.fG(*(:ZHk":M%)i0hruh_="/U1`=NEp1W4nHnSHsP>09;/E)@bQA,u@Op\kZ;\tQu[QtX?#dp^h`A^;u4K2GP-%/2UC_AJ&t[P["A4_o:fSD2).cO5&:gFWgG8cKo+7t/D(h?Hk:,'ZBqVF'T)_%V/L.j\LYqI>j58Dn<qC?o?L*[IkM"Tns'aUENXWa[#=B"ZT]8$CG[^gT78CE%HC1^A]pEVUNeKgs/h;0!*T;ojAfFO&7u('c0Y88gb+p>afX;mPJGbM(kX:6BUW3Ihj0W.iD_@O(o[D9_WV[^B8+cco3<#t?ahjn9J]%d_&2%"iL(_a=pJce%2*5Atj!3+<XG8Jh>B/t94d397T@NrCZmbc^&2e7`\&r4HL5SARE;f8(\u?>E``Cbj&EBpt2\5_I!&NQ)LD/0#EG5t/&K+X('L$j9ls2J^*X\(>iNaAV)Nle_pnb0lIXj[KqQaZhKg`on6K=-_.GnnOse-cL&4#EP%&9u/.K:E#RX[Qmil"G70_kdEc6bXuC#&[KuY%X%;3_]<62N`\c"i&+n;a0pCMRfHD]:E$/dkh8.,e%j,GCAj3CFod&&<#f>j?^L>T$'"5`i!m_6(k/j8-+mitR=J>FV:?aB'()_Di\EJsjs'qpaMUb.St:,!0;i+,\1aqangpbYGqKi[3MNsdHO8dHAm\WMm-&9JN3d\9`p4(5HNqcHs"!9'cEWV)_gNsu[ToJ!)>\R8aLW(n%@Q>Ajg0\Mej)u+ck^&1g=i9R@\rj'0o^ah@^YlF(DmOR)d/)G;$n"^*4i$olG:=]r&BGO3E2AI\lR#+'0lqd#"t-XT:r]!ciMeo`cA#5.qcppRp*D9Mnr8,7+!2dQClkbe*?CJmM1PGiX"5Ph;n_hNGRc&!HV<)]^/j&9]\rB5a<>6eL,quqJ`JL8)%+F[Ddj;[H".WqgBPV*e*,H6hqUj>7=RJec_'-i\P3S5IZ`_g;n#?gD0(]SjLmK47/QP"/h-5NS/'<rTK%UYr431rHHUEcc*ndHt0jG9][\"M@"k_pd52(6E6;o<J4ek*#NrHJ(S5?9,tOD:f(,.G)L2g?+E9NhO6eLpaL_TMXliu$MLK'O7^JC+e%bqBQPU=:/<)<0;alg@_gPa\[-t`_p5rK1^[(@r:eVf4Jn',4%;Mh9H0qoQl,\WU1[t%UHQ,&D%2/]Sf-9jV/t=/pbTS*OiS_rL&1"uH*f\&_=_Pp"JT%Bd[o:t0o-lpPh"EM.1D$2,I+mY/lll[[=Q^t,Fdr>L6lt+qu5m*mCPC.[]JoSC.tV1.=1S7@s?P*RL=Ia#J\)^CXuiuPfU,ei3Gi/M>GU@ceqOjYn`a"q1H]'8L$%$Q`)j[%#G'Lm"6fr=sMAb*OkDG*Gj9S#dnm5>`&mcgJ4l:;]$5hA^lR8^b'S]I6]+"Z#8$CHTm8`Z)[n@<n1GrGHg+HD3dISmkIm^$b>@M8!RrdlL:1A%@s3q.J[IMdBL$\IYU/>9"%G)X&Nk8@kk:44O)JYip=E3BYKQUT>K`S0+&PZ"FC449<W5/8ABg2VI-JoD:Q3oLVi:W(G3/*Y:<_epGo`DUD;)bF?$:<9YglZ>H[?nK@S%>SQI?lhCu'WG=J!e=;aRV@A^[N,4oPoE["qt_bI@UjiZRlXHjlCDgKe(c5u=TXf0he7o;6&%JHEHgi-9#I,7qS,ql]LZf2KI8tb2<+ml:@,k/oBqA*l$D7$W@]!;[h^&iIl`[J)uF60!pqTSMP)cdJA<l5j\pt2kjQ-]/'aSoP9*Gi>$TF'.V=%sb>pEXe+]dPU,Y?1-L0$B#lhYh/ZftG_F=0/hL_"tN@I0m6M28'-b_h64igLK*_!bu6hT-\M;q[\3p+uK~>endstream
endobj
51 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1547
>>
stream
Gb!;cgMYb8&:N/3bbH32lb2hSLB=Jc(F\Y"NR[YjJ?U.tc3=uLk!_T7opoAd&g^2-MA?4<_'[-lR@Q?ZAnT28ig9K@kn;q;.GW]7?lZ$O@$4'J5/>uroJ.UILgr&g0[>8(!Q(OV*YkUi-'Z>Va<4"T_P86,J.]pmL_:UAmm_uL<$WH>9\^)=81*sJl>3R=\ke"M_L6sQ:8`:jEZW4>#Os8/@n@<M)%Rm&PXI>t_'N>K4cH<!qqjS#No@kr9ZAk338QdSI@Q<>/n+S[VXJgmc?`"_B0X6bO5$S&E%3sH+)KX`FjX=$X.)^=1D-j(P?O>n*l4s1(+.L!C/k1UZf@a80YlUj,Pb8>oh7nbJJ6b*kEH5-h&j$.;`)'8/:r\LR)TUo4J"sPKjQ)NMutP*b2,U4UQ3FoVtBL]Osin27m@aq/pu/43i&Ua%KcJIRCQ@/2!>Pc8PT>Q7*'%H*gjqiSMAH0l5@n're)Y`-0Vo'*L/I=V*JtVMB2j%4_.FQRD\j!`M#A,EoTsD'qF%=UA:l@<LULeQg/MD2KbA@8scQ\&R&,3,1WQr:o[,Rj.phPW#G:@dN#5&'IHiQ%SheAips151.C#"%ZG\$Wrh=:h'_et="BL"*l2KjLothT%B1p[#Sf"OFc&p6DfDCN1D0.NT)]tf&oB$.1D5QnL+j[c9-UH#&R^DsHSL860$/XG.uD:h3*bL?0:rB$ctSY2To9gp4*<3:Osh_-*@Ob!qmOFtg$hN,`]a6GO-g;e%r*Z[%VacEmIe"_?\"'`$n`TR2)Nq&kAP=&EkTlZF(],e^>="rp;KPVQj%LJZPLgT1t7:Or@@kq)7_*RNuLkCLR2O&$K9Ge,9l&]J''?/,'FZ@R!AR^GssO,]U_I<&1#qJ2"''&'0_3YQEjgHa0">q4KrYn`a#FA:cWQ:M<1;/.o=R'aHZ-A=('56g([B;$9C0V\QP6p<Inm(]0pG96*')DPRV2EeePq(YVESR^U1T&?"lCM@!5BkC5A8^^Y@"]YbO>@6.djW>(s8fGY6JcaR$ZAe]#\0bg%h:!+2t:#6,I\BBo6DAsCE@i31J,WQ/Rq"d?tuQ\M*2VT@!(A1^^0Og\5gnQE^-[$/;2dL0@>>]d3QpX*L1XO5dZ*"N2=3/r*`a2>H9Jp5l*Rh!fI>`!No`Srh[Y2!+4e/m@>q"s9cq<XX!]6-h2mnUfl;pW<Rlg)"!!q*Y3)nt-"knGlV[bW]XTjBlREND2%ZWMU]^B_Rd;^+OE&0cm;0K08]^X'*uLHhlT:Y^?[R\?@iSgp9@g*<Kc7s+Vn`;aJZ^ND2ZP3)DjO.#Y`_QI5BC&MX*WVaEEo"WU2GPm_nBV!kWne3Le2S'4f%6EA(LA!!:s+&S)4p6&Lq=HYpZO7cACRhK].(-oZEsp\:,265oJukH+bUu&7p1pA&S!2/rHJU<B8>N]bD0K<o\__[+)WJ7^Nfc<Xe=`JR)0V(+Nk!/'09$[jFH*dhLWXcN65cEiS**^]Ch5Ael1]-2cCafsC/S*BJd^C^5M%F;chb[0TG@uD3+;FiDpPn1~>endstream
endobj
52 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2232
>>
stream
Gb!SmgJ['$&:N^lqN9T;Pe$"H2o<EY.1#:CP:'Z@H#.7hd[608VY9?)4h=b8(dq7nXTN.=Fd\X]F8DDER01?6mf''e^eZc]iIV.J0O"PO4We:&E'?@p5Oqnfa2/(B$Qas-GQR7&W5SS-KR%PSEX7iD'2]<-^ca#TLf\7l]KPh1b[_p5o+QEP?K<L#RG3":?V!Bn?8(OJ]U8-OQ5J!Vj'dL`P]sAXM'oe=+m.",=H"%3E-u_P3]k$brcE8in'Btal)g0Ohcu`P2ESU""1'X7HeXNIUPE/@j$u)jrpcEG"S:C<hj^u%FBKPBYA1-'>8cpAQ5[7J,(_UL0!`#?dnJl8PO/lLL&hLn*Q]DW<k$MD#)(%Ni<9l\qsO4$5ZZIiQ3KXnQ"79?9t5Zdf`lnnG[VRGJ(!`o(D!u"ErThM7-o7tP/KaQ*_@M\e1Nd7?'M+74sDf[)^F<q15[6<g^A?>+eeEWF([EFF69DQ#%I0I"XG_Vh;]eD$?9Rh,REEA=.UOL1?`610eO)6n6[-uj=*Fb]*\1?3WanaT[7DkfF>o8^gi[H`SVm//&&_;+juJY!^o),IE?Kb+iANnl,+*fp9e7T6-I=K3IL8ghAp$/#MA/GGA_fWa'mhrVG9>KWl.q75oZ%Rel#Jg>`.en=1)3K>:>kS\B7]bEij/GQ-5_;+&C3cct6f6KGP(ZO*piiTHST#6qeFn6A!2>\A8;@+0@rZ3?#4I^aI,4.31A-$TB,OZK&Di@*>*kA`*aj7OG7?E#1@2,f:dFF2,T^26<pJ2^fdcIkcs*85$``@;9<:XL`>U.ROZ$=rf;?M/PeMV$NZEo`1fn2qBOcWle#7ZDn.md=a?%bl,m_T!%]`'sQ9"J^g?Ag`hmC"uK(VV<6(g!Z+[E<NY!b>,&I8aZWlLkFb)$A5C$iUB*TGQ!;;[`5d2\Uh[5^*4h'`H\0n2GCW,W*I(:ra*m)mWU8`sYo#F'-#0(+!`S`EMeSSBg?,V6eo(/m*;,'q)766bc(9mM!WiQi^/"1j8o_[7]Ushg1rS;hl3knr[sG9R0<Af`gh*G(64mYGd!#@c<nVLnT;4"1TRS4.l?n:R9NA(_Vs&qlRKCige64f=lKI!PenbhBE;bURWC>O.D5K9BreW($)0/J+-'!t,2PAj)<(!YM`i>Q1=7>_Cj%G7ATUGP%HMW5\s#n64W:V+djM+#?FK!eg62%=;am;VaVogfbjXX?YQCa(H@?'#k?EH)%8iBal4^Fe7.!cNqhRiqT),'>qeJ!>o81n&`X.:(<Fb7Endsa9>blGd+HI6eYjN-Z&C$o/-"ZKGIhTA[WAn`a)4eA48]`Yi5jn1_1\LCL7nu6Na]E0TD[[X0gF?@A<L9u<l5$DEl`M9lYU3^gB`SlmtK,_)YbS8,5M_nQZ9GXWt6)CWbHEC$=djAFsFeIn3j?$A!1(jjBPFW(rS#X#EfbFQ0*AgTZ+)]r1Os*j3%BkUeks-nb>dDf`P7fi,TTt+_qPGjGba7/PgB#AkP(NsP/+^&Ec*LlM%odOB1'L\jKgph`]H/&;aKEg/kgBZTS\QP$9CNH&.]iEf\fi-J%#_/kf1;pg7jaXq8Za/nfka4`>:A`&=8JInge7\rPQqZE[9]L@IiDC\$Y35)lHFEL:80V1brM`"FpOh29gC(fcha+a[TWsi'nHG(k0uK%T>+<SUPb++XIAQc=POrU>>SVjRL3#R]U3?n$@.5kP`iL-<)0kkr=KBVBl',ZQ'3Ie[6'<0<S%OkgFLju1m5sh-eX4(pW_@6>Mfqn>p5;`R?QFManQIBP#>.;"*>e5'Tm#AHqd*1K*mGrkD$PbMga(,q'kj\Zl2Fi>uKWE9#:nu"G^7uI;[Wi5%Is4LlLQ4%(%PO!`e+=Tl)s0];EG#Ermuno0%oj><PO,55b#":/:s(,n]shpY(7-0<S,h?D5`/*Hhp@dV.H(`UaR[e]\K%be/9J5B?TpDS=7,&Kc2iS6=Zsj3W6,J_Y@.9%m(jE341])V<_Fau/l+"BU83h+7HAep;&NN'JN@Z`.\EoOV72J7DG?!M//QM]HG#"niJPMHi[.jI6onj$njH]%<B"jc&^)Z\g:.\D5d;/_,>%[PoJ3)j.8\$K9(W:H_X+=_IP]pY_*J(&EN%XXZ0fTmsrNAW7Z#4@6[$*-fZ?jiM7oQ'ua>,:HpiQ(%GC.*=uMeRf.2#Mrq=ZXTuhYHe^n<7A2PQ5ZO`G)I^]e<Os[eo:6t-A8Hl~>endstream
endobj
53 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2425
>>
stream
Gb!ku8TPh0(;B*9]H`sb\$>Ro)roSMRBg;Bi[b=8WLtS/+k$hTMk$U6j7=>=h/"]<;'$at)0mB]jXhS2$?9JKLYMjX*8nF4]R%bP$WsK]qjn=VGOT3ap$'V/5o^7S4>(ji-K#3/2HL'RM\>0k@E9T_!`)3M#Qa[eO<O&=Yl<e-j"ZaS!7+GS!++_6m*'J+T*sm2_YoXH^l.GN-XoIV`e/e$b<'!m`"QC/&9Hu&/B3Z0bW`\/0BR31J%VAjpM\u%^6DORi;1]ZMtst$YkT(OhfNsrHKYN#5=1[boU51Oo,[eubI#N:d?O-tDCX0fA\^5O/R^(r"L$!XD-"5=NQR9L9rfJU!0uHnjIa#`QdV3H#`h":Jr.:&r_]"iMFLZ9M'/N<VC>"DV_=2"i0T9:`W>Za]"MPDE#b#;YHC[>^)&(e9]jA?.AYMJC<(b'j[c);cnbKNOmk[T`=PW/"[%XgTQ-7MeK&/FXD*U/Ykd2Y&k09B]S&cu?EBIr@2u$&$)/c!?=t=SORNlIS>$h+#f5sH9O^$aJq5&pM.19-1*S^<CeoP9^u+\>cW>pE,_?#YNac@9PWND8Jr`-$Tk^3HiH#VR5;Th6mbZ6n]a;.%@FrMSIN&"TAno6(A<b_F.W@udZs(lb/XNiU/lt(a@AVpQ]iiKSVPXJ*f4CWpo:&(!O0$_Nl$b#gB7Y-M8-gjebQ*XkZc.c1NO?o^nK]B=I#b[1)"jn%Zp8EUCs_kYrK)DJTYID!$p:R:g0k\:5D7!gi)WXbbf%I9:KRq=PF['qFB9+FK%YLl#<[ka*Y_T37]@=tb8aEFX-J9ZV0-\0gk5>YCbo(U208k.UFR:)<]IM;RHhUu<ZhoY.6tm4Si,dCf>2?t]BNjr&\YcF*@.7[p=i3gXWV>KT0@M3X\>`j*7s[f0JjL_[S+![]?I)9%$d,:*)Z7a25tLOhG\7A+K>&;[74.8h&M@EWRV:Hg<-0?L#5l7NS.M1e$<X`\4f!V+&HUAR8rLBnA7O#_*$@SaQX.3N_JXCOm:=J@Qe"I`$coaMc(c%%UJ#WPWO%C%"CEilgAH'$jAo"pKcQ7FEuQO4QilMYV&_.Z>EbJ*+AhS%JOo3%RkCNgHGr=8N?GCah5[A!W7Ja9/36E"CnC&:saFE*X$s.]*#?7OIYAMWYs]r7fH>M"/\$6je%+`BbXi2i"j6QYh!L^PPdURad:ER:mGfpf;:+m.[G_ALqeoDlO81j.+%9-8W2fU8DgdY>s4RdX<pN^n-c)48>%D<XesL+YZC'`/oZ*T$d":QN$@F0LTGa@.s*(7QfMGNqA/*gA$Dtd=$=T,F0@+%20F!.`fhbWEE7h73KbCF/'Bb*72PNmN8n(:eX'F`Zh!Icp5CHBT&!qRE)81,qt6\C0seFX7>UhAV8Q4A@<OQdk[LAnhf;5TXi?Z<=</8ACbCQ#aj]>DfdV22oMU:_9PtcK?3ZNG//:lPP#bZq@2ZAqoM2@<>>qIEeYXhT0;.W1ASQF4;6Ek-p_QSals:\[S$=tD:K&PNVc:3EZc9XoaF/0b%)HiT;/]H?@sT!14h-X5.qtr^L?-<WLGTj6YUK=sR]Sd>--r=*41YVGOTs[U^JK4n?dB_8]t1R.;cRG>"=$oGNO@c\b3D2K)WF>/V^q\Y\G[s*2MgVA#:hm.m?HhY")b?%X>0)+I&@*J.N<3)?$STRr)bAI@"rfGV>=`>ll*6f^k'achtLc^9l=_P)YG,=Rn\'i';_>g@Zl1)mMR&/H<MD1_]$OYXq\/hqMj.g7AD*U[?$nE:7OBBTq*`hNS#k;UEbmOQB2K^c9OOIO3tuhkr8%2GGZP<26A5l@GT^9j#ZOHWsAQ5:R&is'&,:]=Lbn5*4WPtd7D91#r$OQYeu,7JEYKp#<5H/[=0*emhu$Wr]ffgr`6*NV8nS5c3^,i4nt`N#g+omC@3eZFWk=T`gL`DB^=.W^hh+;pUWe3FVf`k,e4R:)'N"hGBI)WD1aRSG)q8jJ-rOSGX2$rB^6h2Q7Xp3'2eqUftZEor0A\`>$&1F[9LO%ItaUKdSV42n&ftPiphKMkOB7b_>b@L_'qon)?d5jUY?rKFW;`E;f(B9:ar-=EIc`U\QuU"XP91%kS9+V9(lD"a82.)WV#Iqct*&eZG]snS1^9,())-k]d7>O\_^O7fr^ea?2t<3XkIksX4jUe=.Nnu58PC$)u*c#^2Ma<37\:6UMom'>utUo6+`X=?Zb8B`di&*f[<,s:$(-`<4No75!V4qIfZq*61h5IpH;p5HW=kl@&:?Q*M.Q25=aH%dB)-MS*&Q.rBSAN!HMOmUdQe/PNgl6Z7-hoAlad#E(HfT]r)&7`r"QgC;1Wbe#-4]pec4%'"f#'a4OIQh!!SL@<HLeh$@R\^J(HM2a"]jbA(R*p:#L,*A/mU3At:>YC>$%3m,Wbo`n<6(af~>endstream
endobj
54 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3011
>>
stream
Gb"/)gN)%.&q0LUo\1cm@tjtTOEB#:33g/^:#^//c0iJ_b6:PCb`#(H]K"#>rdDDpRjabj[?e-Pl`#^Y&=5l\l%=V#(teU*1Om\[J2^DOIE$eWR,.mc/Jk9+i,j;7jhS18+Q+kSN&+7_?ua8R#76F^6e_s$@,Qa1`I1O4a"3]A!dc?*rV=6<JaJ+1/2s@0Y^AC:7u"?^S=grGq`FEP<^:/0'7I/?BFVm\<pq=C/^CkVJD$6,#M:bcai0%ql1*H1QQM:Ij:nbWB9'n"iT'1uo_?<SR!seSch0CW]<FDdho@$ukI8"@D7>u67*j%Y-&%*&]Z=V<F$-mijr.j%qs[W6\'[Mgo16#*/iL?W7sMQ>r_EHmZ@(fENWsCSeb%YZ?M3qpXs$CEPpI?>LmCG_?<_A.Lj%s1-aZ;"g$Y&uf62%eUZ\3`4s%5"+,0g4O8090bc]4Y=LH>[%=LQR2?SAF$-*RlGF7Kscpp@]>MHgX(1t"+2K+/kg(\MQF>LoDR4g7u<p5B*T]I>Cc1.5cZN,u<6i9r$oZ9`3E9dZ3ICePH)`V]*Adln:2GH/>XLJ5P"+S/e59HPrII;CEBg6PWL3<qDTA]:d#Iq1FNU\`kO2"b8<QEt'fY%L$lXAYP&,OeYgVD=c`un%I2W[r.ieUdl'dAufohdBl9#F!3M-T8W6M&b*:;mhu<A/-f[5Vr1[TB<%cai/HiCD7C2?"bZ0k^u4((0Vlb:q*"RqBhR,0sZMDKkotr.8spS\MWka0X"XQmE=8*eO8>@2AT-5n#8=Z+.%=/>l5NQX]$KT/maMR,EFYjDgEKo18$W(,CY3hP_1g:9t5J,aD4:$OqCC-fZHBCq\7B/T4\;]P_TrR*^p6REUZ&(Zb#(]=+s]?,bW]*[_ab0:&$;qXafUD[Eq7,[?V6]TKb1M!>\uAVt$]'d\Tq=:Z[S`N@;PR1nP1/.7G",`Ckm67Obt?V7m*;Q?h-lsugP;9`ofQc`<KY?1I\TiLCjcu0',$eGKq6f*/bN>93#F3A-ZpD>@I]==O]m5_'4+lcAAqDDZ:.ZnN4+%"RrZR6/9D>Y0=LfNL@[kU-mi-I:$$?'#*_'[a7_+]_1r2R:Z\)N:i1i9JpqTNZ(;aD;[i<09gMA[IOpF9n)H3@/@0O7%\REHGi10VIu`u6Se)[n;uEh1<_U#Tp72k/#t=n#J3.AhQf&t'Hc3(!9<K#&2?(:^IFR]+I&aGUB[Bh!=r<":C4@abV$;kfR67lZl.()cWi\B(CHB&DR\q@<OW1MSu"R>R;%e^Oieq)Q/Rf*.\2X'mb%,UJK][2PNqG7`cK"7OeSV1u/E,1+;mE(="fm*Ac[0b+"U9q74tMY]57WVl!smg6_IYtjaI(aS!uPLj^!chciqB(s08,0//N#h=[72"B@l<TY^Uk\hJ7^<Ij)^"+p$i*6ng+WuoV&Su#nOZYIP0n+e8-ciGS-Q^lOhD-a(7@$@\OF'Q#0$C=[08-53el.j-)i#lK;+fV`f6q.U(cuCUa\W&lU4Ek:Y$rLFBn[a#DJieh[s@A$^MhYNL`FTplptos9p*CL.V=4K+.Wr4dso&B53@W1o.cJV45!-Vl4<+)B7%88(MrrG2k=o2S'C&)ZZ3GDp?;eg%`[&-i^F\'34+o;n3_*1Et(VVC-[dbWatHD+1el%4Wc=b?=uj3B<4Cb=CWZ,0+[^nYN;0X8[<O9@If!V*\t-Ro$K;Z4K&lh0"l3#5%3a$M4I8@>9&Kid7()34*<]55HK@)[I%/!^:Cc;=;tQ5<'#DWL.-4qX)Abq&e?V4bq_SuN)pB$6&,s<c>SD/J!l<cRn0hWm].>H^q5d>hDD5SO5_Q_=!T9Y-l^4$b_+_H+8ugSe^cOi;WAhdQ?n:@$h^V=pFsU8-P&"WaMerN<NGDL6(*bbK.ACSNZaZD^Qo8?<hS`WV,j\W)jPQ/B9MP]OK3.($MI*^eA<GG,S63Zq[%qGcZP'k(#RL1J?1?uOo]!B]<?`YUX\(8nB`daleH)EFj?gkgo3uE(bC'+YG)/f%c5/N$!_Y7Tqm%$E*Ci1qpI'>C:3g*EK@^Cm[iF%Y%2OQZhTtEAse-DKXsUE)55sf4rtr0mkJNILuGaf\^EG!HAuqPpFL%-&nVgMm0M2c_CI4OMPE0l6=rfR/Fa5!#3WM;U77<*$lL[odRKRJUQKS?'\%bt]#BTMFb4(D4@n:S0:G??gXF8aY%('I4<RUsqgs(:Xc2Y*T^EC#fl`]G]ES$,g+E)oIVl:]ch6UBQQMm6C.LBERMuZ?+kYms(e4U[3u/Wel/bYfj+-N_H:bjUiL\j$[l[aRa(\-U2&(Y8SP?t?p'O._3M\l/4=e93H1khq5KL$;*t!%h?cB_^jI8j!.m+HFZHl^fF:H-o8PI@t%3-<QF!jgm;A:mtD8."Z\[sLp\WY(?*6T=B]_b'LV<dTlnL*U&QE0!NIS,O92jZaga`Dr-IOq3qrF6d$FK<*R5i5G25J`E$Z-!W\YHOb9^;)Wik0nbCls/#KaQn,>(HNTbL-<'>F0:^r;87F(EpKQ*W#X%L:qXn=1lla1O%8^:5=l:^!dJbcq_:6:ImP+R9Op0V-T%i7`MS5R:X)Bc.Tk6'pscaH?$.Y]26Bi:W`nYdD+43MXn^DS,38#iHtll&YA(tRVDW#q$TbmfA!Z>[Ed?Jq&"TQ!TtLtfe5Ui-CVM#j/[4X^g&mnXmtb8Al>;%3?tG\r/L>IUs1`)n<+"U.W0hXQ9?!FsH``YkoWRWf@>`a)W]@>XN8^h_rlA"u<tOJ>77D5q!(o-*XJkbfd"PSefGS&^?gq)i(TN/6G$V81?M$Gr:%C&>fmWfRd=P`aO!n.1+"*h=(M]=MOaP#'\uD-R#'R+T)QfI'hAIc22cX&Ud?=-S)Xb%kq/`RImM!&#U.*l]*']Z$s-&W6MlMg)L"CaPNcuj9qDb0uR=sg*3W03(NNI>Z+&^>;QcLj<#]sFo5dO%SS!(?Qb@$B;31_`_Sp=_%T[<bU`o5]2XImaHrL`o$_1)HN]Hm:~>endstream
endobj
55 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2295
>>
stream
Gau`U=``=U&:XAWfS@aSktDp64?M!s2g.5r*9U5S(JQbD"Z]7*6KTG(;YRqM6')XhM3J_';eH^f944rAZ+Bua&H"nrJ"m1_04T7sM?7?CSccAP)oe4`0g<-2Z&%H;bQ`t\lF6js%'l@d'\@U2Z$b0"huo(Z4N@V\>(gl3B*an)G[aOBn:6($#AO7)B*Y5F3Gm?XlGpg<>lB37X#7;BT$ktE$6kbd[KM0#.!*5&`:A_F',N?KIsEq9HhHgegG:h(J59ekc5@,K?<iK9/t]uT$]KS:q'b[6jXC*LMX9J$R)Ol`q4Gj/>_obKE01r>=A64lDrO=)KJ=!Db&e]N6lO$QZ7W04'^e"qr2(ciLb,0;RnI-)dHh`ZNZS.j98M%`A>qsEU*ak'>6;E&,]!/G^iFjYfE\*_-Ca,\\OXKVI,"4D^fTJb4jJ:`F_)S9.k=g.IDEp?(4+2F\`\V2H'Uf"%g-"U/O<taX\?dmY3cgF*IM6V"J-j.>%rqD'n?(\_t@+/h=Ho$3"K9O75_3_6Qm.nUI=N,o,4ZA2=XBj`Rd;IUtTt8m((+kr8<b:k.!D(Ak[3k!gIm?2OdTLiV>C0#e_;k)S!3-7Y2#VMYIA?iF14tCIL_9niDUPWt9!4(AW^RgYV[Uqc^rS>;TNi<Mj'1R2Z5fRY0R/fp.#b(6&6Qdhu,:e,i]Img)5s?!VU<7/:":Mc,69E^8psqLe)cYiuB$B^Gt)8#Y!c</@_HV\Fk00RgACa&[5rEg=57YG]Wl."rR`;r_bq7=>qS\;^"\mNG3^1leGq]TX3oj^q606mS'e/CQIj?7$!62d274E[e:oSBIu=Z:=.T0Qr'$NY1ZN-kg#4B%+!=9*u&N#Un[9k-]05/ACd$iZ&1/n&"*I/4,^1LpB"Y[-L_kGDG8:nF)\W<QV82=)B^X!jq``^ReOIm[CenW%nA`')9sJ_`XW,3,!!$-BX1;MV^4)9[e!c_/b/p.Y<dZMgM[)F5Kl-;t,Qti"AQ37'te2hVAk3TTu#Xc@D;7Rb6l*7_"W!F-HXtYi!NO9Yk(GHFm`2mdT8F8fbOF$7"(n<P^L%Y=0*'"O"$*`NF%2m9&,rj&kTRWuf_mhVr\F[>E=QHUj9Bg]quW4%`N>/R4bXgj+)tqC$58+_;91\QC0[S",m7)^8jGYi&escSU^D;W)G\a/OSo!2^m?)j(-U2-@X,m"ue,<?]Er67<Y0rM1')W6?V]gi"BDMF^I5@gcjjmdm=8Zf(>1cS5]O8j7D?ok31Ms%X*+qC[9%@$@5-No[\"7VqW?jLD@G\pb_cML#%HrDY?AnKZ>!r3lO:_/@H,Hamp<Hdd8$l-NNts5t0"1TRmg(m`i`R^@=?7J!maZ)TmA!M1m1B01G1q_u3:J^Na[SO$DuZTV$O5>Y;JqA_;[a&7&qCSem3a4it(a1;kTDYp'<.`\=Ad3bm=@J^"=c)Wd8mK!q)lb/;*:Q2@A%>=&>fOsB'VLVL[D&F[tK(VFQ]Rc`@GH&Zt$e\8X)Vpu%JRd"jSHsTk`Bs7OZ@&@K:t-f2;Pr`rK<BlZ,<9SP&U,?kC'EV_6qeD+EP3$Yn)#:!LbmG-I)o.)Tss6;9Kn+UQ(ZT(%j]YK#!-Ui,Oub7?733-5GaRCa+q5/(n49F'rOosKNMb3,C,j!2sP/*jg[TUIS-[#e*ao!2AZA599X":W&T0tNDem@k:WO`'>rt:JbI)VRHhD97,Gjr].lS?C:)-r<1?A9QQgB!foHV-o%;X+B/ZK[hsG3I:O-G>3??3.VtpJ]'^jr0"T>=6&$M%g+`8's.kO!rW9]\rJXi>K@8V3Wjd"HL_Of9CPRQXG1DRRdo%,p]EHILSo\[PT)MO)/rN(#T'!p=J?K&E7d/b+5"f65_VWNnJW#FKjpU&$:_*Ge@TbDTUeF_Q!#6RD@[%-mJ!"X(rg[!&!c?V)#^OdF=a'<57YZTf_@Qn.Q8N]p+^6BMD6T=!Tr&'ndrh8uA+*r#)qFpNP?dDQDBc?,2>Wk4CqL+HX(^fU>,3AR8c=+toqZ58VF6a8M]\$VGR(]ArSCS-eLtuot"$aa"l<;=uK^#-_9KcYg>+%[fe\ksfYJdn;1WD[D:Lt.#;uL5Ip6Wid?B=fVNhO`i=3<-tAubR`@&=E)9i!tkV)jO9i`oc0Y=+n]$\uah44D7/mn;r'\pcWUOa0ZAS#7;b1/s`#<#ce@qWY!Pp8"jWCFUK^WpYp*<K.D%!NN+Y1/n\ARYi=W`PZCa`DJu[7^Qtc-F><X#.4%fr8gU8#Nu\5[!VB?m;+q`rCkd=p#?LOr!/!+nkT~>endstream
endobj
56 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2744
>>
stream
Gb"/)D3*F0')oV[K&>-2>(nUR,o*ol[Rmp;hDWW:YP6"d6-J&;Eg#YM-+OESj)6><ZFmX123tka4'[Z?/4FSkSfrm,B1rkkIn9<X=s3J/*5/j/LeoqC73i:.1qu>[k80aC!mK\lL+"1/E06fDU?_0@KR$FHK3/D2GesP$EIg(U0P(;T.!5@b76k>+&rM8p0b"gb@=fNKaY6MX57riejpqf.1T+;/1)e0-K$[/:>EU8)KF/D,*(2c@JH_LU]t'S=?2sp'[k%+92>V:"@#RG4\(>Q\=#]$1q+!:A^0ep^/coqAO$Mdroe?n(%dj+rFP7M1Cc$HD2jJ1pN0Ck)eS%6_KO3s72I!4;c"+OAm'nK<E%lamP!;oG03H:PU&h^G;bX`=rN4X!.iVKR)F+F"Z:7Um*d8gb_U"B1`#OOCo%l?NS.<V'b'c)VK.'<#Zi*G5.-mu&X<USTji!g9YVV&k"EWmrLrBBB8H_*n53))&<\hMIhPT>Y-K>sLm0pJC0U&1Pafr<O/3bj_+DOU72?*fj^Y@.#L@;hRc:o]p(_csgKi&gFdL4a.gEH1JRNb)bEA]LaF1G2Zr!CJ,<NbF]I5\^ChSJR7!U19i_@aK!")p5N2kUI($+QQ!?4O6fG#)7uc&jJ:N"UKK#WE(q;%3%/2#i/>0BHB3>1NQUccUD(3ErsJ8#>P]$ICH+1;aH%_?Y/E'[eRrD5.KlA>uqA)dS#MmOC5cN]Rhg"l6/\.QF-O*68-YNaU:)UK*WCC5l+s2O2RI,3U7"6I;j[d%-Gjl;]-'k`',WLZr2`6j(9"(pQV*Ohr+9M+6qH&Ool]#Dp=siu%_##htn=%-YTARY:Y&,]nMXULtc[!J`!m+qR0#^EAg3E<=(Dh$L^ZEI-bnYX)R\&U`G],YhV1&J:YG*X$60H3!hflXB3b00[i*_Wd'L>\Djd`IidcHsi>3hQWa'MP;g8Rf*`t1F+5(7iLA%"KpCsL>>r35S'P2JfC"o248=8<M$&'M9n*dQr.<mh$l5bH"5mZ<64U>MItN[F`P]Fd1)*eU-T&DAIS)</tS0Z_/K@"<%#O0=p9QrpR!fNJH6R!YMCFS>hE9.l9/q;Cjf7kmDQrV(`jS]-kPOp!rF0Whk$3P"<LquSZ'<9644RVdc;ooN@aKMfQlfip-t=QCmOt^+1m-Pm*-J.pk*H[$pK_7JcBq"rM/5j<4N!AOXIs;QVpginK(YV@_A,uK_HH=nr7B'$!Y)ji-m&j'Drfj:t3;jc\0E0+O#@9`+79hs37A*'B*QMmYO(,l'`\)]Tm3^j_-`thI9NoH&bjT<k84ZW_D$&mi5(G#sU(KVYdi_28.*ia1ZC&Sqq[q_F@4KV_.9FkJWi8TdRK+%EMFSU2T79_"Q31C_V1^[VfX)*l#KPCq%8&JZTZ/%_@c$qE`^?K.uj*1*N&u?Z#[@jpaFG^;8Qu9]8:g%k(tRb/slOB>.7^\Bct`EJ:l(5O.s`F&4:S=0ZJR\m<7RYP\SGCn(SEpENG5$Y7c_W_;F`2N2tQb&(1(FU=hhkn6HJ\`>e%/7SjeD*s\%Ab-T#Z2$0o\$!n#KQrAPKjM[-Ken*="m_i<$@E93lCX%T@tK-JrCs,'90.7$?^H%=W_89I>2@=Z#LKBD*,m:XdfHCHg/\po<*lo(6,0<lK&X_4Dr)FJ$LNsQ#VKaW28RN<#?%*f9h"(+&8\AjnH:QP$QlS+Z7Qb]>0I9OJN?-#!F,>Fhu4d??1#X\#W"][^Ms5m`h%^gXJ-B]d)V>mV0/qOksRB#ok81lfVa4fZD%.pksiN"2qQ1fFsiR8$#bX'Q!E0X.UTeD:[7!!qQ\W5b5H5Kol,=n8Eq+iD;FD"qeG'gXKoEI]&cOcCWJo%(R@p<71;sOB4]n"-F[8uE`k"8J[5_*cOl_]4]&s;D1Zg\%550HH`;I/AI/Z+j=[(:aIVRJU$PkUpZ$t,DK^0,NXRdMX]=ig0,FXV?Ujn+kVueqb0c!,U[NQ'2>0@Ch"O-Nlil;>^!I[E!P5u]>!oLPenqU)NP,mAqd*rq^#6$,(Kt<%m-;;$>%95J6SGg$hKU_+hHD61_o1-/E%if4=>_#)[:ukdg&mB41)<^Q#Lb^rGj(QUZ!V]f*\EC,+l($k(B2)BBG,Rr67/I4G3R8&5^XR1n3m)7&\mEt_?l9?q%8dq$m$Y)f3`+99HaG;ADWXnot`\!;e:/ll(L9l9HjZ#Y^t&_InN;a1*jLpouIrj!.ANhTr,FY%9q3XPi)dGDt9taJI%!CA*SXQ':&,8gZ3;q=R+pg0p5r<i*mtN6TOF>0p4qQ/^6+Wh]H)sj7fTm]Bca![C0$"OhUb5nGcU1Nl9mj#<ljAme#2(][;Edg;IWPDUdrcZOnC/OX64fS."\Fr21<e7b+(In^&(pZ<nlWqRaT*!/`r$emhk4N@K>lc@Z[8UrU[6n?B-VjYhmKLUd/r%D:E+N&r&83;M4F([]WJCZB"ii\O^JEY.=/6$!?Q#I1Vq$5iOO?S'8ELfRY'4"bKSlA_g?Mt%IpITms'h3O&0@.N4#6`mB>8a1M1T%7&8)W[79!DcKWSHGlN.dE;!9`Tag*0Ol([%E%_1?UZI#d.K)VK6_L7Q9#Fg)17E/sp$("DE_d1XJd/DMg^&>G[XTe;^^DDJ=ckT1%<NbG6WZ\2:s<2*WX_hMlbGW?<$BP#X:+Yb$o4-JT%P\Jm`kDV3%dYUt'/*`@D>$(m^70sArQ/8"YORr-K9^DV>L]7;O5%7lQTFT~>endstream
endobj
57 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3055
>>
stream
Gb"/)=d.Su&q6H[J]C?*9G7D%=7*o,4d5HV=(^O[5,_)-+X%^X8^KRHfrb5CgAlIh)@7>$fXGuQ(=FK*C\3>K7PODl+0>Lf8qd2P5VI%1_?;IA#R=[d(@TVpio8^HkD)Vq!p!dA_^Iqp6-\PdK(%^KpQ?rA-qm!h`*n<cm.l<(n-2DK+"oaHpbX;<$W=At?6#cTj`<P5(TBVR]MI'"j<a)\6,ZksQqRP_'b>X_;ib<!T`k7!lmM:3M(V$+]3\b"I_:#WNS`H`qs,Q)";^HE4tNkorI>ZuTuE&p+2pD66H).naQ*2HiXnC/r85Ss06u)>FOPCBl]sBG)j*_!,U>j_A[N:6Y;3qOVdRQS6DG)M^Uo\:H.+B[5U@VGDqG_qKP'nfPt4jpD^l&c"A)!/[fDI.d@\rb;_La05BrLeVO+4,6$(pp5*?%TS>M-`59*TgorduUE2%Qe[@psC1WL#9i^G\!5SE@93/tbJNSjTJbenbmn/DP9+-#:?LnZ$ad0m1VOW'`s3T0Co`oaO0fUgR6f@).)5ZBCr!=6RrT[IfU^^gZ4I#),BlodMJDNauA[t(!h%Goifhq+<fNJ;kP(<D^S7b,BY*!E/S*bhU0@UTh4rP5Z>%&W^W,bQ!E1J*"b7K`&a&YVJ/A-mk+&=;?,a^s9m'I*/"U1-1#<XmAcPIEW&ZL'+!>9JT]Fqg-ld,;oMTV^9C#MhKGpGI37]+li-?Zelt0\lcJ9)/GiHh:aue:cgF+>n,#M3JS\E_I^`jX'W(aRY+]LdWCqIm=DEKo*H5`A)0d2I#!-`05fCZq%3+J%GGpF*G=-H1MB2XeHU[.mt\heXH)<qe19Na9pUn!chZCXV\N9q367Z+XaO`pDpSrSY1(dq2k$(%^mp-gihnem9%uL840@P,4iX5</*"iI'B&XPj>r_fC;(c4,G^qP+H;@HH]bSYB!KWd!nm:7';$.%O)!UnrYkVcXE))[.'gJl?U;!I?jEZ@[\]fA/_lWl.CkXWoJ+'ip@V?7R"HnB+sVPX7O:-\,,ajB<.:e&\lL]4a=-[8l=Th9)#t=:@`=2&D;l!W+:r+b2bq-.X2X`Ma;s5;$msO6q^CUo&Y,A<;N-t^I,bQ$!>'-/sVHLfTD1Y/87d#BgOH3.LNuO=UV2YkA/Wbh(+/\q1uuWFhHEbZELBfSmhYcNXRe+)^U"5r:Z-D@CBnHRa$-_SbFE[H!AFRW/G1jRW5.mS/8iuq23kOjiBks(U1f;Q$)h>X1gr&GseaH(M8/olEC&Y5_"J5^(F349>im]GY#l)LOXfc:u.G?bB95LW^%fTh+nh.7DDWPD5jR%[49P:N=%Q15_[`ZKoBl)YS_(NXr8307N)u?L'EQu"pLs!_.'59"3K./d)YTR!C)*Q0f=RNdX4EM"<Y+Xs0VlQ]$4L]IbFHA5;ZYSR*=C,i>8/bS(j->Qe7t(d8b&^(jnoK>p7Opi-4I%U:b@4Qu>bY#jL?]a;dO#BWkFR"RWf/p+U*5qnAB@ke_Okbo8&NM5oL]*LqPf(AZG>n$q4!Hr09A=,;9S[jsSh#RAc0"bd2..N7\3N$k'o!*!i>`8hIo31\@T7tZ3jG52aM4FSObD"<_Xbl&0c0>K2)Iin)X`uIN+q(PCcP7RQ/`\.\E1e4jlmF_LL*sU5>j!;J"!(s\?^PSTC7p>EFf\Ss`>X5s`r$Z><M>8s/5<cHeH"<qDe]"\nU;^Ve&i0UYkV,%)<!ts`!!_HaR;@ibgA[ej)Gt]F<gJ%iE.rMl[\1=:DjW_5)0EQjDgRbd)Bf\loGt-&V;TmmFX"sm5Mj'TCu2,!lJq%.G\*iV'1>dc!4a`7$"^`bq#i[_Kat(MFi0JC8]4KHRLQ_VR)\B9nBcJ&^c+ffVQiT"-ARKPAbSX<2k^(pBa,e8gV*m$(ThLi\dtbbh65C7\KF+F""/?&!u)AJ<qL.\PJ`7&99c\$MLT1\].'7@nLL:,Ku'5_4lO[:9+o\3_="erUp`Ni`'m([1@GTIeR-<u*!g^D3IuXoU_*ba#a\f'[B,Uh*k>qre6UDB8T)Ik/M$$V$B@"RK1:PPWqKp?Dd1sn5^aI%U6;'LMi=X1?j.!SJ,FQ?H?TF:/&3ZDO!D.pq4\G5T^7S3]Z=3_Et*4Vo1fG!.@DTecp,7KY._"(.s1gjhE's.>pLYY&6GTP'\s'[GD#Zc<*sc8_>6p@:cV'*_)6#X,U-18=QE<D-IJ4fl1!B1OC*8:Oj=!*75G`B5Uf)-b!:#jFkO7PQ@&s\&ULc83qeiB)@dq;g1G_`/*'osI5,"Kpj-*5QD6t6lelISMJ>#3;`?W.;o4M:f6&;[PtRd=(D9HO&2r?S(W#hge<G$\b#J4IfdX$b_:5HMF]'`=CP]&*rYPucV)sh"dnlH<TsaI%Z)tc-V7IH2A8]Tg3,t&p<6l_n'D]ZXe8'3]+5fJ'oT33#lg;0]'0,B!9(+`_P(E%pp$"lr$Rh%s)NIoZpK4]#)j,\):<?"c^S,t.F-5Y:nM^q_`1X&4BTB*OY>09JD@ds&*jsT+0EO0ZW=FfOWT8,pbJj*I[lA2_Tg/c'@,MaaW^kY@o,44)WMcGVot&jAZ["R0Q)R8Vea-YK5EV,a@,CFB[?1@3jZF@E,3IQEiC&8nHEJ)Od^e'e_\Ln?XL>&lERCsT6`B.<X4Ds#?275,mq8OsY!7kL62`BFm>C#%W3#iVcTDSO<Dge0J0/DJWdS5*hJFPa]Hec>`((r72'iDY;RRX/[(P]I/"RRi\U]>(E,3%eXmu4h5*Y4jOaT:O6Ai_u?LfUGkh(Zs=Q]Im.XBS!;NH_QK\[#,KI7Cm$`)MX#usgi^u6ZpQ\EZAa6r1E*;qH'=.ATg?[;H7+ee*8qXg32h>A?+hfV5_mNI=31K[A<Bm@:e;QGq[mMH@4XX7d:E_rfn8k';*2-n;"j)(Ak:"4.CTN2_kNZ?P`UA>N.J**DYqr@Y0bX'MG#:ch;CIU'b1X<Ug7^g^gQ&_KcjOEjr]'U@L(?)s-_O7]X,redc@^X.i@\W4nfR%P&li/k*DF5B\D5lDqK+s*0Wd[I<\KaKYrrj\E4UV~>endstream
endobj
58 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 796
>>
stream
Gatm8gJ6Kg&:O:Sb[ZO[AS65#QcBf'8emQlDP&+Q-?bK50@Y1'pUe&OO?a4+D6.8<j-auNbfg9Bg'@TF\cDL>_=8-=)+5E/5m0SgY_[m<!MQ4<9E7A5Teo"Oe@._Xo9>q:98O4KO>%DA#a%s'V2\#SEtf28PT'fp(hWhG_U)B4EQF@3UMN&:+!B;!oHbtgTcOTK?L5':6PqQb)C\N_gV<l&hp1B3I!K^S5)7Sj>gHKA1M`L6^.._#%hRP_FO]>1F@efSY:Scl5%TCEdG^]ulYVcMD$>ipXlFTD6bL[p's&6NE)Msj/4n$-(?[?mVPS5L>4MJ^I\_+midt7I#jpOfART5"D^riN2Qb0F2UPQg[FGcGoFU7cqmdkJLK+3!oZ#fDh0/4Y`lGG/ar'Ns%i&a4X7ki*Ga1@B0(!6\M2DnePm1q^$.105d')l>j<-br+5'Jph0ZVX]eJ9U[:f6"HC?2b8GsQ;iscmkE/>V0#e2,*,!/*294e8H!'5X5']5^3]VfRI)iAj50!d=YZQr;o8S$H2G9C8J1hln"gI5(j)(4s^Rp%`WAgeS0+M?S:6'W5`"eY#RYikTeT&&PZ9q;57X#=1[c)Z9h07<AHXJm+/@^sbE2VqDU0g-Q/6hPZ!-kESS<!=OM;e/T9kOp)IfgD'AXa+:("8cohCi.A#c42`1MuDK9mLRiT4^SWO;]mOr=.]T3iGhuh_QUI,Ek%_5dG@=*a567)iV%ABC[@ndH)PqGBKc!o`")_t@b)27J:'0KYVm2$S3'4ga3Zp(ffLkc"2SoMig'9~>endstream
endobj
59 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2779
>>
stream
Gb!l!>B?Su(4Q"]d%`O"+hE2S8+4C&Q*&/?0"lP!W\,1=D)-W&#jceOJ,C?Z5W@L.DEO`;47UtPC8#^/cDU+Nh&m!@5@XXg$AADm69i+/LetJ4+Q@`3>.m;4X/k-]4<-a$ooV`m$$_CkMQ6&D$$'I5]ng`]JiN75(iU0#%o^KAKg_F!Xum>*(/s;iYpgeKoOC!,:-(CQIR7_obRa7g,L^fj1d-fo%RMa]I>MHp'$='>YSZ:))A_Q5rO\QLc.cShrd9?I]LAH14u5[%nFQ%^<]BLHL/u'_eT\,:URca_d3!t^EW)8N'`1Z*O"+iN=k3r9lFIW$B,6SGQ72BndLa)hjX*W3R:IL+2)ak0irM,(o]0(Vl-IRd$;h<qob>ljQ2[m#b0!mgV#h4%-X!P]%#tiBhS[ZKO%06E0,hO<EA%gCIF[i7aS3P2NXYkWa=+B$[shL6%ikEGbj5I"l>[S/-tL7(8/q])i@232pP'!_e>>C68KS!B\o))lR"QF`er'VOEThZVW3N#eR7,CnoK@R]7kZd:Ol%&o`Uj-9N2UaPBT0#ZC_U1h3q2#Mn5&iVm0\pbBb19m@H93GrT4qhki4n1^pV4co7cEmI-0Wk48-)e%k@(_Kme+moV&bKdF>%Sd4@"k.WA-[1Rd5:0\:Pl>;h1)'!8I1dZ2Kmj[N8^8(<2$KM\qsAM$ZI5OJrrJW%j9;D7B^!:5'>5ac.mBc1CnEs%S$@(6Y:SaQt8BM"21GL,L9-91brWRZbdf!=*jd'FD[=l9J>Yf8<=&.8L>;D8a(B_@1aImH-+Q\@](ggm6Ld_bT^l$?RU:ESGeSYu[k<=3-MD^#Mj>G;Js+E@XJO4$]LQ)p8Ecqdj>8H9_&`1mA'JUb\h(,S=sY\@TOAZ83H1;C]iL,;fF&-NM!8#\h!U?kdHQ3B&>V;,esI=R8EEY-gK1Ta(PNH[-"R_@:?Y/)RU%"XJEKdih8p3Zt^%`"\KjNg;g%2+O8"?=(G^^nGO]9mH+4!c7bNm>@u'VGhg%R@i)/n@cAdR=NeDG!*K(;T^/A'(mabHp:#d,6`C$ZFOl:d<kk6^RST:B]SmlJ3_I:GM>-KqP>'5i:%.XrsTcqmjd1`,i8#"R%r%^'SL-DBT1",8ZdrfQNAo_F3i:O]ZjN/a>g/jb&uJf4$8RoJH%/o+b])iIQ/iFMfn2pqp>(-9SUc$7N.'jD!MIa3Y?Uhrrcs8R98V5,2n(^JDpN;WQM\7KrV(&@bO-E$OpW201#ULjYVORL-QS\P^C5)bm2ZKr>V]NDEa<q&#D&Nu_R7j:b`WS>8j/n[-h*E=W:p`rQ4)'JLuMKX*.LP\&7CUhh?@VQ+$X.G+7A:<mH%M9jRl1eFD,7k)46:ZpCb+a03c:p<c-IK'AZ-Rl'*0n[]79[`-.'l2:I<'9EKU+V@-$F8kulLmp^WQWSj\M/p:2iqci=RZ2+M0<?M[=e_+80470DQ\G,A>W[M+q*oeI"5qKnf]-ZOq.eC9`C0q?(ItcAJpKTF1dm/h:@;u?uQ#YB,9fIPKWP9Cq:K#hhaArri_5RCRNJ9Pj<[!M&e.If?SqWl0%Wm]eW7SV_q:4q=7Ou"u\Jj6K'q2pIu-Ch,^+Z3>R&G=Ut.E_B/]XJ`!J"?cM;t.nVKAZZa66+e_f5G>b&+_65*&I1p.)G8G/*K8`Z=Il\;e_`Z.L\aMq]BC7:)]7eeUT`+$Qs-Z&g`<:6l,*lLiSKH:rZ=h1koNkOlb"PUslLcKgDh/J!XCYCBV+fL19]euC/e`#U)?tBY)VPIR8rh+e'7sdogrX<>==S&G>=cXu.[fb[SPm!4jf>-PkQ.@FoBGjHT14=ho@5e"1q.^4=_i'o^&m#sa54;?2q`i)@D+9N'$)-4?@M>-;u\E8h:5M9O(J.d5\fAgNkn''&+E[n@K;sGbe(KsQq*YbnsPCfEdXWN,CUmlcpp*fWK6k=qJ\8e+,Q!u)X%9FB'NmH%Lr*ij6f.,00Jr@TIuTn)TU^YQrFo:aj^"B4Pccb$"P@M@+",M;0c*#Y9c`X?DRPOp-S&XRAB+!<ThZVqWNpl]^%D(GXZ?j\_^>$PN>\h'r9n^LR,3CKn/iD$?Iot0<->bojB^3c&;h`GH.ZOj4@u=c#*0P+Q,%o'W3)c8YR-ecfCh96Eu@&9dSoX9`mn07KMTrccS&q<!U/7A:ZqS&^]C$XA#Nt?H1cWL[.>kWX?sgpJkfaDGRV4<LY>]iL=_.Z?jU!3d4*B,=@&/=Q,!'HYs?T%F-.5Dp6K?SG]?#D<WCN"ao*kM1C,eLWuQmXR*TGG2q4@5b`Kp#eFM)SZ$0F@i<1%cjq@1b#c;F?918n\nKlc0ti3fWT4"RA?#O+C?Ptge:XjN`afC">`_VM]TSb]9]^N7![qlA%&KFTSQ)#:`QJ6a`9\NVO[(2%,cU+;6UE`c1pBLjU*BWiA^rUcA;M9a"NA)@p+U_km0kP((q/%Bi2F2`S+&1:,l?[)S:_:7<fY4lEGZS/'3!hc=[-"4na(0#G2c(NQY$1c/>"2=T%XiAZR+$qHar>n(:@5B<93c'VWm4015er-h4'u_f9J&Hj$k9#6u"GLbI*XYD-k6Q@(6TT#*5[1?,CQPD=7q5;c16pqM99\jIi`]RiYMnc8j+OeCC!c?d!FjbP,WnB#iTU6E.0-;-/\MfC*<1l"WqlY73O.VUX5'cC-3T[CEPT8mQ;d)%62IK$I>7i)f@_+.i$+h'l/"[\haA\%k8nI,fkR#Pp@Hktog'PA$",mFr&6r[#UAKRlS-I!,e*_]Epm5H1is.0~>endstream
endobj
60 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3061
>>
stream
Gb!Smfl#S)(4GpYn?LE4.&(kgP6'HV-V2/.m-4cZ.%ii+k_bOd<70]9qWm<L;PGG7(0<us1>0lJjQ5@6k:S#?ZgjNcJ!5hrhra;$/'bZBk')c2QH/,hluIRKous`?-uqF^EJOEWbUeO7la%T&_Uu;k+Q1J)IEMrqJGJ!+*B1b>amOU<ZZp#mZ,8+GV?X@n0=DBbkM<e$IYAX-dP#3$1oH$E'#M]i6n`KQ'$$<USUa/a[R*'m^l6N%>b03e?bO4'Hd7)TA2iZZf6,GbHhhQ.IeAB=5I:/=XW3KF=?ah;Z/gB0[UV>+Vrdqb,Y]WW\mUk)?&EseqL"h\"^<r]a=`AJ/mZR]8]Bi4`V+5h%=Du@]T^*@=Y3n)/XU&$>.e?,^2EmP'eIUF=lomP#UF+i$l7W:7(ZO8)d>VhgYS^,B*Ojid+j+^4t#p&0TIBP*tMYt<P-iZjD($6%=J@8]8<4k"`>0^d"_4<*.lQ4C9+Q:_VA2AFptXdE<.-E.9=[1!-YbZ5DT@@O.eD^n$^Fb?t@UQ6gsWo.g4(3(YGe:JKAXid*>3lU]fBsE5p_]p9au$^u*IN:itpr4qo#,T$)[HYgHq3YKpU@D]SV3i8a!Th6a%\4nbhlmRO3o_atrp\fFFDo[k*.7[nF8]dj>hV/:do[&mD(%T8&i0O@fHqSS$Lo+a;kplSr7akk,srAR:UQ5`lNIM7!)r1K7/(qK$LY79L<$4T)4a_se$'S7$%A"1L>l-A<s!U6acM$mcBWnTuN]B">45lQ#o5"1`ql&eAqDiCaaPEB>qE\'^(@$]PnNCPmKTgq"fc`4@PAL:MX#q!F=19],Q6#fCMV#o(\.qj)b;X&t2Wn4)[q09kr/hd4:IP_!?,2oR]mk1fuR0LNeD+,m7Q.cSBA_Z#3Htpt=a2GE$2PO`OTmcLg5"UlR2tB$qZ8H1SF]pOt(Km3&/$Y6QRhmSPYr7[iT0t$is7bc&ficjA`N?)7<Gn#>D".X_B6Rp<rT5VGLEeo8L*$h`I6o@Oh3K.VG(%Qo'FS:h-hMe(6@f6*I`@r!7=kOX!q"bpqB+te)39Yb\5I#8#_!t0GTD=MB:a%JB7YPI0L?TEf&4\f5rgOOnoXg)_0&u]5(D[;<71>\XU"r3PN*X[dBk.AMXkPHf!90cZ$F1*:B$?0+LFh2>%GPm"u^:/RAEp6%UTtGe,-i<#INIb0in`9$sAm*Q)4<9a]"L@'U[TR%GJHg415m#`SOQbQYI91:P"6;^e;JKZIgR3IVg@BD+ZKG"hZREg%EmTKu>JVCa@hRO]UO#h.W\SND+[DPTOj3g>)inpZGY,2#XeOG5iq[7bTteDdB6u>aM3I1;u\=9$=CMJZ<^IG^N!M;8_bKG?7R.!I[Eq4-8GmE?9^dE^lsJ7S_-ocUa_o1]Ffd;N-^sODe(._&q5G[.\a]8=nSXF/-dt6Xk<p@3j1hSJ1,$+?K9W]B&K\jmc+&DL;_=-])mdWoqciJr(&6XDJO8-DiE>pm@D(3;YKFETAL#^K2TB:Ahe,ZHI_-pA<T:QM7*afno6t]k_%&]5rb@jiVgbDd:I8V9b<&MHVWr9q&!%&r?^E_IBrZjsXpZ:C\l%2&4mk!MHs[J`$S1S`thW9@W2A$;b\<\_O*.JgtDp4&"$Nl:,S<i"tcsST>9Ea,U#G+N)$>5X*6A1@S+3'e`sq*r`>I>`1"ofe(L,9P1BJ7/n"`==&u+'nEthF=`Rn74RY/27F;3DsOdOQ`/oL,op!NaUPhiCRM>c&RZMrkCa;`7[<+fWY?egPX&&mLp\_6H^cC)LbG`L@#LG-BiR$&<U;#d+%33smnR?l^B@OPX;9o$ecso=VJ5q?re*%=MdsVFPXFs,Zms5;'2sY0DOPE>esKgGMMgARX&r`rYGRGkVf=U/?MXEu%CR4<PKW8eK<kHEY+@D%"8PZN4&1ZiJA,ln?s9h.RUSet`>5KF2T=C#KnGBY,k"$Yl6$li!nq[G:0W8"eXQbg%m@L&U-B9sE'eFPJ\r#BYj$\tYoE_]Cp8[:36B@gL`\c;>2Al\3Qd%i5[>d7UC\SORbtTC(We&QGL$F4FmG%2N\b&]GZVjf7JIl+I+mndK0)K1m!<Bg+lbV2''&7^Ig7:"CX?WAPG2;6mtu-g8LV%Q!Oc0tFj.MI^et>$;VWHF;G%Ygl@T\A[MgX$!Tdk#/a-s$Te.ApFQ^hZ]0X,P,lUSmpoPqX_;OVu\U%DF-jsSs;64?C&t/fVm9MKB3h\ZD@ii1EAGW/UQ?s'8$/Y`"9homn#L,@CZMF0_f<6j2gPY5=2AFae?POf)g9Pg[cSEp.]E]D1Ujo.^d*2Dea7ek*Ld.Qf/6+ZZ@;l6a;jJ$sBj!>5A5^S=#u8>:gc)%7[O"BO#)jFo59F@SUu"mM8l^(\/\V4Y`2TW6QhSe("K[M718i(*('!,)i`MkG#i^PM#)X+[oPk)WAOV@S_2p^m0Xt$EgqqBm'k,+(%.0*OFG_4=LH.+!jK93HqNBaFC"X_V$J=WQCQP*U$:-t`D-;BHQ!gcn.oI49gAY=lZ"Z<s-[/F2bTl\U;WW+;:oS)8DO1F50C,uMY)XM*Va8pGGuGH%&bN4latM9#ddRfJ;.'B?V?0:[([qY=*hQR2mlW5;CK",h[=e7B%]:X5*AjU:,)h.sg*"mACaW/)?+l$7[Y@l2+19*CF&%AW&thkXEL<i8O@e.U_p`\F",d5MGfmtf#"L`8!gH;-L%k(jMne;?7mk]q%'W+F3]E3>orrqE"_TJ+E0RcqlQ)`Vr=?N7j(MU;]!:3-?J(WD!Cc4HW?5'B9ljSb2X3bdr,/(R6TZ2jPif9rktj]$HG,ct"B@?>nY/WN2KZEn+1g2NZVGSJYQi+MPZ2kWVLGIun!?#0'.<u)Fg+Q\Rq\5Sm"q=@X[VqJqsJsF[+tB.QS+mdFWX5Xm\,tbQut'i9Wo?"%`ZraZ0nHSl1LUM(G4+Z]ri0RY*YW_Hn^kUZ$\5@n9$)s#+'luI_2I0NQAbQdoCe-?*qW?g3o$R3$)]m#;t(i0KgWmc9>M5&%^nZFbGX$p,dDBq_lasrTf4SG4m!o+QDb~>endstream
endobj
61 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1620
>>
stream
Gb!Sk>Ar7S'Roe[367:f1^l%3/d:t,[Ea:G?.Qs[P6$6"BMX=.3hi-RrUi8%bu_GiQ3"l9,U+Z-SF9^t,6ej3=oCS,!IgWVeMRHK@C?0u&J&J40V+1E7YJo<j;nlPRIh^H*6RBA^jA@88a-$[A0_HlU^rV#b(Ym(1b;so#RdVf3"R``$Qt!:d50BWGL(It!M1+q#>"(HZk*=p0^Ji"/o,<\,/P5l+_6f?1rP#,N.8U1bU1rNG981$IL>h5\I]_s+/Zft-9&,9p'#'3><,Hi:Sa0rTZU^=PS6g*7fIauCiWf\#,\gY<AbSU/[NC2SFZa;R:;;T\k"Ta3`\.TMXVo1^Mh4A?.ujYKE4h],G%gdS(/V35ZUjq/.=QOO^sa1;)fO`\okcDL`]7S4Xk^UiVklamN7.DLE6br.!4^o!l7<BF^lf&XkYYp,A(P31#Ttd5mY2h7'\5VKKbm7N&'lJKhL8G`l*)CfB/pe]G4jf:ZR9pU**=kHND#3$5TrA7!N[Bf!r.81KkS0mn9Y_*^V7nYW[LWJ"U?8JZERH#FtkW"H9NC"_L%sB>U6-G$Vt\G`M,pGhAYqlJ%t>_4Ci,p9W<2U[!Ct;IZ?@.SJ9a.d,A@PWYZk_@.)p6?tBn4`R9dq2K:ClE396:.hW<*S\hbD9=?:g=jaboC4lEFJ4RAc8l6koF9_r[>ORWfMpSM?_-lZbM[_KAdTNX`\Wn*&LX`LgJ9]*"Q((nIC.%.$<j045.4):=JZ9>9X4H[$sja[QbbbQh7NoGp#n2,U%X_U"?rebQ7=<-9#FG/W'.pPEG!UZiUT`u=VYqV<uOH><`Ol+fnHqA```//=9^G;)JmgS!co9t$'#;\\_NDRC0[cl9b(s(3'b(L&-c"pTH>20fJhGL&lZC$i/TX5,;Fo4'ltuF?#X/hJ3.&)Z;C&D?l2nkE(fc"atX>;[,"c?``Ad6fIfeWeXPf>$JHA!HeY+lqCDPn)*,R;WM'O\>,fOZ2VboO7RY&DY5dX,8&`FSmK`Kjh_ohkkk8]%7I(t)Up#3eDdKs(T$[Ef>4;)U>)e;=)\#;f%[?Qcq$EJH^&2uA)&BWll^n&@_&@mmfku7$b]t\-Ba-#gB?em1GdKi=:F8#%VG0OOSKmX$J4mnS`go1?liVGLgnrcBOb"[iDtV_G!*04k!&tO7Xru"$QfBVU"?5/>ba(nl]O6>./pD.M8]r)dpoo7Y4RZVpMB2`b6P<[emeUA%`/frhq&]DC$^uXhNJC;E)D4uPTP2A9pH<dFeZQ2r$@/9[I<@.9%aMWWgDB`"YfhGI<B\t_S..6<n.o3ZK0mo)/W28;Uhhtaa*".Nj$d\QNmkJn.C3ei.bCDP'WH/QMc@Z4O!V54e]'qi6(%>MM[m2++AgPWTc1k9D`Or1J$DJB=g>U>%?GDFDa"7)8?m)c>0hmUn@>B=1/'r]/r8G8P7'q&M-`=i(CF*k/D<dU^=7<VbP#YtLSK,2GIK%q"<3i)98^6)hhcm+Ub4e'PSt?8]uP1??:dtN;iR%0d2r`DPsaNKRHM="c7P^F@,.oc9=(StX/MLmrge#F:`.HgM@mZj\CE%5Bq9?\"kp,%i]]%3mRt4[!^2#1^&c^8FVmI_r=(!;-[b~>endstream
endobj
62 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2894
>>
stream
Gb!l!D,]1[')p1[kV&nZ.F?A0hY?49aP@TtP/&DOW1jKACm<D,=-<m!^](fW,[!%6$/*QEhBNal*bi3`+8%bFcX74ks)IotaT:7)_,$sK\:CUdE9/OY3eNWs^#/NGk!tP1i,QU3+9kV,2;Kd%>So7M$3C]dm!BrK"7WkAKFI>V"-YZj*fl\I"aDMo#'ci!fp:U0)]-jsLsJ`j!AC0%m1_pV@FS'aH[-Q'*.*9q!$]_$O@AqP%f7&TUT;f,0-AM1E-q_RUMD:^2U5*I^)EQUL8utHoqo,aecX5Gq]WQ+CnD#00]hXCQcV?K78q&ph,LtrQ9Lq<8I=!8K^c,q-L'.s8ZK,;c8LG%"!</&NoZD'l>*A0$,U2t]O8U#X^CB,d\8Hr;'LfUPI)bD%QD-j[X_`)?4cp+IFHs:+7=n/Ee!<GM+Do#*(Q6B4Hr1dW*PqA7QCb<"(l.(0GIh9_[&TN4eKb<-8aRjp?odPB/[.dT@ge`!YVDKa*9<L"+7@[lj$C<1QN,*l$"lR#U]q6G]A:`Z\nf_lX1s7"!AEd$Cr_;efZ2iqck_lmh#71rr99Z8;G(V&a:uIP2j1WL+W2qILegCg.ac/l/OXRbP'cH*k'S7(/NS0_a,Aj^Km:3qUik%90rA+."PMo;.8Tb<5V_2@;Ok$f;>FbZ)LH;?Y9&3?!iYE?_%U<^0@5'&74#P!G:4j$P\__:!8po3$=:sa4F$.Fg4S56(?B*%sO$3J/7=b^9I=HPAR`YJmLVJ?i$AO^"1i@%(?e@a$BL2+MP_l0,XI^e3M)?=\X%$riX9fQlLREl_Z.?@Y$:%f$Ul1,NODIV5\t$bU\V9XY,bX5Xq>aaTu-eOnFT-N6ueXfDCOVFOASC0WW>fRMfr+W@>j`!%'/O8k`ER#>gXZ3^J8:8)Z=gQ)Uro_\`6hBgVMscYr,=>NV\Ob`_l%1nS?6qXmrP#/bs`UeT[hFloDN1mj&dWD`X>a:O&Wo,>peQb'iK7;+IDX]ZC>020@`$X6+@8e)NseZG7:"Z6'61Uteq#*LO.@?.kJTTl\FTiBr!+XgEVD`K)]A>3Z=M7j63drX$udQQ=q&j3OekaLE8\n7KFqCicI/1!QI1[&,P(M;iq^`@fO"3'+IHs)NmrX:F?Oa7'5[o=b,eqN:2NHcaC>RqMp?78@WoJ'g%n[p`AJ?ua;&<C%e_8XEU5`orK,J7nmP'.b!G^Uo+F%,h>i1)tp?G7G,[3d/e3+O)j2P3c8QRBS8Er8cu,!g^6O8\ZG(LZ0bcI-^EhQR4:R.M#ZNUrb<(8U-GBnOTHVr>pr+_K]C/-G\nm4qe'WG'aO8\8-*B>4fW+DA*#h%$_QF>Nq\IWX',Me*)'?$S`TdPimARU#?mX-tVbQYG9C>XL^$Re1QrB-r8Q*9jLTLtIOK(>RWmX_Z_Q-GRMT%gMZ.escsUJPpksJK!3i`U.\;8HHF-/bcQNHI=a&:a"FMR$7::q(UE7L>AKjFAn64Z`X"1jg(ZV]YgkVVN%0j4e8CUM:NH8Hc3oWP'L);hGfkNo0sgFO!$-d1Z-m1;AVAIE%s^_A8,J\!>YLDMUP!o(<r@aN[f/XUp3$"PU;%d&m?fWY^ga/c&K93goEU0J0i^i,W48"lWO\8Y!R6aQYpb85h.8ZNtA>G8f*/E#)tK?::D.Cl<mXnJ.cl`2:[8CQ.'KQot[NWRVKg"o\oZ>2S`;)X+6bS^Gn]5WcZgV)#GStqDTi2/Bo,8QXa/\+/?iYOH>9_lM1iSCMkQHOQj7:&QY\h$HlZ&BUBWIQa'b/S5GE!K!+:1)X1J(>N;'^)ZH-W`b@.r2P(g2Prqd@1,HKkW7U#pb9fUI9oF,Tb\'ALD\f+>lkDPTkI-8B<i=?UN4N?+ORaC!AD><+^)$fiS_30&:n5/3+R],+d.UnClP$Sk'6<>bJ@aCUBu&1Pd4*$*Sh<j7+s2pc;I89k)2\4F$_rNr;F!Pk!5JCN[Lt(S726+cL%::_<bhlpLN]asTekHX:\HCG?JVIPks_jTS\n3D/[SpOMCfgY!=LR+hKg:3=kOZ7GY+RWOX(5.cnsuS(X`XccGuqt;Qd84]lc_@a^6mVak#$O5I1$o*k`$Z>DZagrY@Wm1&AKuW(a>YD\fl_@?K$r//m_j(;XC?Zh.>`n'HR3qR\m5<OcWQ,u;,4^6bP.`=LARa6CNAp'?.F!klcn$')FA?Qd9%mBP(5$?=9;-cACPH\D/tIECLc[TtO51u,91l9o8eOj'c\`A\Q'HUc^hZt!1Vro:n)HiWV*a+rUS]KQ8WokTrH\C6Z2_QkLYiAA9bbrniA.fHFOlX%S[U@VNE$6uB.>$sYcKO!D?3I7dm]A$4bA#aq#I@8=rb5nm5jX6<Zdu's>K[>7N/>d-6`H(K:=h)'NoUJfp9QGL\`"==b-V=uU]6R_WQtg_S-FuB+d88/3QMs&j'b]k6R8R(7*c9M0].6?8RZ.5R&P,<eVUHPb;PcJ-BM:V-RrjBn*_9'9cp?K:EI@WWPad:.15!:%W'(oLJ#Bdmis(I52fB1f`b2lcc/:61oTu%/P$i"[gs#D?b'?p(c_'#.TdL@,?.;%-/;^SUOL/0eXUM8BK15b5'Vsn/fb(f$dbguu2'!DQ5]Sfe$IH7CIB].$^\TCWA(>^04@>G6h[*-/0e3<iL!'N39Aui"&tQ$>$;`623W[ro>tCp>rlZ%LbR%,BilaT;1:8YuQfIiC!d(-j`2Wh)I',tXK)ikt>6QDcj)5O&s%DJH$jk$^E#A5Uko2K.+R(C7aJ^:^r_*%hY5BFEntVn9);\Aj:,[Kdjj+a&1$sU@RqhkpS13+uAP05GQl&NXZDpX&!Jf?uCItTHeh?dCYiD,=.n_dOnIdJE;nPLob9L<hk:>d=Y0p%W_DXb+":a_r(R!6]3W~>endstream
endobj
63 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1892
>>
stream
Gb!SlgMRrh&:N/3$jm,VDCimUpJRXpC?$!WOuA?Np-jKtY:,h'?I[er'D1TP6gj5O9<]'dJ1XnJG=9_q%YqqX(;TGlr-)HKr%run?OIDX-?B`U.@@c%e&ggG&`YoHV#i!>cuB7`6F.M/3%rB59B0-)c8:`AN^4&mAJF/1$hJ!U-;kmr&t$h?""[:e5;HLQo4!"l3'O&QYa";B92'#Ql$@n/'tl8'`h\\NUk5q+V`:o6PP9"9PO[tr5J.rQX&Cu":SUHikM>B@PO[D*0_l!<B>#A7*]$Oe'/Xu:5F5&a(%f8@o2TM*Y(kE6CJ6[HZrjiNL8gCDr*3<E2mZNP9QAP6@991*U[u*hr+VXD)QBY>a`pW8\^[>I7pCVH3nmUkJqtps?+jh=n0[]8r)(Jn<-*2[h-1Cr:IjK[CAVPA=H[5EKqCm[)0rE<T?;Mkjj4l7jtJNP/<5l-@Z`DtZ317JIsl?O(TrA8>S\(kQ?SE,DM`.)EDd*88SecNA#:Lii1Mp<`H79]^7.IW>7m\kDUX@/IUM&PY;M(cq!/esUF3L=h6>-%E]6Mpma%>,T/<fo-G=rd4lBjWiTDM0!ml/BLa""jTQCQ]gj?n_Ik$X"(E%+(eF0$h8ZEskFE?Z>(r;1F8_>^(^OmH6Q\-d>lcDI7+r_o5>\!;/`DPZ1Qs%3Q2bUdJ9FIoP<3mL/9g*]SD.,Ga&p[X<q.k2+j_P`l9KuK-5$.pfs"L::dT'AgkFORWJ[RAjWeKn:LEC1%G@4'GRa0lr(@o%i`aEtoY!.S9+s/9ukGtFrY$IYJo)dCGY$>iG5b5#Ne\U,QO([4.X;cBobJIZHlqFaih2aY4PihSV+L(QQ<2o5XU@u=#(QBjQ72B?bGThN-O!2NeV60*STZeFrg<qfiq[eK"Zar.,&)Nfe'V<e"92kL,jdk;I<=@ogkaYF6D<pK0\Ge2Y+N)-YUFt]0U,W?OoMCZqB05\e+RA'84!HB9>hA?Nj<r9_Z$HFoDNU;_Qo1,LBH.6%/^XEQT8t5VLRq*42%"/b&:!#[pgqAfp_<-bU$4VY99FN(S9l\G*e`/`AB9+L$p0)8`Ou[AK++/<OC;d`nQs/IjQmrRe@<g$NNSB$77tKc;^-k:PX;O8Ua[l]%iqAm4Y[i%3gE<>XAcNpWDpk?-ZU$c&>@L@mLI;-.eZU2o>1jC$kqCKE%$Fa=O7/t.GkRJp(rsScM+*+lbKN1+kQ)J2Kl!-otR^mji@1g$=h\OKM&p)C3)po>7=-f@;AcfP^aX$8<J?@L-B'(LVZ-QLJG;@-D#PS&fYjHMDdY-Il#r^a<>4Zjp6lSas!.R0`MD.%)VU43CJ<60<r2DNE,W*/iDM-R8M%&E_m@1MO0iQ!L$;unp5\4$>RX?S7FRaZ.<8kj5^[#?A2TkWfpEKJ]2X`2ha8ZoclTo63KZ7cI\-k65i$q<T"(D;;mLl'`!T&Dn1&;p8f8_.56]I8Km^BPS=Xp7DB_h'nirsDE>D?Tr<WNfZ$c[@29^6k/(GOIa.4l/FG1Rc5*`%2ttJ&I78aOMRm!$DDcL#3I8k5>ZO!?Cd[qXh:o)$QS2'Kn`"FRFYRYC$Z1<?6'1r;KSU2)AgXG0_`giT9ZJ3Bo.F8@`InJs2,oWGk8I9NX4gQ`Wn^q-!/dLVa[Jik7ZK/t:HkL2!EbMlq3<PVo]bB]f&QLC^g'cR"^[0q\TQ.ncOOML6[61fh38^M9iZX--QHIq6&$MW[RWop\''j%5<_2.=D8HM[*gkC5E!L\V49BG.Jfnh(fH8,VD/LdcB\+\]!?:A\@OcpIM=&3-lj`$Rd1;+Xe%?7iAp;I?bWCi>GO3^`-?OjkH:6A>(?O2q";>D#L@q"Q.r=X@!!Kgr$tF"/]-<_>gH[0]H'/h@2SqJ6lE,b~>endstream
endobj
64 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2897
>>
stream
Gb"/)968iI'#+6EoMM$>)$533c#K8Uh+Di2>FCq4W`62.a[erh!D,@7^O@+%T=-#gNlU"U)Q+3'+OKJ!J))p^(]P'<mkP,m2hSR]#S7?L'bK9m@J1;YAk1a>*&i[`l@VGP$,Qfb$>Ougi417USN%#a"A.Ou-jUt=N_@%sGAp[_Eo`>7ob%UlTBI55,!U)A*%1-a;#`qnhpXgJ`;30F@\c#C3sK\i1pSB`6O=2F-E\#m0lG3DErPT>G4OTV(S=I\r>Ak3bWV$Hc)7mtcV?[<J_=+lrR0'?e3bV1oHSbFDYbu.W9[[an)^k#Gu7-m.kAT=\JL@8=A"rZ)SrC(<[-YE;R#NebBAg'K`MNFQK1!-b.6\^&uahoo$jLe^&(]"&4OQ<+]4*;M5]LnKtE?.p?MAb-]A/Z5KJ0,k3b3Dm\e9!A)E-+,hDBR,*.%>C+H3IEZi_AoZ[#:7%M\_@Yd/+"=/iNU%,*tT(XfaY&S=43B$+g^dH\t)(-S)Lk^&8+H/\K4@-)p5q=aJO\%OPM\YmB=+oKj0A*8QJKe?l>'*In2?[;[E'f/=r)Nq"_S9H=:t$!c!n;<+q]8&;6Po4LiZCZ=g25#\Y1^j2#+(>:c&+SdE;L[<g$mAOm_,`lRe3j@98nN2XABVE<_J-)eP_qf=ie![3J1;JVr,d.54RPE06oR-F&1`+$kPA%0c<jR7%N_mj)0isFFl6MR(+CJ[:4N8-J"&gjn7!fN$Gs*Lg0(u3ZreGeBe<<CHUD(lLsY&lig/2RPM.9Kc%'LJccY($-[;-.R:P&CPJ;10ZB^1Tb9A@V9EA%]..-_^u:/&^Zi@nlWpJ@Vm'#b.e*`odOsll:#$'%L#]t]#IBZSCKA=HR_tRET#2#\UdB!3R=1dYKQ8]0KY"6>gCU1H^^9.hQ!ueXe5`V-T?'EO=mi?I"8mp&.mA9a%9s4,'<bM]NVO,!UHZ.a19E&eKeL39-ou"7kX[!T\]rgej*+4K<MhXmp'oLjg56a!6sI2=F6K=3NKJRrC>G'b>6Bk3I[YV:?K)/6cA/u%HX>ZOmY+so;RVeR2Dt<84(N&<2C;`5Ic/'tm?6`%]pNI7SW66FDi*%sI>e^/]$<1RI6C=ZX&0lH3m3Y*HG^DICCf@rOU3YW[j>(bNf!/W[2Fr@jdpOI?4k7XmSmR24GiWa-<PG<;t;c^mqh4TV!HF+d64h@\_E5hnK2cm(OTT=ZaTbDRfTE[`DMh`$ko<J`Q16s3/,I#Ad<o_+4?IQj5KTHL'j,sWi5XH:m[36cU7:Q<h`H"1laP`nI1U"VNsoY759KB9,V=k3GgdKPW[uSf]#Z7>fsXV(=dmn]\cbo)mhoGnVWU!Y:r7[fS4$cI4:!?b&/_K>B!';qsb>1pYB\lT@DCK>h0jkH.?IkY!g9&==V/`YaQ[a/B,3!2cIddRV)J`+U;_JZ6pToE`"ai;N%Ve`h@lG<7kO?n!tRes,8uB?j/IQGG'ceNY=M`+^4clOUf*IQ-+oBeZu.+'W##jJn'p"V5KL[0euq=Y-?R/B\9/sZ8e)u3WYnn=FOg'/u;IYg7>#`Pl/LQe'l.t*upgFr:=K/o0"V-Y9[%\+%POk>bRM$^d!1M0f:j/"Y1p?<`Io3q.,oXZ;HE(HX"WdDS_1Id!SMt5,70TkGK^8B,+c)1P*b&X;o]?-gL^bEh6WHCH$s*O-cfA9V>%EI:/f'RZ1d+fnj#tn7GrSRCKkol<B)W29;^5MDp?P0S2'44GR4t,d4OH**_B0<F_4DM]mVahra8po8_G23QPEB0U0:e)k4W[(5$V5UR1=>h^9QMCC&kSp<A4rWS:pTN_cArpMmU)BX_VBFK[fXmF<-1B@f[j(\P1ConZo(f62:L?fafGI^&5:oXVdrD.b6-6qKAI%E!f8S&Wia(u%gra4Rn:0K/U5@r,D"*6&KWK(?riCt.O<KX%.7q(Rd+"MtCPlmL/.(h44<r-nt)0mPpDD34?-9k.cm*KV29qit:8C0\MP;/TnfY(U\X#FKQrZ,@DMNr[<ka`CK:Z#/nbFK,.S`lgX7du,mJLQHdCo=Zmn?e1#PM^;`/)DV%ecWir.\P%h;kWs7"fC9OC&S8]"=pL@=<7q1*hqGPZK0b-dV9dAY$KXaiN(JfiZ1ji'o3]5,I3`M"*YW@"AKU,NXiBh>>1:<VH!/UjT`;+3nsU$ck1Ff]&o0n2#B"4*]\p@/7(E';`%%(tj0fmJ:$6sD-pMo\D-3X2e<<RQ[h+?8[FBVFZ!b.m?EnDc>5W`lbA,FA]:i/>hC=*!C4\#GG^2O-bX\0$2<O")g%2WoD:37BgRe>?&#G,kn$LEU3o]qbTS"7VO,&2p:no!>!6)PcJ;,YI%<oYA*`+*<C'`bRK@mZf1W$lNC(I8PEj_*E[qA*NY<2nPh<c-^l>C_IW12>.J=8sTIR$X+jLG<!f1`A.V@NhC:;nuP^u6Lg[#Zp6+LXaIoQp,pDB/VQ@dCelf72,M?P-JF7)9Ea$H8l]/]p\8>#P4k#R6"+<WcXQVGL<pW_3^2f&L4o^tM["/6`rJQ9HgrIR_`bH0=c@S<Q^.M'hEFa8?Ct3$m.3R\?p5#\b%`'"BIQ@b%6D8\WR*'!dnA/\el_o%SUur<dU]Rta#B99#N%)6`,)G:]m3UVG3657l"hH2.)#0VUG&6.e::^BBC"W@#sc#RIi*l[`i*0]DO)YmF8&^h@D_^GO.^Y8$aZLt'E]FTD2]hO0<%W"_UCk?1uWn)d5lg0j1'$Wk?*<"s,=%.n`aaMh&,_-s@ZeRoh^59oI.i&@Cs7Kb$h?)@3l1C,)4C+:]mI\Ei%)l]hqXp:L`pS"J_on(IHq`VJ@6m^@aYeP,Q?ED%na%^W7(!AH\bO*,os!7Y$JCp1*IWdXK(b#DcR#UQgnkYj:_pSJa$ZVVP~>endstream
endobj
65 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 3241
>>
stream
Gb"/j>BAQ-&qJm</+Ct)2\Vqh_!=?0Pf.Ee-I0S2POm]M%n&`>;Mn9Wd^f;PmK+Y&ad/Ed->ntkB+csFYSV88D\o&a$/c>8s5ls@*_jaP^G*MaREc*HBh5*samo#]cN>(RC!&e0).ug:&^!\Y32iKWOp*\%@n*1DU-+9A&ht;D&OQppjpqqM9*:;r6Gj4-#Yi/e\>).Oqo/>o5^S&HKVmD"1GM0'e%n;ZauWn8N,lpJP-TTb5me'fjRC.;a`R*so<ZmPH#cU(-Po*g3&s-N2Ouu/O\FYol%;_@8rm\>ne4j1>CO@:%mhrLM94MKWZ"6bD,\X?6:>X`F$hQ>:WR$Ec#o_[>g)]8ZJD[>3!/8%4aEl=HQb^a#cE4icN>6aZeql.\nY2M)&PcG6oS_RE;)^.f#'-r)Q-FDIalgc)@tY4jSmQej(V&J/5%_q=<b7o?@+nHkN0dYQOoR5a+VoD,Ul:fUCkZ^#\^^e\@ZOq/9%<-LVRN>.j2WLf8!rhM;c;!/;hmn/C._R7+D+X;D=7R_8/50d4/T<Fp`DQl"D1@_U3LjQo]d!$9VkhLAu]*rW@Z-*5G)m4GmOBP;Es^m#D0-,A4b4J89(Mj-OFC*(t>/4aVB!fXMKo>I]%?:+ljcI@.V1A*5DJiDem/iXiN]'gs[#Ff'"n/5bJ-2gQVj^lo.^IV!FW]<.S,OaeGdc=o$rGDN]:DI@F(EFG4</;nLE63`Je#Gq/p5fgJY%L2ZFL/lI8On^#1C8!0+aB>Jb%.]/6V-\_kfLZK=0.[MF.ThW3(e4![iFNVVS]2q!a4)ua'B*.*?..A^8[2icLU-B@U6<7('9Nrm=HYs=G6j^%3Op?;+->dG#?;KtCh>Y_I;GZV0]iY$T,??7`-\-McG=&_d;]inEK`Ck_M?CbF0_#Va`2o<['_G1\X"=J<>\WT![F^[^l_)dZ>+7]dh'@Cg(`rVKi$/k>9U^Jias/82!WuEbi=;c=GGKV#T:YT>B9M/e/6kHUp_PP!=MbW=9/6"[Z+H_8u;forj,VtHW$eX(j0*c6>Ee]F_CKG/ta*+kHaM]#Z,mS_42c)1SUo)/4jV]G]d,j5st)uH%[Q))\\ViCI,<L)n=6PR8?/hQ>f;\Q:*D\.,;6]0W2IgcsN9bP*N*pO_k,LWAQ[*fG#0%f35j6Alcf2$k/'u>,%=Y5o9'"X\>_cpD\Dg.s!<,hnJ7tL=#kJ!/h-GrLBHFBXqgWk1jo8%fD50']Xfeohq/L6\n.][:_l(NA(ol==3>oRP?;ICbdT`iQ6*dF3"_9jVl*.itaS*0cDa.\Hs69`*q3u9R(C?[Q3W3jq1]gi2sSU/%=6VBc(J[BCq,9bU>Kj/>gc=ZcA&Cib-:CO7<=N=HXrH@o"%6,e,ZTP.MF0)u7,+Nk:%CS(2G>)>G^D.Hg,Y42'EmD`:4WH_8m#K(fZ$-()nDb!Y-d9[f*>9HR-J_3L)hpYXWLrcq9AUe4&8-k/<`'$B!Th^qO8^g:u/E5'oS*<p(3L;^B!e^Iu%Y>.lt<mu3TQ#uS7fAYJ-RY%f1S93hE"7nR$A"]8hZSa^KrjGkgD!&G!PT[S!27Gr];`=:6p"_ZD3tdO?#=BhW>G_sgIAZMaAI;@$dR)Ia<rcR0FD]*(Q<0*(<gQpX)(VtF;tlM&Lb.n6KVgP#[Z.iACF'pBkm`>unIcmnWJerFD3d>7!VDNps7c'RMs8@TBB8`(a3V3Hbuba'ihsO3T:HWh&'cLLkJuZVjQtRAZX8."=LD-]afI=\J7p&dOUHF+!;'2g+kn/C.*]VH8>f<)inT::@JJ.&Q(1FH-iqna$HE^a(TAob@N'hVqdc;BFBPX'B,GopS4/Of1p)H;P,8-X8]bRV!%f=+.953Z97h,$YY@qsce:QK\3\khX7%K/fEL]hSg<YYJUJm775KKcgmnb);>):k!I1BQ'61UK#pd`OJHKKcE#:?]"E!JZ3t\):'0rC]#pel,a956<nVat_@`!7e5h&CYUgLjDm0r8r]p+6KS9]X@G2rZ\9pg`(LV#?R<IKkn<2CaG&(Ke_n@=dKlmcndD*1.,@\pI1!Wgm+d*HMP4/G6aiQ(2NQgHjcrtTg[Alm"Mn^-GcRj3K7L'4=%CMf;00GIJ82`\R7n$*C6-V^A:KdCYV'nWB.\A[@tN4I'H[Ru\qQN\-cQQuGN#Bq6f5JmBu<\5Ma,fp8F2qKTMp]"6phm(JrN=eD#[YUOY)oPO>T>gDN<ZdIV:%]NaLMf8eHGNr=nK#I(&<DYYF;bgBdtdskphO!O>K$"'h)]:4L`]+6S]&RNKK\k?1?(N<X5-fV(>%HIQ,`hT8RC^_@t:FtI6XfglWj(9oAbsV>U$B%b-]`Ebcbb[2>pE:P1fsgEGH@2a>JMC\L"dkY-p#[6P5,l;8&+cddI?4gfRMDRTa%C[42Oq..+Mqml$Cre(-^:p%neXM8+oG=0aCsDm35eXVtl%VZ(D>eUY<T#sG<<_W2V@\Q7g?JH";-k"d/#!b>rQ/"HHAiS?Bs>QA@Z&Gm_!#p].`2@FbdK$=Z%Ojrhh2NM)?]5.=+`b!Kl4m0R:k=']@AM8X`AcL;#ris<4kBFM!5g'3YBY]fqlF+3ro_e[KIS+nu^cW?UC\p$)Jc_Or(AH/::n<e>YC$fIm<]-&[01=Lpo@Y(A84i?7[NJDg[duLLWh+Zh=hBN%h4TL!EiR(h@!?`Zo$?^9e^L*MkcH3)]k(H#maeM*^1rmWq78\o4"c5LS6QTbfIAGinfS`76`_4Wg506nb;_R^4+tkmsD3e"'cB)Y2B&Aft6-_rL\%W;OI-/GDY(tDM9&IMYp='<4_f*G"*\gqBbc($B1@Qr(7`R%A!_>SB!`/fgGh^b'di&8h>]1^T\3Uq\l>.pdZ:-jbPD&HkF-C<SLmnrW4>^ZD_?`@VNMPQ)]g$?$3k0btOFi;"EpWFW1Kl;d0:Ga4hRm5.gEc!7B>Z>cm_-q00*U^Ae=SO7DZ[j8<gRhV!A<rThAr0pJIaTOeS?.=PobN*=bDWM"RV&ebUZq8(i"a]Xo9`]%[T^MDX*PF$f&!;KU>9_@h&)_Rdj*(#L1%21S%[':-]"^bH6*c>@$^V.jS7.aj6X86#sK)=Ot>V4"R!mifU.tR^oJ)e-DfkC:)TP7h^+9=HMoqFhQdM-O)(2Ye>qfRJhY0-r?4]?5!5XG)D@uk=ldE9mclhJioK[^>M8Zf-q9lf$\0D*-Jdt)GMT"ha&?r-Y_mCt(:fuBmN3VX!H+eHI~>endstream
endobj
66 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2357
>>
stream
Gb"/hgN)%.&r,lR'RcsKZFL+agoA'WbW&h[9%H"9G&LWNJga/Z9EGVf3j\GMqZ@N]fs8U9VoFqMY#DS>*WU<Vf7,^j7Nqabs1<$%I[JSk?8,h#bu_iH+JJT]\sT+;3XN[1.&)@r(9o\H.>Z'H:DD[/b:k?MN)<cU6'S8bV(;nI35ZD]<o9MPX9diAU*(o@(CK`/%"I;[2SPNgT,c1tmY?D2]sCjNmT*F`$cH&RZ)'"(Ld`0r/.D*R^k3*@hRknAT>.m`p\LD/\tJf\=)f5=bNk(9cHfjG8+Vcus*cba)a+R,2h$c9&!%f%5nkZ`pcn#CDhRC4?.dHlmOCub>#q)pU*uo][9C\K:mL,7ra^dYM6do9hmMNP_UtnKM)@^f3"-m_;*'+%fkQ\M'1Q[q&M'!7hO3kP&AtXIj"e]51e`pehXp=03^!.*iJWR=aNLl.pourA@W1pIBC;nW1$C41*+-pb+H,iCnsBS0.rt]M]:m!OSX>*Pm:ZQj9dpW`l2I6HA9ipF#%a5<)-1(\CN2DhbR,nacL<P*HLqmh-(>2NqK0ai+[s[u2e(_N%,HFiG1Z`0"960oO!jF=*i1;JSJOlAYjO'\2gY5m':AK=>^YRtO3a9ODdr1=KQf&%gF)p4h3Ztno8]2aq)G;cGDo-!UEWP#X9,rXWnjc8ZkRVre=\oG8/LnCBWe/WYW_UH08G8GOJ-q6"o1Ofq"sD`jlFF6=ba$%S=:.GJ3,]Lf#N<f@i\;K5;7OemZ.</8:)$Q+/t'*:_k'AfoD-"!M#.1pAG)@a8(.n.#:5A!)ZAnDXN92*^ds":`"N#0'Pg.AF5$BWL$X\nN&j#=]%j6I/&9RM(Xhg>T_!b7f9]\,cDr588%_qSRR1Dm.dZO6<J,\S'?)VBrO%2*%P#:Xrnt4r@!]$,qru#3q>iFKJk$H35paKk%At$:22[$<u1[%]U"i1[GXMG[AGf\TL[BR%R#o,9#b)_GMLW@[hVtH/T6VF94;Ln!p=h:kl&IHM2&%tl5c1K>-FV?W.O\"\%ub)lj^m$.#0<#'ZucbFrV/SGkRD%2e)S\@AnpHZGONsBUXdX)B+kjg^"BfI[Kj(&_b^3$He\.%;cE'&f(br5fB:FqbVT.D>bAlIkB`0gb4+jL%/`.+',R0LANne#C@-QNji1MZ?*B%@5L$UWu8)jh5fs2pRbL5he6SS\G@hN9=<.%/7$,CqNMntqG(8MfOi>q]@bh.egF&<)Vmr"?"(DMcOo"(=rZJB*m]2VfA&IhnFSFq:VU#Wo0GP@\C4Bp50B(R4X]/6W66Qmd7!c`q)&-^Ya5k+qX(e&GTO0:_B@'c%'Rr@e*Lr?7D#ask:O0/P9)a)jn;F^S.^#Y5-:/V5=c,?%Bgf-<n^;*^-B"'_H'.7S^cqWg[_;gdY_,3Q<X&M^B>h6[A[tbhg,43h\A;.m`4BMq-s/[2l[ahARTI02>m=hkoQfFAK2YHV#2_+IOD_#BQ)OBFMBPV"mG'20?#a3p''03)K!=!'<nA;5uh3OKe\2u_lLN9CC\'!DNOH9ruc48hShg$YGH"]ZIl\IJX'2><C.ThC*ZuE>$!Xq;Gf$sDZ!6fBQ#LN0s>oXq;0,3BnlQD26O=?I-LlD!k:ScXNjO#2D9s"K^8D*S)u0u9CeI_-i`Yds#C#*X)MWoi.%_2KDOq_Mp\\/p*)'VY2HM<mUL;GEXn0kKY:N=`&1WJ@!mM0BBAoiUqS5B/ajqt"c@aZ>r1IHT1k_3H7<UUcAekL47D!0M7^.N4$3]>8$tMj+*iD80-:\jS`fnn!/8JedR(CiO$`H>56RG7FSr[8&j?r"\-R;kIi%Km"FJ`nLD+1td6j%ifbM\cj#M>9H;pn)@@.g0mWe5lW!o2*W8Xc>[Hl_tXi$Re(A/Ai;r?l6@CNO_2$Fq*>X1sTmq^e5o<q*$H,2#9d/)mHb+d^&a\lYP/$KoLnps3^GAL0)j,UfX#pUp.O:$uOY^UCQ^#Oj-eFaR/+JH:*[4C7-d?Vb^a/0/;,koo)-,ai?X..5;f3/J^TKVM(47usUG=<b7!k6pi>/0%&\i5o/fF9Q!`r5EnY1'Suqru!to32,#WmO61UVf&2GH]jJ'jl"2LQ7umrLr/f>K8US@cMXDM8Bk=MTTiYeV+b#(>dK^'f<gH;*BK.T(`2^."e\mF8I'7>Oc(Z1I5bsDQ_cO(om9((lB^5\CmG_fG5!:-+MQnA"9!!hmcU7[W!Vt&",X%-%\Y=1AdrC7lt/OdSU:2o8dRoLEhp5J"'@LCh*9[L3Vm2-"a8@IpCLe]T;UF@5\!1dWo`WP5=R!EI3a9eCWEi4$+<rSUQH._<!=hZb,JmI('9$o`l]i8)R\[U6GTYkncp9~>endstream
endobj
67 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2281
>>
stream
Gb!$G?#SK;&r,lR/*>9XYh<<21NuUR*q[6^S650DJRCpLj:+Y_8r?6&?f-'n+/3R%N_`lp2IobIIa-0En'"Uk^oqj0o8;c)Jq!\fP4WIn+rP\M/[";XEUr:L-_`Hj6h7$+UGsLEVe1?TUL0/j09'I5;&4G+'#bPV:\[57(7J8gSTln)60=@IApcmN&n^,bh<!9Bk+;eD"9'GsjZ^Qp'4,*./J*-f?qZf2^@Yo-V=5+[U1$Kgf2icHMdP=jI!7kp0BqWY2@+\8]Y")YEH1Xn>(!3d:W#.SHL+'_IY'#0O"Yn,(L]Q->FTi%Y.']LgMN^o[m;:k-F\*_q['s@ou[u[cAa:q:3PEW+HBk-Td>3PVS*GU^ln/CIX#5p;1J7cCWY(1h:*40Lm=@0SP=@aM.46u60?.#,f1YohUHf8Nh-&OmdnoS9<p*[r+$H8J[;qs1NcaD%/KrZa,Z#F`BfYb&kF[==TY8d<4Jk'M5>0)q\.V&3W]>JmeV0r9O^!4bG$tUZ18=8(.=qB1?Fdkj'Q`g%o3d93)":5:M1UiaCitI`7,5EGed7G*n]B=_#Np"I"Inar$]EW-YXDgX?.3-U6#=;JSAK2`E0^"r%I;Hn@cFY%W7)N$hnJol,,@HLoHT!UFL7k<7DlKK3G\jjK_d8QiZJ=e!7&eQED2"bs!iSrmXh6rFnrQhb,<"K=*@M>?G&KKUIp)bZb]P31%*bZnqc%%Uh(0pW3,L.d#dgDERKAJaJOX3dBjSiC0LTZX-S+1K_NU,/?M\?g.-f_QKik[V)CL;lZs'fqq["Mk-_G2rH6ropsB&Td[C875IlG)T=):gNF$p.VdKcglMDD*STh<E)S;,eXBAT1j):p6W?^2L72q<5+BQp9\UIi/(9[81`CS=1P1oE<I(K]RG1UkD5/T/PXA\r?$VL(G^nT\r:6B,Qp,Xn*6pane@G4$lmU/,'DJD?=StOj_?oJqMa8U:`9/,4B;tuMa`.gTc4+_*0G;DRljrT*S:X+uc@mWt1ifaj+/RC+V%"NC=*+P-2@9Rh6A//Sh/*m$?s)4`ZdV2*F_P^#eBOFAMN(mkh;kT?Znf%hF7+!<R)A1h?QQ?#oj(YDnLCO"@2i]B<8Hgs>+&!b0l/Z)ec:&9<iUq.I<\?j@K_^R\9W^KL^ucis*mGZaT%jS\RYn(@YF@6#D&Bs5GW7#P2reWHlnB[RCNn2-_&_/Z(R/(V&O6Rc\Vl)VK00'8'e+e1L<Xkd]m1C>C\[A8'Zk<pJ`SO)_+OVOnHWn3u2V"#\san]QTOEL/d2R:,c8s$W'JeN,/sTf7gmc6lXlSm9+fD[:WFem7).L"[0e'N-UF:h]RGXin4"h[;,BaeYQc$D0#<LT:j-()j\ZYM'T4bFjUg0f5OrFB<B/BVL;.qL/<6X`,_dcL/86`.Ye*Yk+S&Yh%-qmPQ'N,^\af/\sLLtA^TUd%rfh+20Gf.R6L&Vf=q^R)r5>Tb)FN",Ihb045k6d:.dE)p[hWH\SdAk3H1uthLUPO(ABe2[Fl]2GP&:+C9J--2H;)BCoLE5RsI5?e?0NFqSZ0:-suDY$\Rcs$[Pb2'M!P,H%U#rBZ!mN]0'B7q<caE8M2i;/9J;>r'Rm$a8LtNDN/b^I@:"mFh2=(Vs>4_\;=C.%dFV-2l^PG2T;8r`jB4r$'r@Ub9V260%;6oLo<=<:Oe(GITdUp(3<=@9KQoC7c7OeCqBb"[X=M$Z*:(`;s;2p7k]=?`YDA_4^ZgTDH&cp<**AKqmh;&r);[MXEoQ4CV\c.'gh$d1:U3_RWk'+iO#.YFCi7\AF8II>&cEM^d[^kc0mL-kVIN.M/2aD1P7.SH)7Om*AEr@1-OB\8q41TmW6/BKu/pG*(8Jp78Z1q&;qnbR[VGLMT$2'H>s5h=s;SP>.W\"U83"EDS(Z%ZIAhUHM0<g*/,?<m*(^jX5Q3A_$Tt&6LD+\>XS.LAp=<TaZI4)DH,FF0ra#&h(6I=MUO.NS#]\X`JG^MqJIZe-98.l;>MP#.)R75oDH'^_,uj_=i1A^r+HNq^>fR]]W.PoF9QA<'Vs]/([)7H]=0uh?^#^hfA?!ddX1"<F>DMcV!1^;#"([+Kh2'l)[N-9aL$@m\MH;ml#6;L3-M0AO:`2NW=0"lauOlT+i!$</2'*1!"*iQ8]kn&"Mg92G'm`WHF3uhBnQre1VPIQN[Me.,DTdSV/dYr;%l=hG^]eBS`QiJJ;o>:R"F6BWa?REq,$E6\F]Mi&#hbl)JXRS#DZ=)FgsE(3dO?ie>.S=d'&4^:At<,Z-tl~>endstream
endobj
xref
0 68
0000000000 65535 f
0000000061 00000 n
0000000159 00000 n
0000000266 00000 n
0000000378 00000 n
0000001557 00000 n
0000001762 00000 n
0000001877 00000 n
0000002082 00000 n
0000002287 00000 n
0000002492 00000 n
0000002612 00000 n
0000002818 00000 n
0000003024 00000 n
0000003102 00000 n
0000003308 00000 n
0000003414 00000 n
0000003620 00000 n
0000003826 00000 n
0000004032 00000 n
0000004238 00000 n
0000004444 00000 n
0000004650 00000 n
0000004761 00000 n
0000004967 00000 n
0000005173 00000 n
0000005379 00000 n
0000005585 00000 n
0000005791 00000 n
0000005997 00000 n
0000006203 00000 n
0000006409 00000 n
0000006615 00000 n
0000006821 00000 n
0000007027 00000 n
0000007233 00000 n
0000007439 00000 n
0000007645 00000 n
0000007715 00000 n
0000008080 00000 n
0000008332 00000 n
0000010298 00000 n
0000011936 00000 n
0000014918 00000 n
0000017772 00000 n
0000018667 00000 n
0000021716 00000 n
0000023148 00000 n
0000025847 00000 n
0000028961 00000 n
0000029856 00000 n
0000032578 00000 n
0000034217 00000 n
0000036541 00000 n
0000039058 00000 n
0000042161 00000 n
0000044548 00000 n
0000047384 00000 n
0000050531 00000 n
0000051418 00000 n
0000054289 00000 n
0000057442 00000 n
0000059154 00000 n
0000062140 00000 n
0000064124 00000 n
0000067113 00000 n
0000070446 00000 n
0000072895 00000 n
trailer
<<
/ID
[<1067f889993850e52c02a67053a8ee0c><1067f889993850e52c02a67053a8ee0c>]
% ReportLab generated PDF document -- digest (opensource)
/Info 38 0 R
/Root 37 0 R
/Size 68
>>
startxref
75268
%%EOF

View File

@@ -0,0 +1,231 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Agência PSI — Billing (Arquitetura Oficial v1.1)</title>
<style>
:root{
--bg0:#f6f8fc;
--bg1:#eef2f8;
--panel:rgba(255,255,255,.85);
--border:rgba(15,23,42,.10);
--text:rgba(15,23,42,.92);
--muted:rgba(15,23,42,.70);
--accent:#2563eb;
--ok:#047857;
--warn:#b45309;
--danger:#b91c1c;
--radius:18px;
--shadow:0 18px 60px rgba(2,6,23,.10);
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
}
*{box-sizing:border-box}
body{
margin:0;
font-family:var(--sans);
background:linear-gradient(180deg,var(--bg0),var(--bg1));
color:var(--text);
}
.layout{
max-width:1100px;
margin:0 auto;
padding:40px 20px 80px;
}
header{
border:1px solid var(--border);
background:var(--panel);
border-radius:var(--radius);
padding:28px;
box-shadow:var(--shadow);
}
h1{margin:0 0 12px;font-size:30px}
h2{margin-top:40px;font-size:20px}
p{color:var(--muted);line-height:1.6}
.section{
margin-top:30px;
border:1px solid var(--border);
background:var(--panel);
padding:24px;
border-radius:var(--radius);
box-shadow:var(--shadow);
}
.rule{
border-left:4px solid var(--accent);
background:rgba(37,99,235,.08);
padding:14px;
border-radius:12px;
margin:18px 0;
}
.ok{
border-left:4px solid var(--ok);
background:rgba(4,120,87,.08);
padding:14px;
border-radius:12px;
margin:18px 0;
}
.warn{
border-left:4px solid var(--warn);
background:rgba(180,83,9,.10);
padding:14px;
border-radius:12px;
margin:18px 0;
}
.danger{
border-left:4px solid var(--danger);
background:rgba(185,28,28,.08);
padding:14px;
border-radius:12px;
margin:18px 0;
}
code,pre{font-family:var(--mono);font-size:13px}
pre{
background:rgba(2,6,23,.05);
padding:14px;
border-radius:12px;
overflow:auto;
}
table{
width:100%;
border-collapse:collapse;
margin-top:16px;
}
th,td{
border:1px solid var(--border);
padding:10px;
font-size:13px;
}
th{background:rgba(15,23,42,.04)}
footer{
text-align:center;
margin-top:50px;
font-size:12px;
color:var(--muted);
}
</style>
</head>
<body>
<div class="layout">
<header>
<h1>Billing — Arquitetura Oficial v1.1</h1>
<p>Versão 1.1 inclui procedimento formal de migração controlada para planos core, mantendo guardrails ativos e auditáveis.</p>
</header>
<div class="section">
<h2>1. Fundamentos do Domínio</h2>
<p>Billing define recursos e limites do produto. Não é camada de UI. É camada estrutural.</p>
<div class="rule"><strong>Princípio:</strong> Role (RBAC) ≠ Plano (Billing). Plano dirige features e limites; role dirige acesso.</div>
</div>
<div class="section">
<h2>2. Planos Core (MVP)</h2>
<ul>
<li>clinic_free</li>
<li>clinic_pro</li>
<li>therapist_free</li>
<li>therapist_pro</li>
</ul>
<div class="ok"><strong>Política:</strong> Planos core são estruturalmente protegidos por triggers.</div>
</div>
<div class="section">
<h2>3. Governança de Guardrails</h2>
<ul>
<li>Impedem alterar key de plano core</li>
<li>Impedem alterar target de plano core</li>
<li>Impedem deletar plano com subscription ativa</li>
</ul>
<div class="danger"><strong>Proibido:</strong> desabilitar triggers diretamente em produção.</div>
</div>
<div class="section">
<h2>4. Procedimento Oficial de Correção de Plano Core</h2>
<p>Correções estruturais devem ocorrer via função administrativa controlada.</p>
<pre>
create or replace function admin_fix_plan_target(
p_plan_key text,
p_new_target text
) returns void
language plpgsql
security definer
as $$
declare
v_plan_id uuid;
begin
select id into v_plan_id
from plans
where key = p_plan_key
for update;
if v_plan_id is null then
raise exception 'Plano não encontrado.';
end if;
if exists (
select 1 from subscriptions where plan_id = v_plan_id
) then
raise exception 'Plano possui subscriptions ativas.';
end if;
update plans
set target = p_new_target
where id = v_plan_id;
end;
$$;
</pre>
<div class="warn">
Esta função deve ser executada apenas por role administrativa e registrada em log de auditoria.
</div>
</div>
<div class="section">
<h2>5. Entitlements (Contrato Oficial)</h2>
<p>Entitlements são derivados exclusivamente de <code>plan_features</code>.</p>
<pre>
plan_features (
plan_id uuid,
feature_id uuid,
enabled boolean,
limits jsonb
)
</pre>
<div class="rule">
Formato oficial de limits:
{"max": 30}
{"per_month": 40}
{"max_users": 1}
</div>
</div>
<div class="section">
<h2>6. Preço Vigente</h2>
<pre>
create unique index uq_plan_price_active
on plan_prices (plan_id, interval, currency)
where is_active = true and active_to is null;
</pre>
</div>
<div class="section">
<h2>7. Onboarding</h2>
<ul>
<li>Tenant clinic → clinic_free</li>
<li>Tenant therapist → therapist_free</li>
</ul>
</div>
<footer>
Agência PSI — Billing Arquitetura Oficial v1.1
</footer>
</div>
</body>
</html>

View File

@@ -0,0 +1,957 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Documentação Interna — Planos, Assinaturas e Seeder (Billing) | Agência PSI</title>
<style>
:root{
--bg0:#f6f8fc;
--bg1:#eef2f8;
--panel:rgba(255,255,255,.78);
--panel2:rgba(255,255,255,.92);
--border:rgba(15,23,42,.10);
--text:rgba(15,23,42,.92);
--muted:rgba(15,23,42,.70);
--muted2:rgba(15,23,42,.56);
--accent:#2563eb;
--warn:#b45309;
--danger:#b91c1c;
--ok:#047857;
--shadow: 0 18px 60px rgba(2,6,23,.10);
--radius: 16px;
--radius2: 22px;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
}
*{ box-sizing:border-box; }
body{
margin:0;
font-family: var(--sans);
color: var(--text);
background:
radial-gradient(1200px 600px at 18% -10%, rgba(37,99,235,.10) 0%, transparent 60%),
radial-gradient(900px 520px at 90% 10%, rgba(2,132,199,.10) 0%, transparent 55%),
linear-gradient(180deg, var(--bg0), var(--bg1));
}
.layout{
display:grid;
grid-template-columns: 320px 1fr;
gap: 20px;
max-width: 1320px;
margin: 0 auto;
padding: 28px 18px 42px;
}
header{
grid-column: 1 / -1;
padding: 18px;
border: 1px solid var(--border);
background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(255,255,255,.72));
border-radius: var(--radius2);
box-shadow: var(--shadow);
}
.kicker{
font-size:12px;
letter-spacing:.08em;
text-transform:uppercase;
color:var(--muted2);
margin:0 0 8px;
}
h1{
margin:0 0 8px;
font-size:30px;
letter-spacing:-0.02em;
}
.subtitle{
margin:0;
color:var(--muted);
max-width:980px;
line-height:1.55;
font-size:14px;
}
aside{
position:sticky;
top:18px;
align-self:start;
border:1px solid var(--border);
background:var(--panel2);
border-radius:var(--radius);
box-shadow:var(--shadow);
overflow:hidden;
}
.toc-head{
padding:14px;
border-bottom:1px solid var(--border);
background:rgba(15,23,42,.02);
}
.toc-title{ margin:0 0 6px; font-weight:800; }
.toc-desc{ margin:0; font-size:12px; color:var(--muted); }
.toc{ padding:10px; }
.toc a{
display:block;
padding:8px 10px;
border-radius:12px;
font-size:13px;
color:rgba(15,23,42,.88);
text-decoration:none;
}
.toc a:hover{ background:rgba(37,99,235,.06); }
main{
border:1px solid var(--border);
background:var(--panel);
backdrop-filter: blur(6px);
border-radius:var(--radius);
box-shadow:var(--shadow);
overflow:hidden;
}
.section{ padding:18px; border-top:1px solid var(--border); }
.section:first-child{ border-top:none; }
h2{ margin:0 0 10px; font-size:18px; }
h3{ margin:12px 0 8px; font-size:14px; color:rgba(15,23,42,.86); letter-spacing:.01em; }
p{ margin:0 0 10px; color:var(--muted); line-height:1.65; }
ul{ margin:10px 0 0 18px; color:var(--muted); }
li{ margin:6px 0; }
.card{
border:1px solid var(--border);
background:rgba(255,255,255,.72);
border-radius:var(--radius);
padding:14px;
}
.rule{
border-left:4px solid var(--accent);
background:rgba(37,99,235,.08);
padding:12px;
border-radius:12px;
margin:12px 0;
color: rgba(15,23,42,.82);
}
.ok{
border-left:4px solid var(--ok);
background:rgba(4,120,87,.08);
padding:12px;
border-radius:12px;
margin:12px 0;
color: rgba(15,23,42,.82);
}
.warn{
border-left:4px solid var(--warn);
background:rgba(180,83,9,.10);
padding:12px;
border-radius:12px;
margin:12px 0;
color: rgba(15,23,42,.82);
}
.danger{
border-left:4px solid var(--danger);
background:rgba(185,28,28,.08);
padding:12px;
border-radius:12px;
margin:12px 0;
color: rgba(15,23,42,.82);
}
.table{
width:100%;
border-collapse:separate;
border-spacing:0;
margin-top:10px;
border:1px solid var(--border);
border-radius:var(--radius);
overflow:hidden;
background: rgba(255,255,255,.72);
}
.table th, .table td{
padding:10px 12px;
border-bottom:1px solid rgba(15,23,42,.08);
font-size:13px;
color:rgba(15,23,42,.88);
vertical-align: top;
}
.table th{
background:rgba(15,23,42,.03);
font-weight:800;
color: rgba(15,23,42,.72);
}
.table tr:last-child td{ border-bottom:none; }
code, pre{ font-family:var(--mono); font-size:12px; }
pre{
background:rgba(2,6,23,.04);
border:1px solid var(--border);
border-radius:var(--radius);
padding:12px;
margin-top:10px;
overflow:auto;
line-height: 1.55;
color: rgba(15,23,42,.90);
}
.pill{
display:inline-block;
padding:6px 10px;
border-radius:999px;
border:1px solid var(--border);
background:rgba(255,255,255,.72);
font-size:12px;
margin:4px 6px 0 0;
color: rgba(15,23,42,.78);
}
.grid2{
display:grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.kv{
display:flex;
gap:10px;
align-items:flex-start;
justify-content:space-between;
border:1px solid rgba(15,23,42,.08);
background:rgba(255,255,255,.72);
border-radius:14px;
padding:12px;
}
.kv b{ color: rgba(15,23,42,.88); }
.kv span{ color: var(--muted); font-size:12px; }
.path{
display:inline-block;
padding:3px 8px;
border-radius:10px;
border:1px solid rgba(15,23,42,.12);
background:rgba(255,255,255,.72);
color:rgba(15,23,42,.88);
font-family:var(--mono);
font-size:12px;
margin:2px 0;
white-space: nowrap;
}
footer{
grid-column:1 / -1;
margin-top:14px;
text-align:center;
font-size:12px;
color:var(--muted2);
}
@media (max-width: 980px){
.layout{ grid-template-columns:1fr; }
aside{ position:relative; top:0; }
.grid2{ grid-template-columns: 1fr; }
}
@media print{
header, aside, main{ box-shadow:none; }
.section{ page-break-inside:avoid; }
body{ background:white; }
main, aside{ background:white; }
.rule,.ok,.warn,.danger{ background:white; }
}
</style>
</head>
<body>
<div class="layout">
<header>
<p class="kicker">Agência PSI • Documento interno</p>
<h1>Planos, Assinaturas e Seeder — Billing (MVP)</h1>
<p class="subtitle">
Documentação interna do <strong>domínio de Billing</strong> do SaaS multi-tenant (Agência PSI),
cobrindo <strong>modelo de dados</strong>, <strong>views oficiais</strong>, <strong>catálogo de planos</strong>,
<strong>princípios de produto</strong> e um <strong>seeder idempotente</strong> para instalação nova.
O objetivo é impedir divergência entre <em>UI</em>, <em>backend</em> e <em>banco</em> (e evitar pricing nulo, upgrade quebrado e gating inconsistente).<br><br><strong>Atualizado em:</strong> 2026-03-01 (após validações reais do schema e execução do seeder).
</p>
</header>
<aside>
<div class="toc-head">
<div class="toc-title">Sumário</div>
<p class="toc-desc">Navegação rápida entre as seções.</p>
</div>
<nav class="toc">
<a href="#1-visao-geral">1. Visão geral do domínio</a>
<a href="#2-principios">2. Princípios e decisões</a>
<a href="#3-conceitos">3. Conceitos: role vs target vs plano vs feature</a>
<a href="#4-modelo">4. Modelo de dados (Postgres/Supabase)</a>
<a href="#5-views">5. Views oficiais (fonte de verdade)</a>
<a href="#6-catalogo">6. Catálogo de Planos (MVP)</a>
<a href="#7-precos">7. Preços (MVP) e vigência</a>
<a href="#8-seeder">8. Seeder (nova instalação) — SQL idempotente</a>
<a href="#9-onboarding">9. Onboarding & Upgrade (fluxo)</a>
<a href="#10-runbook">10. Operação (runbook rápido)</a>
<a href="#11-qa">11. Checklist de QA</a>
<a href="#12-prompt">12. Prompt Mestre — continuação (Billing)</a>
<a href="#13-tags">Tags</a>
</nav>
</aside>
<main>
<section class="section" id="1-visao-geral">
<h2>1. Visão geral do domínio</h2>
<p>
O Billing define <strong>o que pode</strong> e <strong>o quanto pode</strong> dentro do produto.
Ele não é “uma tela de preço”: é a camada que decide
<strong>limites</strong> (quantidade), <strong>habilitações</strong> (booleanos) e <strong>estado de assinatura</strong>.
</p>
<div class="rule">
<strong>Definição operacional:</strong> o Billing é composto por (1) catálogo de planos, (2) preços vigentes,
(3) assinatura ativa por tenant/usuário, e (4) entitlements derivados do plano.
</div>
<div class="ok">
<strong>Objetivo do MVP:</strong> todo mundo começa no <strong>FREE</strong> (clínica e terapeuta).
Paciente não é pagante; o “portal do paciente” é um recurso habilitado pelo plano do terapeuta/clínica.
</div>
</section>
<section class="section" id="2-principios">
<h2>2. Princípios e decisões</h2>
<ul>
<li><strong>Separação rígida</strong>: Role (RBAC) não é Plano (Billing). Plano define recursos; role define permissões de acesso.</li>
<li><strong>Planos por target</strong>: existe plano de <code>clinic</code> e plano de <code>therapist</code>. Isso impede aplicar plano errado em outro tipo de conta.</li>
<li><strong>Tudo começa gratuito</strong>: criação de tenant atribui automaticamente um plano <code>*_free</code>.</li>
<li><strong>Pricing público por View</strong>: a UI de preços deve consumir <code>v_public_pricing</code> (não montar preço manual no front).</li>
<li><strong>Preço é temporal</strong>: preço tem vigência (<code>active_from</code>/<code>active_to</code>) e um “ativo atual”.</li>
<li><strong>Seeder é padrão</strong>: nova instalação do banco deve nascer com os 4 planos do MVP + public metadata + preços PRO.</li>
</ul>
<div class="warn">
<strong>Problema real observado:</strong> a view <code>v_public_pricing</code> retornou preços <code>null</code> porque havia histórico em <code>plan_prices</code> mas nenhum registro vigente (todos com <code>is_active=false</code> e <code>active_to</code> preenchido).
</div>
</section>
<section class="section" id="3-conceitos">
<h2>3. Conceitos: role vs target vs plano vs feature</h2>
<div class="card">
<div class="grid2">
<div class="kv"><b>Role (RBAC)</b><span>permissão de UI/rotas (clinic_admin, therapist, patient etc.)</span></div>
<div class="kv"><b>Target (produto)</b><span>tipo de conta: <code>clinic</code> ou <code>therapist</code></span></div>
<div class="kv"><b>Plano (billing)</b><span>free/pro por target; é o “pacote” contratado</span></div>
<div class="kv"><b>Feature / Limite</b><span>entitlements: booleanos e limites numéricos derivados do plano</span></div>
</div>
</div>
<h3>3.1 Regra do produto: “um usuário pode ser paciente e terapeuta”</h3>
<p>
Essa regra é de <strong>identidade</strong> (um mesmo <em>user</em> pode estar em múltiplos contextos),
mas o plano é aplicado ao <strong>tenant</strong> (clínica/terapeuta). Assim, um usuário pode:
</p>
<ul>
<li>estar em um tenant therapist (com <code>therapist_free/pro</code>)</li>
<li>estar em um tenant clinic (com <code>clinic_free/pro</code>)</li>
<li>acessar portal de paciente como consumidor do serviço (sem plano próprio)</li>
</ul>
<div class="rule">
<strong>Consequência:</strong> plano nunca deve ser inferido do role.
O role dirige menus/rotas; o plano dirige features/limites.
</div>
</section>
<section class="section" id="4-modelo">
<h2>4. Modelo de dados (Postgres/Supabase)</h2>
<h3>4.1 Tabelas mapeadas (schema: public)</h3>
<table class="table">
<thead>
<tr>
<th>Tabela</th>
<th>Responsabilidade</th>
<th>Observações práticas</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>plans</code></td>
<td>Catálogo interno de planos (id, <code>key</code>, <code>target</code>, flags e campos legados de preço)</td>
<td><strong>Não</strong> usar <code>plans.price_cents</code> como preço público; é legado/fallback.</td>
</tr>
<tr>
<td><code>plan_prices</code></td>
<td>Preços por intervalo e moeda, com vigência</td>
<td>Fonte do valor monetário; a view pública agrega mensal/anual.</td>
</tr>
<tr>
<td><code>plan_features</code></td>
<td>Entitlements por plano (limites e habilitações)</td>
<td>Define o que o produto permite no runtime (gating).</td>
</tr>
<tr>
<td><code>plan_public</code></td>
<td>Marketing/metadata do plano (nome público, descrição, badge, destaque, visibilidade)</td>
<td>Direciona a tela de preços e o “tom” comercial.</td>
</tr>
<tr>
<td><code>plan_public_bullets</code></td>
<td>Bullets de venda por plano</td>
<td>Lista simples; a view pode agregá-las em array.</td>
</tr>
<tr>
<td><code>subscriptions</code></td>
<td>Assinatura ativa (por tenant ou user) e status</td>
<td>Fonte de verdade do plano vigente do tenant.</td>
</tr>
<tr>
<td><code>subscription_events</code></td>
<td>Histórico de mudanças (old/new plan)</td>
<td>Útil para auditoria e debug de upgrades.</td>
</tr>
<tr>
<td><code>subscription_intents</code></td>
<td>Intenção/checkout pendente</td>
<td>Controla upgrade antes de virar subscription.</td>
</tr>
<tr>
<td><code>entitlements_invalidation</code></td>
<td>Invalidação de cache de entitlements</td>
<td>Garante refresh quando plano muda.</td>
</tr>
</tbody>
</table>
<h3>4.2 Padrão de “preço vigente”</h3>
<div class="warn">
<strong>Armada clássica:</strong> se não existir pelo menos 1 preço vigente por <code>(plan_id, interval, currency)</code>,
a tela de pricing pode retornar <code>null</code> e o checkout fica sem referência.
</div>
<pre><code>-- Um preço é considerado vigente quando:
-- is_active = true
-- AND active_to IS NULL
-- AND now() >= active_from (se active_from existir)
</code></pre>
</section>
<section class="section" id="5-views">
<h2>5. Views oficiais (fonte de verdade)</h2>
<h3>5.1 View pública de pricing (UI deve consumir)</h3>
<div class="rule">
<strong>UI MUST:</strong> a tela de preços deve consultar <code>v_public_pricing</code>.
Evitar compor preços no front com join manual, pois isso cria divergência e bugs silenciosos.
</div>
<pre><code>select
plan_key,
plan_name,
public_name,
public_description,
badge,
is_featured,
is_visible,
sort_order,
monthly_cents,
yearly_cents,
monthly_currency,
yearly_currency,
bullets,
plan_target
from v_public_pricing
order by plan_target, sort_order;</code></pre>
<h3>5.2 View de preços ativos (infra/diagnóstico)</h3>
<pre><code>select *
from v_plan_active_prices
order by plan_id;</code></pre>
<h3>5.3 View de assinatura do tenant (gating/RBAC por plano)</h3>
<pre><code>select *
from v_tenant_active_subscription;</code></pre>
<h3>5.4 View de saúde de assinaturas (debug)</h3>
<pre><code>select *
from v_subscription_health
where status &lt;&gt; 'healthy';</code></pre>
</section>
<section class="section" id="6-catalogo">
<h2>6. Catálogo de Planos (MVP)</h2>
<div class="ok">
<strong>Decisão fechada:</strong> MVP com 4 planos (2 targets × free/pro).
Os planos antigos (ex.: <code>pro</code>, <code>plano_2</code>) podem ser descontinuados e ficar invisíveis.
</div>
<table class="table">
<thead>
<tr>
<th>plan_key</th>
<th>target</th>
<th>Tipo</th>
<th>Objetivo</th>
<th>Notas de produto</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>clinic_free</code></td>
<td><code>clinic</code></td>
<td>FREE</td>
<td>Entrada de clínicas pequenas (começar sem cartão)</td>
<td>Usável, mas com teto claro para gerar upgrade natural.</td>
</tr>
<tr>
<td><code>clinic_pro</code></td>
<td><code>clinic</code></td>
<td>PRO</td>
<td>Clínica completa</td>
<td>Habilita secretária, relatórios, automações etc. (conforme evolução).</td>
</tr>
<tr>
<td><code>therapist_free</code></td>
<td><code>therapist</code></td>
<td>FREE</td>
<td>Entrada de terapeuta solo</td>
<td>Permite operar, mas limita escala (pacientes/sessões).</td>
</tr>
<tr>
<td><code>therapist_pro</code></td>
<td><code>therapist</code></td>
<td>PRO</td>
<td>Profissional estabelecido</td>
<td>Expande limites e libera automações/relatórios conforme roadmap.</td>
</tr>
</tbody>
</table>
<h3>6.1 Limites sugeridos (MVP — ajustável)</h3>
<table class="table">
<thead>
<tr>
<th>Entitlement</th>
<th>clinic_free</th>
<th>clinic_pro</th>
<th>therapist_free</th>
<th>therapist_pro</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>therapists_limit</code></td>
<td>1</td>
<td>ilimitado</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>patients_limit</code></td>
<td>30</td>
<td>ilimitado</td>
<td>10</td>
<td>ilimitado</td>
</tr>
<tr>
<td><code>sessions_month_limit</code></td>
<td>100</td>
<td>ilimitado</td>
<td>40</td>
<td>ilimitado</td>
</tr>
<tr>
<td><code>secretary_enabled</code></td>
<td>false</td>
<td>true</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>reports_enabled</code></td>
<td>false</td>
<td>true</td>
<td>false</td>
<td>true</td>
</tr>
<tr>
<td><code>reminders_enabled</code></td>
<td>false</td>
<td>true</td>
<td>false</td>
<td>true</td>
</tr>
<tr>
<td><code>patient_portal_enabled</code></td>
<td>true</td>
<td>true</td>
<td>true</td>
<td>true</td>
</tr>
</tbody>
</table>
<div class="warn">
<strong>Observação:</strong> nomes de entitlements dependem da sua tabela de <code>features</code> (se existir).
A lógica do seeder abaixo separa “chaves sugeridas” da implementação final.
</div>
</section>
<section class="section" id="7-precos">
<h2>7. Preços (MVP) e vigência</h2>
<h3>7.1 Preços sugeridos</h3>
<div class="card">
<ul>
<li><strong>clinic_free</strong>: Grátis (sem preço, ou <code>0</code> se o front exigir número)</li>
<li><strong>clinic_pro</strong>: mensal R$ 149 (<code>14900</code>), anual R$ 1490 (<code>149000</code>)</li>
<li><strong>therapist_free</strong>: Grátis (sem preço, ou <code>0</code>)</li>
<li><strong>therapist_pro</strong>: mensal R$ 49 (<code>4900</code>), anual R$ 490 (<code>49000</code>)</li>
</ul>
</div>
<h3>7.2 Regras de vigência</h3>
<div class="rule">
<strong>Regra recomendada:</strong> 1 preço vigente por <code>(plan_id, interval, currency)</code>.
Para prevenir inconsistência, criar índice único parcial.
</div>
<pre><code>create unique index if not exists uq_plan_price_active
on plan_prices (plan_id, interval, currency)
where is_active = true and active_to is null;</code></pre>
<div class="danger">
<strong>Anti-padrão:</strong> encerrar todos preços e esquecer de inserir os novos. Resultado: <code>v_public_pricing</code> com <code>null</code>.
</div>
</section>
<section class="section" id="8-seeder">
<h2>8. Seeder (nova instalação) — SQL idempotente</h2>
<div class="ok">
<strong>Objetivo do seeder:</strong> instalar (1) planos, (2) metadata pública, (3) bullets, (4) preços PRO vigentes
e (opcional) (5) entitlements iniciais.
O script deve ser <strong>idempotente</strong>: rodar várias vezes sem duplicar registros.
</div>
<h3>8.1 Convenções do seeder</h3>
<ul>
<li>Usar <code>plans.key</code> como chave estável (única). A view pública expõe isso como <code>plan_key</code>.</li>
<li>Para inserts, preferir <code>insert ... on conflict ... do update</code> quando houver unique constraint.</li>
<li>Para preços: encerrar preço vigente anterior e inserir um novo (ou atualizar, conforme sua política).</li>
<li>Manter <code>source='manual'</code> no MVP (provider pode entrar depois com Stripe).</li>
</ul>
<h3>8.2 Seeder completo (MVP)</h3>
<pre><code>-- ============================================================
-- SEEDER — BILLING (MVP) • SCHEMA REAL (confirmado)
-- Planos finais: clinic_free, clinic_pro, therapist_free, therapist_pro
-- Observação: v_public_pricing expõe (plan_key/plan_target), mas na tabela base é (plans.key / plans.target).
-- ============================================================
-- 0) Proteção: 1 preço vigente por (plan_id, interval, currency)
create unique index if not exists uq_plan_price_active
on plan_prices (plan_id, interval, currency)
where is_active = true and active_to is null;
-- 1) Plans (public.plans) — usa colunas reais: key, name, target
insert into plans (key, name, description, is_active, price_cents, currency, billing_interval, target)
values
(&#x27;clinic_free&#x27;, &#x27;CLINIC FREE&#x27;, &#x27;Plano gratuito para clínicas iniciarem.&#x27;, true, 0, &#x27;BRL&#x27;, &#x27;month&#x27;, &#x27;clinic&#x27;),
(&#x27;clinic_pro&#x27;, &#x27;CLINIC PRO&#x27;, &#x27;Plano completo para clínicas.&#x27;, true, 14900, &#x27;BRL&#x27;, &#x27;month&#x27;, &#x27;clinic&#x27;),
(&#x27;therapist_free&#x27;, &#x27;THERAPIST FREE&#x27;, &#x27;Plano gratuito para terapeutas.&#x27;, true, 0, &#x27;BRL&#x27;, &#x27;month&#x27;, &#x27;therapist&#x27;),
(&#x27;therapist_pro&#x27;, &#x27;THERAPIST PRO&#x27;, &#x27;Plano completo para terapeutas.&#x27;, true, 4900, &#x27;BRL&#x27;, &#x27;month&#x27;, &#x27;therapist&#x27;)
on conflict (key) do update
set name = excluded.name,
description = excluded.description,
is_active = excluded.is_active,
price_cents = excluded.price_cents,
currency = excluded.currency,
billing_interval = excluded.billing_interval,
target = excluded.target;
-- 2) Plan public (public.plan_public) — metadata de pricing
with p as (
select id, key from plans
where key in (&#x27;clinic_free&#x27;,&#x27;clinic_pro&#x27;,&#x27;therapist_free&#x27;,&#x27;therapist_pro&#x27;)
)
insert into plan_public (plan_id, public_name, public_description, badge, is_featured, is_visible, sort_order)
select
id,
case key
when &#x27;clinic_free&#x27; then &#x27;Clínica — Free&#x27;
when &#x27;clinic_pro&#x27; then &#x27;Clínica — PRO&#x27;
when &#x27;therapist_free&#x27; then &#x27;Terapeuta — Free&#x27;
when &#x27;therapist_pro&#x27; then &#x27;Terapeuta — PRO&#x27;
end,
case key
when &#x27;clinic_free&#x27; then &#x27;Para clínicas pequenas começarem sem cartão.&#x27;
when &#x27;clinic_pro&#x27; then &#x27;Para clínicas que querem recursos completos.&#x27;
when &#x27;therapist_free&#x27; then &#x27;Para começar e organizar sua prática.&#x27;
when &#x27;therapist_pro&#x27; then &#x27;Para expandir com automações e escala.&#x27;
end,
case key
when &#x27;clinic_free&#x27; then &#x27;Grátis&#x27;
when &#x27;therapist_free&#x27; then &#x27;Grátis&#x27;
else null
end,
case key
when &#x27;clinic_pro&#x27; then true
when &#x27;therapist_pro&#x27; then true
else false
end,
true,
case key
when &#x27;clinic_free&#x27; then 10
when &#x27;clinic_pro&#x27; then 20
when &#x27;therapist_free&#x27; then 10
when &#x27;therapist_pro&#x27; then 20
end
from p
on conflict (plan_id) do update
set public_name = excluded.public_name,
public_description = excluded.public_description,
badge = excluded.badge,
is_featured = excluded.is_featured,
is_visible = excluded.is_visible,
sort_order = excluded.sort_order;
-- 3) Bullets (public.plan_public_bullets) — reset simples para MVP
delete from plan_public_bullets
where plan_id in (select id from plans where key in (&#x27;clinic_free&#x27;,&#x27;clinic_pro&#x27;,&#x27;therapist_free&#x27;,&#x27;therapist_pro&#x27;));
insert into plan_public_bullets (plan_id, text, highlight, sort_order)
values
((select id from plans where key=&#x27;clinic_free&#x27;), &#x27;1 terapeuta incluído&#x27;, false, 10),
((select id from plans where key=&#x27;clinic_free&#x27;), &#x27;Até 30 pacientes&#x27;, false, 20),
((select id from plans where key=&#x27;clinic_free&#x27;), &#x27;Até 100 sessões/mês&#x27;, false, 30),
((select id from plans where key=&#x27;clinic_pro&#x27;), &#x27;Terapeutas ilimitados&#x27;, true, 10),
((select id from plans where key=&#x27;clinic_pro&#x27;), &#x27;Pacientes ilimitados&#x27;, true, 20),
((select id from plans where key=&#x27;clinic_pro&#x27;), &#x27;Relatórios e lembretes&#x27;, false, 30),
((select id from plans where key=&#x27;therapist_free&#x27;), &#x27;Até 10 pacientes&#x27;, false, 10),
((select id from plans where key=&#x27;therapist_free&#x27;), &#x27;Até 40 sessões/mês&#x27;, false, 20),
((select id from plans where key=&#x27;therapist_free&#x27;), &#x27;Portal do paciente&#x27;, false, 30),
((select id from plans where key=&#x27;therapist_pro&#x27;), &#x27;Pacientes ilimitados&#x27;, true, 10),
((select id from plans where key=&#x27;therapist_pro&#x27;), &#x27;Sessões ilimitadas&#x27;, true, 20),
((select id from plans where key=&#x27;therapist_pro&#x27;), &#x27;Relatórios e lembretes&#x27;, false, 30);
-- 4) Preços vigentes (public.plan_prices) — somente PRO
do $$
declare
v_clinic_pro uuid;
v_therapist_pro uuid;
begin
select id into v_clinic_pro from plans where key=&#x27;clinic_pro&#x27;;
select id into v_therapist_pro from plans where key=&#x27;therapist_pro&#x27;;
update plan_prices
set is_active = false, active_to = now()
where plan_id in (v_clinic_pro, v_therapist_pro)
and is_active = true
and active_to is null;
insert into plan_prices (plan_id, currency, interval, amount_cents, is_active, active_from, active_to, source, provider, provider_price_id)
values
(v_clinic_pro, &#x27;BRL&#x27;, &#x27;month&#x27;, 14900, true, now(), null, &#x27;manual&#x27;, null, null),
(v_clinic_pro, &#x27;BRL&#x27;, &#x27;year&#x27;, 149000, true, now(), null, &#x27;manual&#x27;, null, null),
(v_therapist_pro, &#x27;BRL&#x27;, &#x27;month&#x27;, 4900, true, now(), null, &#x27;manual&#x27;, null, null),
(v_therapist_pro, &#x27;BRL&#x27;, &#x27;year&#x27;, 49000, true, now(), null, &#x27;manual&#x27;, null, null);
exception
when unique_violation then
raise notice &#x27;Preço vigente já existe para algum (plan_id, interval, currency).&#x27;;
end $$;
-- 5) (Opcional) Integridade: impedir apagar plano em uso
-- A FK subscriptions.plan_id -&gt; plans.id deve estar com ON DELETE RESTRICT.
-- Se precisar aplicar:
-- alter table public.subscriptions drop constraint if exists subscriptions_plan_id_fkey;
-- alter table public.subscriptions add constraint subscriptions_plan_id_fkey
-- foreign key (plan_id) references public.plans(id) on delete restrict;
-- 6) Validação final (deve retornar 4 planos visíveis)
select plan_key, plan_name, plan_target, monthly_cents, yearly_cents
from v_public_pricing
where is_visible = true
order by plan_target, sort_order, plan_key;</code></pre>
<div class="warn">
<strong>Nota de adaptação:</strong> o seeder acima assume certas colunas (ex.: <code>plans.plan_key</code>, <code>plans.plan_target</code>, <code>plans.is_active</code>, <code>plan_public.*</code>).
Se o seu schema tiver nomes diferentes, ajuste no primeiro uso e depois mantenha como “padrão oficial”.
</div>
</section>
<section class="section" id="8b-entitlements">
<h2>8B. Entitlements — Schema real (plan_features)</h2>
<p>
O MVP usa <code>plan_features</code> como tabela de ligação entre plano e feature. O schema confirmado é:
<code>(plan_id uuid NOT NULL, feature_id uuid NOT NULL, enabled boolean NOT NULL default true, limits jsonb NULL)</code>.
</p>
<div class="rule">
<strong>Padrão recomendado para limits (jsonb):</strong> padronizar chaves por tipo de limite para evitar ambiguidade no front/back.
Sugestão:
<code>{"max": 30}</code> (limite absoluto),
<code>{"per_month": 40}</code> (por período),
<code>{"max_users": 1}</code> (limite de assentos),
e manter <code>enabled</code> como flag binária.
</div>
<div class="warn">
<strong>Pré-requisito:</strong> para seedar entitlements, é necessário listar/definir as features na tabela de features (ex.: <code>features</code>).
Este documento mantém os limites do MVP como referência de produto; o seeder de <code>plan_features</code> deve mapear essas chaves para <code>feature_id</code> reais.
</div>
<h3>Template (exemplo) — como gravar limites</h3>
<pre><code>-- Exemplo: habilitar feature X com limite max=30 para clinic_free
insert into plan_features (plan_id, feature_id, enabled, limits)
values (
(select id from plans where key='clinic_free'),
'FEATURE_UUID_AQUI',
true,
'{"max": 30}'::jsonb
);</code></pre>
</section>
<section class="section" id="8c-regras-negocio">
<h2>8C. Regras de negócio confirmadas no banco</h2>
<div class="ok">
<strong>Regra confirmada:</strong> inserir subscription de <code>clinic_*</code> exige <code>tenant_id</code>.
Em testes, uma tentativa de inserir assinatura de clínica sem tenant resultou em erro:
<em>“Assinatura clinic exige tenant_id.”</em>
</div>
<div class="rule">
<strong>Consequência:</strong> assinatura de clínica é “por tenant”; assinatura de terapeuta pode ser por <code>tenant_id</code> ou <code>user_id</code>,
conforme sua arquitetura — mas o banco já impõe pelo menos o caso de clínica.
</div>
</section>
<section class="section" id="9-onboarding">
<h2>9. Onboarding & Upgrade (fluxo)</h2>
<h3>9.1 Onboarding (criação de tenant)</h3>
<ul>
<li>Ao criar um tenant <code>clinic</code> → atribuir automaticamente <code>clinic_free</code>.</li>
<li>Ao criar um tenant <code>therapist</code> → atribuir automaticamente <code>therapist_free</code>.</li>
<li>O plano deve ser a fonte de verdade para habilitar recursos (entitlements store).</li>
</ul>
<h3>9.2 Upgrade</h3>
<div class="rule">
Upgrade é troca de plano na assinatura: <code>*_free → *_pro</code>.
O sistema deve invalidar entitlements e atualizar cache (via <code>entitlements_invalidation</code> ou mecanismo equivalente).
</div>
<h3>9.3 Downgrade/expiração</h3>
<p>
No MVP, a regra segura é: ao expirar, <strong>bloquear novas criações premium</strong>,
mas <strong>não apagar dados</strong>. Apenas retira capacidade.
</p>
</section>
<section class="section" id="10-runbook">
<h2>10. Operação (runbook rápido)</h2>
<div class="card">
<h3>Incidente comum: Pricing mostra preços nulos</h3>
<ol style="margin:10px 0 0 18px; color: var(--muted); line-height:1.65;">
<li>Rodar <code>select * from v_public_pricing;</code></li>
<li>Rodar <code>select * from plan_prices where plan_id = ... order by created_at desc;</code></li>
<li>Confirmar existência de preço vigente: <code>is_active=true</code> e <code>active_to is null</code></li>
<li>Se não existir, inserir preços PRO vigentes (month/year) e validar view novamente.</li>
</ol>
</div>
<div class="card" style="margin-top:12px;">
<h3>Incidente comum: Plano aparece errado para um tenant</h3>
<ol style="margin:10px 0 0 18px; color: var(--muted); line-height:1.65;">
<li>Verificar <code>v_tenant_active_subscription</code> para o tenant em questão.</li>
<li>Verificar se o plano tem <code>plan_target</code> correto.</li>
<li>Verificar se o guard/menu não está inferindo plano do role (anti-padrão).</li>
<li>Invalidar entitlements e reavaliar.</li>
</ol>
</div>
</section>
<section class="section" id="11-qa">
<h2>11. Checklist de QA</h2>
<ul>
<li><strong>Seeder</strong>: rodar duas vezes e confirmar que não duplica registros.</li>
<li><strong>Pricing</strong>: <code>v_public_pricing</code> retorna 4 planos, com preços preenchidos para PRO.</li>
<li><strong>Upgrade</strong>: trocar plano e confirmar mudança de entitlements no runtime.</li>
<li><strong>FREE</strong>: criação de tenant atribui automaticamente plano free correto.</li>
<li><strong>Target</strong>: clínica nunca recebe plano therapist (e vice-versa).</li>
<li><strong>Vigência</strong>: inserir novo preço e confirmar que o antigo foi encerrado (active_to preenchido).</li>
</ul>
</section>
<section class="section" id="12-prompt">
<h2>12. Prompt Mestre — Continuidade do Billing (Planos/Assinaturas)</h2>
<div class="rule">
Sempre que iniciar um novo chat sobre Billing/Planos, copie e cole este prompt.
Ele representa o estado oficial do domínio e da estrutura do banco para o MVP.
</div>
<pre><code>
Estou desenvolvendo um SaaS clínico multi-tenant usando Supabase (Postgres + RLS + Views)
com planos e assinaturas.
══════════════════════════════════════
📦 Domínio: Billing / Planos
══════════════════════════════════════
Decisões do MVP:
- Tudo começa grátis (clinic e therapist).
- Paciente não tem plano (portal do paciente é feature do plano do therapist/clinic).
- Plano (billing) NÃO é role (RBAC). Role dirige menus/rotas; plano dirige features/limites.
- Planos por target: clinic e therapist.
Catálogo de planos (MVP):
- clinic_free
- clinic_pro
- therapist_free
- therapist_pro
Views fonte de verdade:
- v_public_pricing (tela de preços)
- v_plan_active_prices (infra)
- v_tenant_active_subscription (gating por tenant)
- v_subscription_health (debug)
Tabelas principais:
- plans (colunas reais: key, target, ...)
- plan_prices (tem vigência; preço vigente: is_active=true e active_to is null; a UI usa v_plan_active_prices)
- plan_public + plan_public_bullets (marketing)
- plan_features (entitlements)
- subscriptions (+ events, intents)
- entitlements_invalidation
Preços sugeridos (MVP):
- clinic_pro: 14900/mês e 149000/ano (BRL)
- therapist_pro: 4900/mês e 49000/ano (BRL)
- free: grátis (pode manter sem preços)
Problema já observado:
- v_public_pricing retornou null quando plan_prices tinha histórico mas não tinha preço vigente.
Estado atual (confirmado):
- Apenas 4 planos existem (clinic_free/clinic_pro/therapist_free/therapist_pro)
Objetivo do próximo passo:
- Seedar plan_features (entitlements) mapeando features -> feature_id e limits jsonb para nova instalação com os 4 planos + public metadata + preços PRO vigentes.
</code></pre>
<div class="ok">
Este prompt deve ser tratado como contexto estrutural completo do Billing no MVP.
Qualquer solução proposta deve respeitar essa organização.
</div>
</section>
<section class="section" id="13-tags">
<h2>Tags</h2>
<span class="pill">#Billing</span>
<span class="pill">#Planos</span>
<span class="pill">#Pricing</span>
<span class="pill">#Seeder</span>
<span class="pill">#Supabase</span>
<span class="pill">#Postgres</span>
<span class="pill">#MultiTenant</span>
<span class="pill">#Entitlements</span>
<span class="pill">#Subscriptions</span>
<span class="pill">#v_public_pricing</span>
<span class="pill">#MVP</span>
</section>
</main>
<footer>
Agência PSI • Documentação interna • Billing (Planos/Assinaturas/Seeder)
</footer>
</div>
</body>
</html>

BIN
docs/specs/spec-v2.docx Normal file

Binary file not shown.

BIN
docs/specs/spec-wizard.docx Normal file

Binary file not shown.

View File

@@ -0,0 +1,612 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Relatório Técnico — Sessão de Correção: Subscription Health & Entitlements (2026-03-01) | Agência PSI</title>
<style>
:root{
--bg0:#f6f8fc;
--bg1:#eef2f8;
--panel:rgba(255,255,255,.78);
--panel2:rgba(255,255,255,.92);
--border:rgba(15,23,42,.10);
--text:rgba(15,23,42,.92);
--muted:rgba(15,23,42,.70);
--muted2:rgba(15,23,42,.56);
--accent:#2563eb;
--accent2:#4f46e5;
--warn:#b45309;
--danger:#b91c1c;
--ok:#047857;
--shadow: 0 18px 60px rgba(2,6,23,.10);
--radius: 16px;
--radius2: 22px;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
*{box-sizing:border-box;}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
background: radial-gradient(1200px 600px at 10% 0%, rgba(79,70,229,.10), transparent 55%),
radial-gradient(1100px 500px at 90% 10%, rgba(37,99,235,.10), transparent 55%),
linear-gradient(180deg, var(--bg0), var(--bg1));
color:var(--text);
}
a{color:inherit; text-decoration:none;}
a:hover{text-decoration:underline;}
.layout{
display:grid;
grid-template-columns: 320px 1fr;
gap: 20px;
max-width: 1320px;
margin: 0 auto;
padding: 28px 18px 42px;
}
header{
grid-column: 1 / -1;
padding: 18px;
border: 1px solid var(--border);
background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(255,255,255,.72));
border-radius: var(--radius2);
box-shadow: var(--shadow);
}
.kicker{
font-size:12px;
letter-spacing:.08em;
text-transform:uppercase;
color:var(--muted2);
margin:0 0 8px;
}
h1{
margin:0 0 8px;
font-size:30px;
letter-spacing:-0.02em;
}
.subtitle{
margin:0;
color:var(--muted);
max-width:980px;
line-height:1.55;
font-size:14px;
}
.meta-row{
margin-top:12px;
display:flex;
flex-wrap:wrap;
gap:10px;
align-items:center;
}
.pill{
display:inline-flex;
align-items:center;
gap:8px;
padding:8px 12px;
border-radius:999px;
border:1px solid var(--border);
background:rgba(255,255,255,.72);
color:var(--muted);
font-size:12px;
}
.dot{
width:8px;height:8px;border-radius:50%;
background:var(--accent);
box-shadow:0 0 0 4px rgba(37,99,235,.12);
}
aside{
position:sticky;
top:18px;
align-self:start;
border:1px solid var(--border);
background:var(--panel2);
border-radius:var(--radius);
box-shadow:var(--shadow);
overflow:hidden;
}
.toc-head{
padding:14px;
border-bottom:1px solid var(--border);
background:rgba(15,23,42,.02);
}
.toc-title{ margin:0 0 6px; font-weight:700; font-size:14px; }
.toc-sub{ margin:0; color:var(--muted); font-size:12px; line-height:1.45; }
nav{ padding: 10px 6px 14px; }
nav a{
display:block;
padding:10px 12px;
margin:4px 6px;
border-radius:12px;
color:var(--muted);
font-size:13px;
border:1px solid transparent;
}
nav a:hover{
background:rgba(37,99,235,.06);
border-color:rgba(37,99,235,.12);
color:var(--text);
text-decoration:none;
}
.nav-sec{
margin:10px 12px 6px;
color:var(--muted2);
font-size:11px;
letter-spacing:.08em;
text-transform:uppercase;
}
main{
border:1px solid var(--border);
background:var(--panel);
border-radius:var(--radius2);
box-shadow:var(--shadow);
overflow:hidden;
}
.content{ padding: 18px 18px 22px; }
.section{
padding: 18px;
border:1px solid var(--border);
border-radius: var(--radius2);
background: rgba(255,255,255,.80);
margin-bottom: 16px;
}
.section h2{
margin:0 0 10px;
font-size:18px;
letter-spacing:-0.01em;
}
.section h3{
margin:14px 0 8px;
font-size:14px;
}
.section p, .section li{
color:var(--muted);
line-height:1.65;
font-size:13.5px;
}
.grid{
display:grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 980px){
.layout{ grid-template-columns: 1fr; }
aside{ position:relative; top:auto; }
.grid{ grid-template-columns: 1fr; }
}
.callout{
border-radius: 16px;
padding: 12px 12px 12px 14px;
border:1px solid var(--border);
background: rgba(15,23,42,.02);
margin-top: 10px;
}
.callout strong{color:var(--text);}
.callout.ok{ border-left: 4px solid var(--ok); background: rgba(4,120,87,.06); }
.callout.warn{ border-left: 4px solid var(--warn); background: rgba(180,83,9,.08); }
.callout.danger{ border-left: 4px solid var(--danger); background: rgba(185,28,28,.08); }
.callout.info{ border-left: 4px solid var(--accent); background: rgba(37,99,235,.08); }
pre{
margin: 10px 0 0;
padding: 14px;
background: #0b1220;
color:#e2e8f0;
border-radius: 16px;
overflow:auto;
border: 1px solid rgba(226,232,240,.08);
}
code{ font-family: var(--mono); font-size:12.5px; }
.kbd{
font-family: var(--mono);
font-size: 12px;
padding: 2px 6px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(255,255,255,.65);
color: var(--text);
}
.hr{
height:1px;
background: var(--border);
margin: 12px 0;
}
footer{
margin-top: 12px;
padding: 14px 18px 18px;
color: var(--muted2);
font-size: 12px;
border-top: 1px solid var(--border);
background: rgba(255,255,255,.70);
}
.small{ font-size:12px; color:var(--muted2); }
</style>
</head>
<body>
<div class="layout">
<header>
<p class="kicker">Relatório Técnico • Billing Health • Agência PSI</p>
<h1>Subscription Health & Entitlements — Sessão de Correção (2026-03-01)</h1>
<p class="subtitle">Este documento registra, de forma minuciosa e operacional, a sessão de diagnóstico e correção dos problemas
na página <strong>Saúde das Assinaturas</strong> (Subscription Health) e no pipeline de <strong>Entitlements</strong>.
O objetivo é permitir que qualquer programador entenda o incidente, replique o diagnóstico e aplique correções
com segurança, mesmo sem ter acompanhado a conversa original.</p>
<div class="meta-row">
<span class="pill"><span class="dot"></span><strong>Estado:</strong> resolvido e hardenizado</span>
<span class="pill"><strong>Atualizado:</strong> 2026-03-01 11:46:44 UTC</span>
<span class="pill"><strong>Stack:</strong> Supabase + Postgres + Vue/PrimeVue</span>
</div>
</header>
<aside>
<div class="toc-head">
<div class="toc-title">Sumário</div>
<p class="toc-sub">Leitura rápida com passos reproduzíveis (SQL + patches + checklist).</p>
</div>
<nav>
<div class="nav-sec">Visão geral</div>
<a href="#01">Resumo executivo</a>
<a href="#02">Escopo e componentes</a>
<a href="#03">Sintomas e evidências</a>
<div class="nav-sec">Diagnóstico</div>
<a href="#04">Causa raiz</a>
<a href="#05">SQLs de diagnóstico</a>
<div class="nav-sec">Correções</div>
<a href="#06">Patches aplicados</a>
<a href="#07">Hardening</a>
<div class="nav-sec">Validação</div>
<a href="#08">Checklist pós-correção</a>
<div class="nav-sec">Contexto</div>
<a href="#09">Notas de front-end</a>
<a href="#10">Linha do tempo</a>
</nav>
</aside>
<main>
<div class="content">
<section class="section" id="01">
<h2>1. Resumo executivo</h2>
<p>
<strong>Sintoma principal:</strong> a tela <em>Saúde das Assinaturas</em> exibia divergências e a coluna <strong>Owner</strong> aparecia vazia
(linhas com <code>owner_id = NULL</code>). Além disso, os botões <strong>Fix</strong> e <strong>Fix All</strong> falhavam.
</p>
<div class="callout danger">
<strong>Impacto:</strong> a ferramenta de diagnóstico do Billing ficou pouco confiável e os reparos automáticos não executavam, impedindo
correções rápidas após mudanças de plano/feature.
</div>
<div class="callout ok">
<strong>Resultado final:</strong> view de entitlements corrigida (filtra <code>subscriptions.status = 'active'</code> e exclui NULL),
funções RPC alinhadas ao schema atual (<code>subscriptions.user_id</code>), dados inválidos removidos e constraints/índices adicionados
para impedir regressões.
</div>
</section>
<section class="section" id="02">
<h2>2. Escopo e componentes envolvidos</h2>
<div class="grid">
<div class="callout">
<strong>View de saúde</strong>
<p><code>public.v_subscription_feature_mismatch</code> — compara o esperado (plan_features do plano ativo) com o atual (entitlements).</p>
</div>
<div class="callout">
<strong>Entitlements agregados</strong>
<p><code>public.owner_feature_entitlements</code><em>VIEW</em> agregada (sources + limits_list), derivada de <code>subscriptions</code> e <code>tenant_modules</code>.</p>
</div>
<div class="callout">
<strong>Rotinas de reparo</strong>
<p><code>public.rebuild_owner_entitlements(uuid)</code> e <code>public.fix_all_subscription_mismatches()</code>.</p>
</div>
<div class="callout">
<strong>Tabelas de configuração</strong>
<p><code>plans</code>, <code>features</code>, <code>plan_features</code>, <code>module_features</code>, <code>tenant_modules</code>, <code>subscriptions</code>.</p>
</div>
</div>
</section>
<section class="section" id="03">
<h2>3. Sintomas observados e evidências</h2>
<p>Foram observados os seguintes indícios no banco:</p>
<ul>
<li>View <code>v_subscription_feature_mismatch</code> retornando <code>owner_id = NULL</code> tanto em <em>missing</em> quanto em <em>unexpected</em>.</li>
<li>Contagem estável em 4/4 divergências, mesmo após tentativa de reparo.</li>
<li>Existência de uma <code>subscription</code> com <code>status='active'</code> e <code>user_id = NULL</code> (dado inválido).</li>
<li>Falha de execução do FixAll com erro de coluna inexistente: <code>subscriptions.owner_id</code> (schema drift).</li>
</ul>
<div class="callout warn">
<strong>Nota de leitura:</strong> ao ver <code>owner_id = NULL</code> em divergências, trate como anomalia de dados ou view/joins permissivos.
Na prática, “owner nulo” não é um caso de negócio — é um caso de <em>integridade</em>.
</div>
</section>
<section class="section" id="04">
<h2>4. Diagnóstico e causa raiz</h2>
<h3>4.1 Causa raiz #1 — Schema drift nas funções RPC</h3>
<p>
As funções de reparo estavam escritas para um schema anterior, usando <code>subscriptions.owner_id</code>. No schema atual, o owner do contexto
“terapeuta” é <code>subscriptions.user_id</code>. Isso quebrou:
</p>
<ul>
<li><strong>Fix owner</strong>: falha ao buscar o plano ativo do owner.</li>
<li><strong>Fix all</strong>: falha ao iterar owners e chamar o rebuild.</li>
</ul>
<div class="callout danger">
<strong>Erro observado:</strong> <code>column "owner_id" does not exist</code> (hint citando <code>subscriptions.user_id</code>).
</div>
<h3>4.2 Causa raiz #2 — View de entitlements agregados não filtrava status</h3>
<p>
A view <code>owner_feature_entitlements</code> agregava a fonte “plan” sem filtrar <code>subscriptions.status</code>, permitindo que uma subscription
<em>inactive</em> com <code>user_id NULL</code> continuasse “vazando” entitlements com <code>owner_id NULL</code> para o sistema.
</p>
<h3>4.3 Causa raiz #3 — Dado inválido</h3>
<p>
Foi identificado um registro em <code>subscriptions</code> com <code>user_id NULL</code>. Mesmo após torná-lo <em>inactive</em>, ele continuava contaminando
a view (por ausência do filtro de status).
</p>
</section>
<section class="section" id="05">
<h2>5. SQLs usados no diagnóstico (playbook)</h2>
<p>Use este bloco para reproduzir o diagnóstico com segurança.</p>
<h3>5.1 Ver divergências e amostras</h3>
<pre><code>select mismatch_type, count(*) as qtd
from public.v_subscription_feature_mismatch
group by 1
order by 2 desc;
select owner_id, feature_key, mismatch_type
from public.v_subscription_feature_mismatch
order by owner_id nulls first, feature_key
limit 50;</code></pre>
<h3>5.2 Encontrar subscriptions inválidas (user_id nulo)</h3>
<pre><code>select id, user_id, plan_id, status, created_at
from public.subscriptions
where user_id is null
order by created_at desc;</code></pre>
<h3>5.3 Entender a origem dos entitlements agregados</h3>
<pre><code>select pg_get_viewdef('public.owner_feature_entitlements'::regclass, true) as view_sql;</code></pre>
<h3>5.4 Verificar tenant_modules inválidos (owner_id nulo)</h3>
<pre><code>select count(*) as qtd
from public.tenant_modules
where status = 'active' and owner_id is null;</code></pre>
</section>
<section class="section" id="06">
<h2>6. Correções aplicadas no banco (patches)</h2>
<h3>6.1 Patch: rebuild_owner_entitlements (owner = subscriptions.user_id)</h3>
<p>
Ajuste para buscar o plano ativo por <code>subscriptions.user_id</code> e reconstruir entitlements com base em <code>plan_features</code>.
</p>
<pre><code>create or replace function public.rebuild_owner_entitlements(p_owner_id uuid)
returns void
language plpgsql
security definer
as $$
declare
v_plan_id uuid;
begin
select s.plan_id
into v_plan_id
from public.subscriptions s
where s.user_id = p_owner_id
and s.status = 'active'
order by s.created_at desc
limit 1;
delete from public.owner_feature_entitlements e
where e.owner_id = p_owner_id;
if v_plan_id is null then
return;
end if;
insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list)
select
p_owner_id,
f.key,
array['plan'::text],
'{}'::jsonb
from public.plan_features pf
join public.features f on f.id = pf.feature_id
where pf.plan_id = v_plan_id;
end;
$$;</code></pre>
<div class="callout warn">
<strong>Importante:</strong> se <code>owner_feature_entitlements</code> for uma <em>VIEW</em> (como no ambiente desta sessão),
o <code>DELETE/INSERT</code> acima deve ser direcionado à <em>tabela base</em> real de entitlements, se existir.
Nesta sessão, a correção definitiva foi feita ajustando a view agregadora e limpando o dado inválido.
</div>
<h3>6.2 Patch: fix_all_subscription_mismatches (itera subscriptions.user_id)</h3>
<pre><code>create or replace function public.fix_all_subscription_mismatches()
returns void
language plpgsql
security definer
as $$
declare
r record;
begin
for r in
select distinct s.user_id as owner_id
from public.subscriptions s
where s.status = 'active'
and s.user_id is not null
loop
perform public.rebuild_owner_entitlements(r.owner_id);
end loop;
end;
$$;</code></pre>
<h3>6.3 Patch: owner_feature_entitlements (filtra status e NULLs)</h3>
<pre><code>create or replace view public.owner_feature_entitlements as
with base as (
select
s.user_id as owner_id,
f.key as feature_key,
pf.limits,
'plan'::text as source
from public.subscriptions s
join public.plan_features pf
on pf.plan_id = s.plan_id
and pf.enabled = true
join public.features f
on f.id = pf.feature_id
where s.status = 'active'
and s.user_id is not null
union all
select
tm.owner_id,
f.key as feature_key,
mf.limits,
'module'::text as source
from public.tenant_modules tm
join public.modules m
on m.id = tm.module_id
and m.is_active = true
join public.module_features mf
on mf.module_id = m.id
and mf.enabled = true
join public.features f
on f.id = mf.feature_id
where tm.status = 'active'
and tm.owner_id is not null
)
select
owner_id,
feature_key,
array_agg(distinct source) as sources,
jsonb_agg(limits) filter (where limits is not null) as limits_list
from base
group by owner_id, feature_key;</code></pre>
<h3>6.4 Limpeza do dado inválido (subscription com user_id NULL)</h3>
<pre><code>-- se for lixo de seed/teste (recomendado remover):
delete from public.subscriptions
where user_id is null;</code></pre>
</section>
<section class="section" id="07">
<h2>7. Hardening (constraints e índices recomendados)</h2>
<p>Após corrigir dados e views, aplique hardening para impedir regressões.</p>
<h3>7.1 subscriptions.user_id NOT NULL</h3>
<pre><code>alter table public.subscriptions
alter column user_id set not null;</code></pre>
<h3>7.2 Uma assinatura ativa por usuário</h3>
<pre><code>create unique index if not exists subscriptions_one_active_per_user
on public.subscriptions (user_id)
where status = 'active';</code></pre>
<h3>7.3 Índice de performance para consultas por owner/status</h3>
<pre><code>create index if not exists subscriptions_user_status_idx
on public.subscriptions (user_id, status, created_at desc);</code></pre>
<h3>7.4 tenant_modules.owner_id NOT NULL (decisão tomada nesta sessão)</h3>
<pre><code>alter table public.tenant_modules
alter column owner_id set not null;</code></pre>
<h3>7.5 Uniqueness e performance em plan_features / module_features</h3>
<pre><code>create unique index if not exists plan_features_plan_feature_ux
on public.plan_features (plan_id, feature_id);
create index if not exists plan_features_plan_enabled_idx
on public.plan_features (plan_id, enabled);
create unique index if not exists module_features_module_feature_ux
on public.module_features (module_id, feature_id);</code></pre>
<div class="callout info">
<strong>Regra prática:</strong> dados inválidos (NULL em owner) devem ser bloqueados na borda (constraints), não “corrigidos” no front.
</div>
</section>
<section class="section" id="08">
<h2>8. Verificação pós-correção (checklist)</h2>
<h3>8.1 Saúde deve zerar</h3>
<pre><code>select mismatch_type, count(*) as qtd
from public.v_subscription_feature_mismatch
group by 1
order by 2 desc;</code></pre>
<h3>8.2 Não pode haver owner nulo em subscriptions / tenant_modules ativos</h3>
<pre><code>select count(*) as subs_user_null
from public.subscriptions
where user_id is null;
select count(*) as tenant_modules_active_owner_null
from public.tenant_modules
where status='active' and owner_id is null;</code></pre>
<h3>8.3 Entitlements agregados não devem conter owner null</h3>
<pre><code>select owner_id, feature_key
from public.owner_feature_entitlements
where owner_id is null
limit 20;</code></pre>
<div class="callout ok">
<strong>OK final:</strong> todas as queries acima retornam 0 linhas (ou contagens zero).
</div>
</section>
<section class="section" id="09">
<h2>9. Notas de implementação no front-end (contexto)</h2>
<p>Durante a sessão, a UI foi ajustada para:</p>
<ul>
<li>Traduzir telas para PT-BR, melhorar títulos, descrições e mensagens.</li>
<li>Padronizar inputs com <code>FloatLabel</code> + <code>IconField</code> + <code>InputIcon</code>.</li>
<li>Adicionar confirmações e “alterações pendentes” em ações em massa (plan_features), evitando salvar por clique acidental.</li>
<li>Garantir que ações de correção (Fix/FixAll) reflitam erros reais (RPC quebrada vs dados inválidos).</li>
</ul>
<div class="callout warn">
<strong>Regra operacional:</strong> se a coluna Owner aparecer vazia, corrija no banco primeiro (dados/view),
antes de mexer no front.
</div>
</section>
<section class="section" id="10">
<h2>10. Linha do tempo da sessão (resumo)</h2>
<ul>
<li><strong>Detecção:</strong> tela “Saúde das Assinaturas” exibindo Owner vazio e divergências.</li>
<li><strong>Inspeção:</strong> <code>v_subscription_feature_mismatch</code> mostrava <code>owner_id NULL</code> em missing/unexpected.</li>
<li><strong>Erro crítico:</strong> FixAll falhava com <code>subscriptions.owner_id</code> inexistente.</li>
<li><strong>Correção #1:</strong> alinhar RPCs ao schema atual (<code>subscriptions.user_id</code>).</li>
<li><strong>Correção #2:</strong> identificar que <code>owner_feature_entitlements</code> é VIEW e filtrar <code>status='active'</code>.</li>
<li><strong>Correção #3:</strong> remover subscription inválida com <code>user_id NULL</code>.</li>
<li><strong>Hardening:</strong> constraints e índices para prevenir regressões.</li>
</ul>
<div class="callout info">
<strong>Atualizado:</strong> 2026-03-01 11:46:44 UTC
</div>
</section>
</div>
<footer>
<div><strong>Agência PSI — Relatório Técnico (Billing Health)</strong></div>
<div class="small">Documento operacional inspirado no “Documento Mestre Billing v2.0”. Atualizado em 2026-03-01 11:46:44 UTC.</div>
</footer>
</main>
</div>
</body>
</html>

243
docs/whatsapp.md Normal file
View File

@@ -0,0 +1,243 @@
# 📱 Disparando WhatsApp Local — AgênciaPsi
Guia completo para rodar o sistema de lembretes por WhatsApp no ambiente local de desenvolvimento.
---
## 🧱 O que você precisa ter rodando
| Serviço | Como subir | Porta |
|---|---|---|
| Supabase local | `npx supabase start` | 54321 |
| Evolution API | Docker Desktop | 8080 |
---
## 🚀 Passo 1 — Subir o Supabase local
Abre o PowerShell na pasta do projeto e roda:
```powershell
cd D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai
npx supabase start
```
Aguarda aparecer as URLs e credenciais. Confirma que está rodando acessando:
```
http://127.0.0.1:54323
```
---
## 🐳 Passo 2 — Subir a Evolution API
Abre o Docker Desktop e confirma que está com o status **Engine running**.
Abre outro PowerShell e roda:
```powershell
cd D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai\evolution-api
docker compose up -d
```
Confirma que está rodando acessando:
```
http://localhost:8080
```
Deve retornar:
```json
{"status":200,"message":"Welcome to the Evolution API, it is working!"}
```
---
## 📱 Passo 3 — Conectar o WhatsApp
Acessa o painel da Evolution API:
```
http://localhost:8080/manager
```
- Loga com a API key: `minha_chave_123`
- Verifica se a instância `agenciapsi-teste` está com status **Connected**
- Se estiver **Disconnected**, clica na instância → **Get QR Code** → escaneia com o celular
**Para escanear no celular:**
WhatsApp → ⋮ (três pontinhos) → Aparelhos conectados → Conectar um aparelho
---
## ⚡ Passo 4 — Subir a Edge Function
Abre outro PowerShell e roda:
```powershell
cd D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai
npx supabase functions serve process-notification-queue --no-verify-jwt
```
Aguarda aparecer:
```
- http://127.0.0.1:54321/functions/v1/process-notification-queue
```
---
## 🗄️ Passo 5 — Popular a fila manualmente
Acessa o Supabase Studio em `http://127.0.0.1:54323`, vai em **SQL Editor** e roda:
```sql
-- Popula a fila com sessões das próximas 48h
select populate_notification_queue();
-- Verifica o que foi gerado
select id, status, scheduled_at, channel, recipient_address, resolved_vars
from notification_queue
order by created_at desc
limit 10;
```
Se precisar corrigir o número do destinatário:
```sql
update notification_queue
set recipient_address = '5516999999999' -- coloca o número real aqui
where status = 'pendente';
```
Se precisar reenviar um item que falhou:
```sql
update notification_queue
set status = 'pendente'
where id = 'cole-o-uuid-aqui';
```
---
## 📤 Passo 6 — Disparar a fila
Abre outro PowerShell e roda:
```powershell
Invoke-WebRequest `
-Uri "http://127.0.0.1:54321/functions/v1/process-notification-queue" `
-Method POST `
-Headers @{"Authorization"="Bearer sb_secret_N7UND0UgjKTVK-Uodkm0Hg_xSvEMPvz"} `
-TimeoutSec 30 `
| Select-Object -ExpandProperty Content
```
**Resposta esperada:**
```json
{"processados":[{"id":"...","status":"enviado"}]}
```
---
## 🔄 Passo 7 — Simular o cron (disparo automático a cada 5 min)
Para simular o `pg_cron` localmente, cria um arquivo `cron-local.ps1` na raiz do projeto:
```powershell
while ($true) {
Write-Host "$(Get-Date) - Populando fila..."
Invoke-WebRequest `
-Uri "http://127.0.0.1:54321/functions/v1/process-notification-queue" `
-Method POST `
-Headers @{"Authorization"="Bearer sb_secret_N7UND0UgjKTVK-Uodkm0Hg_xSvEMPvz"} `
-TimeoutSec 30 `
| Select-Object -ExpandProperty Content
Write-Host "$(Get-Date) - Aguardando 5 minutos..."
Start-Sleep -Seconds 300
}
```
Roda com:
```powershell
cd D:\leonohama\AgenciaPsi.com.br\Sistema\agenciapsi-primesakai
.\cron-local.ps1
```
---
## ✅ Passo 8 — Verificar logs
No Supabase Studio, roda:
```sql
-- Ver histórico de envios
select id, status, channel, recipient_address, sent_at, failure_reason
from notification_logs
order by created_at desc
limit 10;
-- Ver status da fila
select status, count(*)
from notification_queue
group by status;
```
---
## 🧪 Teste rápido de envio direto (sem fila)
Para testar se o WhatsApp está funcionando sem passar pela fila:
```powershell
$body = '{"number":"5516999999999","text":"Teste direto AgênciaPsi!"}'
$headers = @{"apikey"="minha_chave_123"; "Content-Type"="application/json"}
Invoke-WebRequest `
-Uri "http://localhost:8080/message/sendText/agenciapsi-teste" `
-Method POST `
-Headers $headers `
-Body $body `
-TimeoutSec 30 `
| Select-Object -ExpandProperty Content
```
---
## ⚠️ Problemas comuns
| Problema | Causa | Solução |
|---|---|---|
| `Function not found` | Terminal na pasta errada | `cd` para a raiz do projeto antes de rodar o `functions serve` |
| `{"count":0}` no QR Code | Bug da versão | Usar imagem `evoapicloud/evolution-api:latest` |
| `Nenhum item na fila` | Item já processado ou com status diferente | Resetar com `update notification_queue set status = 'pendente'` |
| Timeout no envio | Redis não está rodando | Verificar se o container `evolution-redis` está up no Docker |
| `undefined/message/sendText/undefined` | Campos errados nas credenciais | Verificar se `credentials` tem `api_url` e `instance_name` |
---
## 📋 Resumo das credenciais locais
```
Supabase URL: http://127.0.0.1:54321
Supabase Studio: http://127.0.0.1:54323
Supabase Secret Key: sb_secret_N7UND0UgjKTVK-Uodkm0Hg_xSvEMPvz
Evolution API URL: http://localhost:8080
Evolution API Key: minha_chave_123
Instância WhatsApp: agenciapsi-teste
```
---
## 🚀 Quando for para produção
1. Subir a Evolution API em um VPS (Hostinger, Contabo ~R$30/mês)
2. Atualizar `api_url` em `notification_channels` para a URL do VPS
3. Configurar o `pg_cron` no Supabase cloud:
```sql
select cron.schedule(
'populate-notification-queue',
'*/5 * * * *',
$$ select populate_notification_queue(); $$
);
```
4. Configurar o disparo da Edge Function via `pg_net` ou webhook externo
5. Migrar para API Oficial da Meta quando tiver volume de clientes