jackherd 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/INSTALL.md +2 -0
- package/README.md +41 -60
- package/assets/branding/README.md +5 -7
- package/assets/branding/jackherd-logo-outline.svg +1 -0
- package/assets/branding/outline.test.ts +30 -0
- package/assets/icons/bark.svg +1 -1
- package/assets/icons/bowl.svg +1 -1
- package/assets/icons/decide.svg +1 -1
- package/assets/icons/ear.svg +1 -1
- package/assets/icons/lanes.svg +1 -1
- package/assets/icons/plan.svg +1 -1
- package/dist/{chunk-M6DDZEW4.js → chunk-MCP4XJQH.js} +17 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +19 -3
- package/dist/plan-format.d.ts +22 -3
- package/dist/plan-format.js +21 -7
- package/herdr-plugin/herdr-plugin.toml +1 -1
- package/install.sh +1 -1
- package/kit/hooks/hooks/approval-gate/index.ts +22 -4
- package/kit/hooks/hooks/approval-gate/run.ts +29 -6
- package/kit/hooks/hooks/approval-gate/subagent.ts +18 -0
- package/kit/hooks/hooks/merge-guard/decision.ts +70 -21
- package/kit/hooks/hooks/merge-guard/xargs-program.ts +182 -0
- package/kit/hooks/hooks/sent-file/index.ts +19 -0
- package/kit/hooks/hooks/sent-file/record.ts +128 -0
- package/kit/hooks/hooks/sent-file/run.ts +48 -0
- package/kit/hooks/hooks/sent-file/store.ts +18 -0
- package/kit/hooks/hooks/shared/board-lanes.ts +15 -2
- package/kit/hooks/hooks.json +9 -0
- package/kit/hooks/statusline.sh +1 -1
- package/kit/workflow/skills/implement/SKILL.md +2 -0
- package/kit/workflow/skills/orchestrate/SKILL.md +31 -6
- package/package.json +3 -1
- package/schemas/jackherd.schema.json +26 -2
- package/src/cli.ts +3 -0
- package/src/config/read-config.ts +5 -1
- package/src/config/schema.ts +22 -2
- package/src/context/fill.ts +12 -0
- package/src/context/session-log.ts +43 -1
- package/src/plan-format/clock.ts +4 -0
- package/src/plan-format/dependencies.ts +2 -5
- package/src/plan-format/lanes.ts +30 -5
- package/src/plan-format/terminal-text.ts +4 -0
- package/src/plan-format/waiting.ts +48 -0
- package/src/plans/active-tasks.ts +60 -0
- package/src/plans/current-phase.ts +17 -7
- package/src/plans/current-task.ts +63 -5
- package/src/plans/lane-standing.ts +12 -6
- package/src/plans/lane-summary.ts +141 -74
- package/src/plans/lane-tickets.ts +15 -0
- package/src/plans/layout-phase.ts +57 -18
- package/src/plans/layout-summary.ts +85 -67
- package/src/plans/layout.ts +253 -89
- package/src/plans/live.ts +22 -0
- package/src/plans/phase-models.ts +1 -1
- package/src/plans/phase-ranges.ts +45 -0
- package/src/plans/pull-requests.ts +6 -0
- package/src/plans/selection.ts +19 -16
- package/src/plans/sent-files-read.ts +22 -0
- package/src/plans/sent-files.ts +121 -0
- package/src/plans/signals-read.ts +54 -0
- package/src/plans/snapshot.ts +251 -55
- package/src/plans/subagents-read.ts +82 -0
- package/src/plans/subagents.ts +132 -0
- package/src/plans/texts.ts +16 -11
- package/src/plans/unlisted-lanes.ts +10 -5
- package/src/plans/view.ts +3 -3
- package/src/plans/waiting-click.ts +76 -0
- package/src/preview/command.ts +83 -0
- package/src/preview/embed.ts +209 -0
- package/src/preview/images.ts +89 -0
- package/src/preview/open.ts +12 -0
- package/src/preview/options.ts +32 -0
- package/src/preview/page.ts +89 -0
- package/src/preview/render.ts +41 -0
- package/src/settled/board-row.ts +1 -2
- package/src/shared/is-record.ts +2 -0
- package/src/shared/private-records.ts +48 -0
- package/src/task-signals/agent.ts +15 -0
- package/src/task-signals/claude-read.ts +72 -0
- package/src/task-signals/claude-signals.ts +112 -0
- package/src/task-signals/command.ts +71 -0
- package/src/task-signals/fixtures/session.jsonl +7 -0
- package/src/task-signals/matcher.ts +96 -0
- package/src/task-signals/record-store.ts +40 -0
- package/src/task-signals/record.ts +68 -0
- package/src/task-signals/text.ts +13 -0
- package/src/task-signals/types.ts +14 -0
- package/src/terminal/open.ts +13 -6
- package/src/terminal/text.ts +8 -1
- package/src/terminal/width.ts +2 -1
- package/src/update/command.ts +2 -0
- package/src/update/config-shares.ts +30 -0
- package/src/update/update.ts +12 -1
- package/src/vocabulary/index.ts +33 -1
- package/src/waiting/command.ts +35 -0
- package/src/watch/appointments.ts +5 -5
- package/src/watch/approvals.ts +2 -2
- package/src/watch/command.ts +91 -5
- package/src/watch/failed-tests.ts +56 -10
- package/src/watch/failure-read.ts +18 -6
- package/src/watch/failures.ts +29 -5
- package/src/watch/feedback.ts +1 -1
- package/src/watch/gh-process.ts +9 -3
- package/src/watch/json-guards.ts +0 -3
- package/src/watch/lane-fill.ts +1 -1
- package/src/watch/lane-model.ts +1 -1
- package/src/watch/lane-parallel.ts +1 -1
- package/src/watch/lane-progress.ts +19 -7
- package/src/watch/lane-report.ts +5 -1
- package/src/watch/lane-rest.ts +28 -0
- package/src/watch/plan-badges.ts +9 -2
- package/src/watch/previews.ts +1 -1
- package/src/watch/pull-requests.ts +2 -1
- package/src/watch/source-path.ts +1 -1
- package/src/watch/sources.ts +1 -1
- package/src/watch/stream-output.ts +12 -7
- package/src/watch/task-parallel-input.ts +118 -0
- package/src/watch/task-parallel-read.ts +149 -0
- package/src/watch/task-parallel-touched.ts +39 -0
- package/src/watch/task-parallel.ts +206 -0
- package/src/watch/waiting-line.ts +19 -0
- package/src/watch/watch-events.ts +13 -1
- package/src/watch/workflow-runs.ts +2 -1
- package/assets/icons/bark-dark.svg +0 -1
- package/assets/icons/bowl-dark.svg +0 -1
- package/assets/icons/decide-dark.svg +0 -1
- package/assets/icons/ear-dark.svg +0 -1
- package/assets/icons/lanes-dark.svg +0 -1
- package/assets/icons/plan-dark.svg +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { firstAnswerTokens } from '../context/fill'
|
|
4
|
+
import { agentLogPath, readHeadLines, readTailLines, TAIL_BYTES } from '../context/session-log'
|
|
5
|
+
import type { SprintLane } from '../plan-format'
|
|
6
|
+
import { MS_PER_MINUTE } from '../plan-format/clock'
|
|
7
|
+
import { STALE_ACTIVITY_MINUTES } from '../plans/lane-summary'
|
|
8
|
+
import { type PlansSettings, worktreeOf } from '../plans/settings'
|
|
9
|
+
import { readClaudeSignals } from '../task-signals/claude-read'
|
|
10
|
+
import { recordSignals } from '../task-signals/record'
|
|
11
|
+
import { readTaskRecords } from '../task-signals/record-store'
|
|
12
|
+
import type { TaskSignal } from '../task-signals/types'
|
|
13
|
+
import { AGENT_STATUS, type LaneAgent } from '../terminal/types'
|
|
14
|
+
import { watchedLanePlanText } from './lane-progress'
|
|
15
|
+
import type { TaskParallelInput } from './task-parallel'
|
|
16
|
+
import { planTaskViewOf, taskParallelInputOf } from './task-parallel-input'
|
|
17
|
+
import { isWithinWorktree, touchedFilesOf } from './task-parallel-touched'
|
|
18
|
+
|
|
19
|
+
const RECENT_WRITE_MS = 30 * 60_000
|
|
20
|
+
const HEAD_BYTES = 256 * 1024
|
|
21
|
+
const SUBAGENT_LOG = /^agent-[A-Za-z0-9]+\.jsonl$/
|
|
22
|
+
/** How many subagent logs of one session have their writes read, the newest first. */
|
|
23
|
+
const MAX_SUBAGENT_LOGS = 20
|
|
24
|
+
|
|
25
|
+
/** One agent's session: its log, the signals in it and its subagents, and the tails of the logs the writes are read from. */
|
|
26
|
+
type AgentRead = { agent: LaneAgent; logPath: string | null; signals: TaskSignal[]; tails: string[][] }
|
|
27
|
+
|
|
28
|
+
const modifiedAt = (path: string): number => {
|
|
29
|
+
try {
|
|
30
|
+
return statSync(path).mtimeMs
|
|
31
|
+
} catch {
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The newest `MAX_SUBAGENT_LOGS` subagent logs of a session, by modification time, so a long session bounds the read. */
|
|
37
|
+
const subagentLogPaths = (logPath: string): string[] => {
|
|
38
|
+
const directory = join(logPath.replace(/\.jsonl$/, ''), 'subagents')
|
|
39
|
+
try {
|
|
40
|
+
return existsSync(directory)
|
|
41
|
+
? readdirSync(directory)
|
|
42
|
+
.filter((name) => SUBAGENT_LOG.test(name))
|
|
43
|
+
.map((name) => ({ path: join(directory, name), modified: modifiedAt(join(directory, name)) }))
|
|
44
|
+
.sort((left, right) => right.modified - left.modified)
|
|
45
|
+
.slice(0, MAX_SUBAGENT_LOGS)
|
|
46
|
+
.map(({ path }) => path)
|
|
47
|
+
: []
|
|
48
|
+
} catch {
|
|
49
|
+
return []
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const tailOf = (path: string): string[] => {
|
|
54
|
+
try {
|
|
55
|
+
return readTailLines(path, TAIL_BYTES)
|
|
56
|
+
} catch {
|
|
57
|
+
return []
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const readAgent = (agent: LaneAgent, projectsDirectory: string | undefined, now: number): AgentRead => {
|
|
62
|
+
const logPath = agentLogPath(agent, projectsDirectory)
|
|
63
|
+
const notOlderThan = now - STALE_ACTIVITY_MINUTES * MS_PER_MINUTE
|
|
64
|
+
return {
|
|
65
|
+
agent,
|
|
66
|
+
logPath,
|
|
67
|
+
signals: logPath ? readClaudeSignals(logPath, { notOlderThan }) : [],
|
|
68
|
+
tails: logPath ? [logPath, ...subagentLogPaths(logPath)].map(tailOf) : [],
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const baselineCache = new Map<string, number | null>()
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* What a fresh agent carries before it has worked, measured as `jh context` measures a fill: the input of the first
|
|
76
|
+
* answer in a session log (system prompt, project instructions and the first request). The smallest honest source on
|
|
77
|
+
* hand: a log of a started session, not a guess; cached per log, since the head of a log never changes.
|
|
78
|
+
*/
|
|
79
|
+
const baselineOf = (logPath: string): number | null => {
|
|
80
|
+
if (!baselineCache.has(logPath)) {
|
|
81
|
+
try {
|
|
82
|
+
baselineCache.set(logPath, firstAnswerTokens(readHeadLines(logPath, HEAD_BYTES)))
|
|
83
|
+
} catch {
|
|
84
|
+
baselineCache.set(logPath, null)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return baselineCache.get(logPath) ?? null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const baselineFor = (lane: string, reads: readonly AgentRead[]): number | null => {
|
|
91
|
+
const own = reads.find(({ agent }) => agent.name === lane)
|
|
92
|
+
const sources = [own, ...reads.filter((read) => read !== own)]
|
|
93
|
+
for (const read of sources) {
|
|
94
|
+
const tokens = read?.logPath ? baselineOf(read.logPath) : null
|
|
95
|
+
if (tokens !== null) return tokens
|
|
96
|
+
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const uniqueSignals = (signals: readonly TaskSignal[]): TaskSignal[] => [
|
|
101
|
+
...new Map(signals.map((signal) => [`${signal.agent}|${signal.text}`, signal])).values(),
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
export type TaskParallelSources = {
|
|
105
|
+
root: string
|
|
106
|
+
settings: PlansSettings
|
|
107
|
+
boardFile: string
|
|
108
|
+
thresholdTokens: number
|
|
109
|
+
now: number
|
|
110
|
+
/** Where Claude Code keeps its session logs; the user's by default. */
|
|
111
|
+
projectsDirectory?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The input of the parallel-task check for each of `lanes`, read from the plan of the lane's newest row, the sessions
|
|
116
|
+
* of every listed agent (a lane's work, and the coordinator's subagents in the lane's worktree) and the records of
|
|
117
|
+
* `jh task`. A lane without a plan, without a token baseline, without a task left or without active work (a working
|
|
118
|
+
* agent, a running subagent or an open record in its worktree) is not in the answer.
|
|
119
|
+
*/
|
|
120
|
+
export const readTaskParallelInputs = (
|
|
121
|
+
lanes: readonly SprintLane[],
|
|
122
|
+
agents: readonly LaneAgent[],
|
|
123
|
+
sources: TaskParallelSources,
|
|
124
|
+
): Record<string, TaskParallelInput> => {
|
|
125
|
+
const reads = agents.map((agent) => readAgent(agent, sources.projectsDirectory, sources.now))
|
|
126
|
+
const records = recordSignals(readTaskRecords(sources.root))
|
|
127
|
+
const since = sources.now - RECENT_WRITE_MS
|
|
128
|
+
return Object.fromEntries(
|
|
129
|
+
lanes.flatMap((lane) => {
|
|
130
|
+
const text = watchedLanePlanText(sources.root, sources.settings, sources.boardFile, lane)
|
|
131
|
+
const baselineTokens = baselineFor(lane.name, reads)
|
|
132
|
+
if (text === null || baselineTokens === null) return []
|
|
133
|
+
const worktree = worktreeOf(sources.root, sources.settings, lane.name)
|
|
134
|
+
const signals = uniqueSignals([...reads.flatMap((read) => read.signals), ...records]).filter((signal) =>
|
|
135
|
+
isWithinWorktree(signal.cwd, worktree),
|
|
136
|
+
)
|
|
137
|
+
const input = taskParallelInputOf({
|
|
138
|
+
lane: lane.name,
|
|
139
|
+
plan: planTaskViewOf(text, sources.settings.planLanguage),
|
|
140
|
+
signals,
|
|
141
|
+
touchedFiles: reads.flatMap((read) => read.tails.flatMap((tail) => touchedFilesOf(tail, worktree, since))),
|
|
142
|
+
laneWorking: agents.some((agent) => agent.name === lane.name && agent.status === AGENT_STATUS.working),
|
|
143
|
+
baselineTokens,
|
|
144
|
+
thresholdTokens: sources.thresholdTokens,
|
|
145
|
+
})
|
|
146
|
+
return input.open.length === 0 || input.active.length === 0 ? [] : [[lane.name, input]]
|
|
147
|
+
}),
|
|
148
|
+
)
|
|
149
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isAbsolute, relative } from 'node:path'
|
|
2
|
+
import { parseLogEntry } from '../context/session-log'
|
|
3
|
+
import { isRecord } from '../shared/is-record'
|
|
4
|
+
|
|
5
|
+
const WRITING_TOOLS: ReadonlySet<string> = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit'])
|
|
6
|
+
|
|
7
|
+
const writtenPathsOf = (entry: Record<string, unknown>): string[] => {
|
|
8
|
+
const content = isRecord(entry.message) && Array.isArray(entry.message.content) ? entry.message.content : []
|
|
9
|
+
return content.flatMap((block) => {
|
|
10
|
+
if (!isRecord(block) || block.type !== 'tool_use' || !WRITING_TOOLS.has(String(block.name))) return []
|
|
11
|
+
const input = isRecord(block.input) ? block.input : {}
|
|
12
|
+
const path = input.file_path ?? input.notebook_path
|
|
13
|
+
return typeof path === 'string' ? [path] : []
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const insideWorktree = (path: string, worktree: string): string | null => {
|
|
18
|
+
const inside = relative(worktree, path)
|
|
19
|
+
return inside === '' || inside.startsWith('..') || isAbsolute(inside) ? null : inside
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Whether `path` is the worktree or lies below it. */
|
|
23
|
+
export const isWithinWorktree = (path: string, worktree: string): boolean =>
|
|
24
|
+
insideWorktree(path, worktree) !== null || path === worktree
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The files the log lines show written (`Edit`, `Write`, `MultiEdit`, `NotebookEdit`) at or after `sinceMs` inside
|
|
28
|
+
* `worktree`, relative to it and without repeats, in order of first write. A path outside the worktree, a line that is
|
|
29
|
+
* no JSON and a write without a time are left out.
|
|
30
|
+
*/
|
|
31
|
+
export const touchedFilesOf = (lines: readonly string[], worktree: string, sinceMs: number): string[] => {
|
|
32
|
+
const touched = lines.flatMap((line) => {
|
|
33
|
+
const entry = parseLogEntry(line)
|
|
34
|
+
const time = typeof entry?.timestamp === 'string' ? Date.parse(entry.timestamp) : Number.NaN
|
|
35
|
+
if (!entry || Number.isNaN(time) || time < sinceMs) return []
|
|
36
|
+
return writtenPathsOf(entry).flatMap((path) => insideWorktree(path, worktree) ?? [])
|
|
37
|
+
})
|
|
38
|
+
return [...new Set(touched)]
|
|
39
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { ellipsize, terminalSafe } from '../plan-format/terminal-text'
|
|
2
|
+
|
|
3
|
+
export const TASK_PARALLEL_EVENT = 'parallel tasks'
|
|
4
|
+
|
|
5
|
+
/** A proposal for the non-stream watcher: the line `formatTaskParallelLine` made for `lane`. */
|
|
6
|
+
export type TaskParallelEvent = { kind: typeof TASK_PARALLEL_EVENT; lane: string; line: string }
|
|
7
|
+
|
|
8
|
+
/** Lane → the fingerprint last seen at a heartbeat; a lane without an entry has had none. */
|
|
9
|
+
export type TaskParallelFingerprints = Record<string, string>
|
|
10
|
+
|
|
11
|
+
export const isTaskParallelFingerprints = (value: unknown): value is TaskParallelFingerprints =>
|
|
12
|
+
typeof value === 'object' &&
|
|
13
|
+
value !== null &&
|
|
14
|
+
!Array.isArray(value) &&
|
|
15
|
+
Object.values(value).every((entry) => typeof entry === 'string')
|
|
16
|
+
|
|
17
|
+
export type OpenTask = { text: string; phase: string }
|
|
18
|
+
|
|
19
|
+
/** One agent's recent work: the files it touched, its active task text and phase when the signal names them. */
|
|
20
|
+
export type ActiveWork = { name: string; files: string[]; taskText?: string; phase?: string }
|
|
21
|
+
|
|
22
|
+
export type TaskParallelInput = {
|
|
23
|
+
lane: string
|
|
24
|
+
open: OpenTask[]
|
|
25
|
+
active: ActiveWork[]
|
|
26
|
+
/** Phase id → the paths of its writes cell; `''` is the whole repository. Absent for a phase without one. */
|
|
27
|
+
phaseWrites: Record<string, string[]>
|
|
28
|
+
doneCount: number
|
|
29
|
+
/** Tokens a fresh agent costs before it does anything, measured by the caller. */
|
|
30
|
+
baselineTokens: number
|
|
31
|
+
/** A proposal is made only when the estimate stays below this. */
|
|
32
|
+
thresholdTokens: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type TaskParallelResult =
|
|
36
|
+
| { proposed: false; reason: string }
|
|
37
|
+
| {
|
|
38
|
+
proposed: true
|
|
39
|
+
tasks: OpenTask[]
|
|
40
|
+
waiting: number
|
|
41
|
+
extraAgents: number
|
|
42
|
+
estimateTokens: number
|
|
43
|
+
reason: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TaskParallelProposal = Extract<TaskParallelResult, { proposed: true }>
|
|
47
|
+
|
|
48
|
+
const MIN_TASKS_BESIDE_AGENT = 1
|
|
49
|
+
const MIN_TASKS_WITHOUT_AGENT = 2
|
|
50
|
+
const TEXT_LIMIT = 60
|
|
51
|
+
const TOKENS_PER_K = 1000
|
|
52
|
+
const GLOB_CHARACTER = /[*?[{]/
|
|
53
|
+
const LINE_SUFFIX = /(?::\d+(?:[-:]\d+)*|#L\d+.*)$/
|
|
54
|
+
const PATH_EXTENSIONS = 'ts|tsx|js|mjs|json|md|sh|yml|yaml|css|html|toml|sql'
|
|
55
|
+
const BARE_FILE = new RegExp(`(?<![\\w./\`-])[\\w./-]+\\.(?:${PATH_EXTENSIONS})(?::\\d+(?:[-:]\\d+)*)?(?![\\w/])`, 'g')
|
|
56
|
+
const BACKTICKED = /`([^`]+)`/g
|
|
57
|
+
const FILE_LIKE = new RegExp(`/|\\.(?:${PATH_EXTENSIONS})$|^\\.[\\w-]+`)
|
|
58
|
+
|
|
59
|
+
const normalise = (entry: string): string | null => {
|
|
60
|
+
const trimmed = entry.trim()
|
|
61
|
+
if (trimmed === '' || /\s/.test(trimmed)) return null
|
|
62
|
+
const path = trimmed.replace(LINE_SUFFIX, '').replace(/^\.\//, '').replace(/\/+$/, '')
|
|
63
|
+
return FILE_LIKE.test(path) && path !== '' ? path : null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The paths a task line names: backticked entries that look like a path or file name and bare file names with a known
|
|
68
|
+
* extension. `:line` suffixes and `./` are cut, globs stay globs, duplicates drop. A task naming none is unknown, and
|
|
69
|
+
* unknown is never proposed.
|
|
70
|
+
*/
|
|
71
|
+
export const pathsOfTask = (text: string): string[] => {
|
|
72
|
+
const backticked = [...text.matchAll(BACKTICKED)].map((match) => match[1] ?? '')
|
|
73
|
+
const bare = text.replace(BACKTICKED, ' ').match(BARE_FILE) ?? []
|
|
74
|
+
return [...new Set([...backticked, ...bare].map(normalise).filter((path): path is string => path !== null))]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const literalPrefix = (path: string): string => {
|
|
78
|
+
const glob = path.search(GLOB_CHARACTER)
|
|
79
|
+
return glob === -1 ? path : path.slice(0, Math.max(path.lastIndexOf('/', glob), 0))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const isInside = (inner: string, outer: string): boolean =>
|
|
83
|
+
outer === '' || inner === outer || inner.startsWith(`${outer}/`)
|
|
84
|
+
|
|
85
|
+
const holdsName = (name: string, path: string): boolean => `/${path}/`.includes(`/${name}/`) || path === name
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether two paths may touch the same files. Directories contain what lies below them, a glob stands for the
|
|
89
|
+
* directory its literal part ends in, `''` is the whole repository, and a bare name matches that name in any directory.
|
|
90
|
+
* When unsure the answer is yes.
|
|
91
|
+
*/
|
|
92
|
+
export const pathsOverlap = (left: string, right: string): boolean => {
|
|
93
|
+
const [a, b] = [literalPrefix(left), literalPrefix(right)]
|
|
94
|
+
const bareLeft = !left.includes('/') && !GLOB_CHARACTER.test(left) && left !== ''
|
|
95
|
+
const bareRight = !right.includes('/') && !GLOB_CHARACTER.test(right) && right !== ''
|
|
96
|
+
if (bareLeft || bareRight) {
|
|
97
|
+
const [name, other] = bareLeft ? [left, right] : [right, left]
|
|
98
|
+
return GLOB_CHARACTER.test(other) ? true : other === '' || holdsName(name, other)
|
|
99
|
+
}
|
|
100
|
+
return isInside(a, b) || isInside(b, a)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const clash = (left: readonly string[], right: readonly string[]): boolean =>
|
|
104
|
+
left.some((a) => right.some((b) => pathsOverlap(a, b)))
|
|
105
|
+
|
|
106
|
+
const activeFootprint = (input: TaskParallelInput): string[] =>
|
|
107
|
+
input.active.flatMap((work) => [
|
|
108
|
+
...work.files,
|
|
109
|
+
...pathsOfTask(work.taskText ?? ''),
|
|
110
|
+
...(work.phase === undefined ? [] : (input.phaseWrites[work.phase] ?? [])),
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
const estimateTokens = (input: TaskParallelInput, taskCount: number): { extraAgents: number; tokens: number } => {
|
|
114
|
+
const extraAgents = input.active.length > 0 ? taskCount : taskCount - 1
|
|
115
|
+
return { extraAgents, tokens: input.baselineTokens * extraAgents }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const kilo = (tokens: number): string => `${Math.round(tokens / TOKENS_PER_K)}k`
|
|
119
|
+
|
|
120
|
+
/** The first group of open tasks, in plan order, whose paths clash neither with the running work nor with each other. */
|
|
121
|
+
const parallelGroup = (input: TaskParallelInput): OpenTask[] => {
|
|
122
|
+
const running = activeFootprint(input)
|
|
123
|
+
const chosen: { task: OpenTask; paths: string[] }[] = []
|
|
124
|
+
for (const task of input.open) {
|
|
125
|
+
const paths = pathsOfTask(task.text)
|
|
126
|
+
const free = paths.length > 0 && !clash(paths, running) && chosen.every((other) => !clash(paths, other.paths))
|
|
127
|
+
if (free) chosen.push({ task, paths })
|
|
128
|
+
}
|
|
129
|
+
return chosen.map(({ task }) => task)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Open tasks that could start now beside the running work: no path overlap with it or with each other, none without a
|
|
134
|
+
* named path. Proposed for one task beside running work or two tasks without, and when the baseline cost of the extra agents stays below the
|
|
135
|
+
* threshold; the reason says in plain words which way it went.
|
|
136
|
+
*/
|
|
137
|
+
export const proposeTaskParallel = (input: TaskParallelInput): TaskParallelResult => {
|
|
138
|
+
const tasks = parallelGroup(input)
|
|
139
|
+
const needed = input.active.length > 0 ? MIN_TASKS_BESIDE_AGENT : MIN_TASKS_WITHOUT_AGENT
|
|
140
|
+
if (tasks.length < needed) return { proposed: false, reason: 'no task can run beside another' }
|
|
141
|
+
const { extraAgents, tokens } = estimateTokens(input, tasks.length)
|
|
142
|
+
if (tokens >= input.thresholdTokens)
|
|
143
|
+
return {
|
|
144
|
+
proposed: false,
|
|
145
|
+
reason: `~${kilo(tokens)} tokens for ${extraAgents} extra agents is not below the ${kilo(input.thresholdTokens)} limit`,
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
proposed: true,
|
|
149
|
+
tasks,
|
|
150
|
+
waiting: input.open.length,
|
|
151
|
+
extraAgents,
|
|
152
|
+
estimateTokens: tokens,
|
|
153
|
+
reason: `${tasks.length} of ${input.open.length} waiting tasks name disjoint paths that the running work does not touch; ~${kilo(tokens)} tokens for ${extraAgents} extra ${extraAgents === 1 ? 'agent' : 'agents'}`,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const FNV_OFFSET = 0x811c9dc5
|
|
158
|
+
const FNV_PRIME = 0x01000193
|
|
159
|
+
|
|
160
|
+
const hash = (text: string): string => {
|
|
161
|
+
let value = FNV_OFFSET
|
|
162
|
+
for (const character of text) {
|
|
163
|
+
value = Math.imul(value ^ (character.codePointAt(0) ?? 0), FNV_PRIME) >>> 0
|
|
164
|
+
}
|
|
165
|
+
return value.toString(16).padStart(8, '0')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A stable fingerprint of what decides a proposal: ticks, active tasks and open tasks, order-independent. Files an
|
|
170
|
+
* agent touches are left out, they move on every keystroke and would repeat the report.
|
|
171
|
+
*/
|
|
172
|
+
export const taskParallelFingerprint = (input: TaskParallelInput): string =>
|
|
173
|
+
hash(
|
|
174
|
+
JSON.stringify([
|
|
175
|
+
input.lane,
|
|
176
|
+
input.doneCount,
|
|
177
|
+
input.active.map((work) => `${work.phase ?? ''}|${work.taskText ?? ''}`).sort(),
|
|
178
|
+
input.open.map((task) => `${task.phase}|${task.text}`).sort(),
|
|
179
|
+
]),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The proposal to report this heartbeat, if any: only when the fingerprint moved since `previous`, and only a real
|
|
184
|
+
* proposal. The returned fingerprint is what the caller keeps for the next heartbeat, proposal or not.
|
|
185
|
+
*/
|
|
186
|
+
export const taskParallelReport = (
|
|
187
|
+
previous: string | undefined,
|
|
188
|
+
input: TaskParallelInput,
|
|
189
|
+
): { fingerprint: string; proposal: TaskParallelProposal | null } => {
|
|
190
|
+
const fingerprint = taskParallelFingerprint(input)
|
|
191
|
+
if (fingerprint === previous) return { fingerprint, proposal: null }
|
|
192
|
+
const result = proposeTaskParallel(input)
|
|
193
|
+
return { fingerprint, proposal: result.proposed ? result : null }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const CHECKBOX = /^\s*(?:[-*]\s*)?\[[ xX]\]\s*/
|
|
197
|
+
|
|
198
|
+
const proposalTaskText = (text: string): string =>
|
|
199
|
+
ellipsize(terminalSafe(text.replace(/\s+/g, ' ')).replace(CHECKBOX, '').replaceAll('"', "'").trim(), TEXT_LIMIT)
|
|
200
|
+
|
|
201
|
+
/** `parallel tasks <lane>: "<a>" ∥ "<b>" · ~<n>k tokens for 1 extra agent`, one line. */
|
|
202
|
+
export const formatTaskParallelLine = (lane: string, proposal: TaskParallelProposal): string => {
|
|
203
|
+
const tasks = proposal.tasks.map((task) => `"${proposalTaskText(task.text)}"`).join(' ∥ ')
|
|
204
|
+
const agents = proposal.extraAgents === 1 ? 'agent' : 'agents'
|
|
205
|
+
return `parallel tasks ${proposalTaskText(lane)}: ${tasks} · ~${kilo(proposal.estimateTokens)} tokens for ${proposal.extraAgents} extra ${agents}`
|
|
206
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ellipsize, terminalSafe } from '../plan-format/terminal-text'
|
|
2
|
+
import type { WaitingItem } from '../plan-format/waiting'
|
|
3
|
+
import { shownWithMore } from '../plans/shown-with-more'
|
|
4
|
+
|
|
5
|
+
const SHOWN_ITEMS = 3
|
|
6
|
+
const ITEM_LENGTH = 40
|
|
7
|
+
|
|
8
|
+
const itemText = ({ item }: WaitingItem): string => ellipsize(terminalSafe(item).replace(/\s+/g, ' '), ITEM_LENGTH)
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The line that brings back what waits on the user's answer, in every heartbeat and status until the item is closed:
|
|
12
|
+
* `for you (<n>): <item>; …`, three items and `+<n>` for the rest. Null when no item is open.
|
|
13
|
+
*/
|
|
14
|
+
export const waitingLine = (items: readonly WaitingItem[]): string | null => {
|
|
15
|
+
const open = items.filter(({ open: isOpen }) => isOpen)
|
|
16
|
+
if (open.length === 0) return null
|
|
17
|
+
const { shown, more } = shownWithMore(open, SHOWN_ITEMS)
|
|
18
|
+
return `for you (${open.length}): ${[...shown.map(itemText), ...(more > 0 ? [`+${more}`] : [])].join('; ')}`
|
|
19
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CHECKS, type ChecksState } from '../plans/pull-requests'
|
|
2
|
+
import { isRecord } from '../shared/is-record'
|
|
2
3
|
import {
|
|
3
4
|
APPOINTMENT_EVENT,
|
|
4
5
|
type AppointmentEvent,
|
|
@@ -10,7 +11,6 @@ import { APPROVAL_EVENT, type ApprovalEvent } from './approvals'
|
|
|
10
11
|
import { type CoordinatorLevel, type CoordinatorReading, isContextLevel, levelStep } from './coordinator-fill'
|
|
11
12
|
import { emptyFailureState, type FailureState, failedTestsOf, isFailureState } from './failures'
|
|
12
13
|
import { emptyFeedbackState, FEEDBACK_EVENT, type FeedbackEvent, type FeedbackState, isFeedbackState } from './feedback'
|
|
13
|
-
import { isRecord } from './json-guards'
|
|
14
14
|
import { isLaneLevels, LANE_FILL_EVENT, type LaneFillEvent, type LaneLevels } from './lane-fill'
|
|
15
15
|
import { isLaneModels, LANE_MODEL_EVENT, type LaneModelEvent, type LaneModels } from './lane-model'
|
|
16
16
|
import { isLaneParallels, LANE_PARALLEL_EVENT, type LaneParallelEvent, type LaneParallels } from './lane-parallel'
|
|
@@ -18,6 +18,12 @@ import { type LaneSnapshot, type SeenSeqs, seenSeqsOf } from './lane-watch'
|
|
|
18
18
|
import { emptyPreviewState, isPreviewState, PREVIEW_EVENT, type PreviewEvent, type PreviewState } from './previews'
|
|
19
19
|
import { PULL_REQUEST_STATE, type WatchedPullRequest } from './pull-requests'
|
|
20
20
|
import { emptySourceState, isSourceState, SOURCE_EVENT, type SourceEvent, type SourceState } from './sources'
|
|
21
|
+
import {
|
|
22
|
+
isTaskParallelFingerprints,
|
|
23
|
+
TASK_PARALLEL_EVENT,
|
|
24
|
+
type TaskParallelEvent,
|
|
25
|
+
type TaskParallelFingerprints,
|
|
26
|
+
} from './task-parallel'
|
|
21
27
|
import { RUN_EVENT, RUN_STATUS, type WorkflowRun } from './workflow-runs'
|
|
22
28
|
|
|
23
29
|
/** What the coordinator is told besides a lane that stopped. The values are the words of the report. */
|
|
@@ -34,6 +40,7 @@ export const WATCH_EVENT = {
|
|
|
34
40
|
laneFill: LANE_FILL_EVENT,
|
|
35
41
|
laneModel: LANE_MODEL_EVENT,
|
|
36
42
|
laneParallel: LANE_PARALLEL_EVENT,
|
|
43
|
+
taskParallel: TASK_PARALLEL_EVENT,
|
|
37
44
|
approval: APPROVAL_EVENT,
|
|
38
45
|
source: SOURCE_EVENT,
|
|
39
46
|
due: APPOINTMENT_EVENT.due,
|
|
@@ -86,6 +93,8 @@ export type WatchState = {
|
|
|
86
93
|
laneModels: LaneModels
|
|
87
94
|
/** The phases last reported as able to run together for each watched lane; a lane without two has none. */
|
|
88
95
|
laneParallels: LaneParallels
|
|
96
|
+
/** The fingerprint of the parallel-task situation of each watched lane at the last heartbeat; a proposal is made only when it moved. */
|
|
97
|
+
taskParallels: TaskParallelFingerprints
|
|
89
98
|
/** The ids of the approval requests on disk when they were last read, each reported once while it is open. */
|
|
90
99
|
approvals: string[]
|
|
91
100
|
/** Per configured source of `watch.sources`: the entries seen (`[id, fingerprint]`) and the start of a failure streak. */
|
|
@@ -139,6 +148,7 @@ export type WatchEvent =
|
|
|
139
148
|
| LaneFillEvent
|
|
140
149
|
| LaneModelEvent
|
|
141
150
|
| LaneParallelEvent
|
|
151
|
+
| TaskParallelEvent
|
|
142
152
|
| ApprovalEvent
|
|
143
153
|
| SourceEvent
|
|
144
154
|
| AppointmentEvent
|
|
@@ -173,6 +183,7 @@ export const emptyWatchState = (): WatchState => ({
|
|
|
173
183
|
laneLevels: {},
|
|
174
184
|
laneModels: {},
|
|
175
185
|
laneParallels: {},
|
|
186
|
+
taskParallels: {},
|
|
176
187
|
approvals: [],
|
|
177
188
|
sources: emptySourceState(),
|
|
178
189
|
reportMtimes: {},
|
|
@@ -218,6 +229,7 @@ const isWatchState = (value: Record<string, unknown>): value is WatchState =>
|
|
|
218
229
|
isLaneLevels(value.laneLevels) &&
|
|
219
230
|
isLaneModels(value.laneModels) &&
|
|
220
231
|
isLaneParallels(value.laneParallels) &&
|
|
232
|
+
isTaskParallelFingerprints(value.taskParallels) &&
|
|
221
233
|
Array.isArray(value.approvals) &&
|
|
222
234
|
value.approvals.every((id) => typeof id === 'string') &&
|
|
223
235
|
isSourceState(value.sources) &&
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { JackherdConfig } from '../config'
|
|
2
2
|
import { GITHUB_HOST } from '../plans/github'
|
|
3
3
|
import { runIdOfLink } from '../plans/pull-requests'
|
|
4
|
-
import { isRecord
|
|
4
|
+
import { isRecord } from '../shared/is-record'
|
|
5
|
+
import { parseArray } from './json-guards'
|
|
5
6
|
|
|
6
7
|
/** Which workflow's runs are deploys, and which environments count as their target (`watch.deploy`). */
|
|
7
8
|
export type DeployConfig = NonNullable<JackherdConfig['watch']['deploy']>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-0.643 -0.176) scale(0.948)" stroke-width="1.845"><svg x="0" y="4" width="22.4" height="20" viewBox="439 80 402 359" preserveAspectRatio="xMinYMax meet"><defs><clipPath id="jack-dark-head"><path d="M458 250L465 215L480 185L500 160L525 135L560 113L600 103L645 99L690 102L730 114L765 138L792 175L812 215L822 255L820 290L802 306L772 313L748 321L740 350L724 388L700 413L676 420L650 407L600 377L560 348L547 329L535 318L500 309L470 302L458 280Z"/></clipPath><mask id="jack-dark-rim"><path d="M458 250L465 215L480 185L500 160L525 135L560 113L600 103L645 99L690 102L730 114L765 138L792 175L812 215L822 255L820 290L802 306L772 313L748 321L740 350L724 388L700 413L676 420L650 407L600 377L560 348L547 329L535 318L500 309L470 302L458 280Z" fill="#fff" stroke="#fff" stroke-width="36" stroke-linejoin="round"/></mask></defs><path mask="url(#jack-dark-rim)" d="M694 108c.6 .6 .6 .6 4 3.5c2.5 1 2.5 1 6.6 1.6c11.2 2 19 6.7 26.4 15.3c3.3 3.4 7.2 4.9 11.5 7c12.2 6.4 19.5 15.5 25.5 27.6c-.1 1.3-.4 2.7-.5 4c0 10.8 7.3 19.8 14.3 27.6c4.9 4.4 10.5 7.6 16.2 10.9c.5 .8 .5 .8 3 5c.7 .8 1.4 1.7 2.1 2.6c7.5 9.3 12.5 20.2 11.4 32.4c-1.3 1.2-2.6 2.3-4 3.5c-1.4 3.6-1.5 6.4-1.2 10.2c.6 12.6-3.4 22.9-10.3 33.3c-5.1 4.4-5.1 4.4-10.5 5c-4.5 2.8-7 6-10.5 10c-6.9 5.3-12.2 8-21 8c-1.8-.8-3.6-1.6-5.5-2.5c-5-1.6-7.1-2.1-12 0c-8.3 6.2-8.3 6.2-10 10.5c-.6 11.3 .1 16.9 5.4 26.7c6.9 13.8 11.5 30.9 9.1 46.3c-1 .6-2 1.3-3 2c-3.4 4.2-3 7.1-3 12.4c.1 7.5-.4 13.7-3.5 20.6c-.5 1.3-1.1 2.6-1.7 3.9c-4.8 10.7-11.7 16.6-21.8 22.1c-4 3-4 3-7.5 6.5c-.5 .4-.5 .4-3 2.5c-.5 .6-.5 .6-3 4c-1.3 1.1-2.6 2.3-4 3.5c-6.3 5.6-6.3 5.6-7 11c1.4 5.4 3 7 7 11c2 3 2 3 3 8c.5 1.2 1 2.4 1.5 3.6c3.1 8 3.5 13.7 .5 21.9c-6.6 9.9-15.4 13.7-26.7 16.8c-6.4 1.2-12.4 1.5-18.9 .9c-3.6-.2-7.2-.2-10.8-.1c-9.2 0-15.9-2.1-24.1-6.1c-4-.5-4-.5-10.5 0c-10.9-2.4-19.9-9.1-27.4-17.2c-4.1-4.4-5.6-5.2-11.6-5.8c-2.5 .1-4.9 .3-7.3 .5c-34.1 2.3-34.1 2.3-46.2-7c-3-3.5-3-3.5-6.5-8.5c-2.5-2-2.5-2-6.5-4c-2.3-5.5-3.3-10-3.9-15.9c-.6-3.6-.6-3.6-3.6-7.6c-5.9-3.5-5.9-3.5-12.5-3.5c-3 1.5-3 1.5-6.8 5.3c-5.1 5-9.8 8.1-16.2 11.2c-3.6 4.3-4 4.9-4 10.5c.4 1.1 .6 2.3 1 3.5c.1 .6 .1 .6 .4 3.9c.7 4.8 1.7 8 5.6 11.1c-.1 .6-.4 1.3-.5 2c-5.1 1.9-10.1 2.8-15.5 3.5c-1.3 .4-2.6 .6-3.9 1c-7.9 1.9-15.6 2.1-23.6 2.5c-2.1 .1-4.3 .4-6.5 .5c-13.9-1.1-26.6-3.5-36.5-14c-3-6-3-6-4-10.9c-1.1-5.2-2.9-7-6.5-10.6c-3.2-16.4 1.4-30.4 10.5-44c1-1.5 2-2.9 3-4.5c.5-1.5 .5-1.5 .5-7.5c-1.5-3-1.5-3-6-8c-1-2-1-2-1-4c1.8-.6 3.6-1.3 5.5-2c9.9-5.7 16.1-13.1 19.5-24c.4-1.1 .7-2.1 1.1-3.3c3.9-12.8 3.9-12.8 .9-18.7c-3-2.5-3-2.5-6.1-4.1c-6.5-3.6-11.1-8.9-13.9-15.9c0-1.8 0-3.6 0-5.5c-1.8-4.1-3.1-5.5-7-7.5c-7.4-12.1-10.1-24.1-7.1-37.9c2-7.4 5-13.2 9.1-19.6c3.1-4.9 3.2-6.3 2-12c0-11.8 7.7-21.9 15.4-30.2c11.9-11.1 26.8-16.8 43.1-16.3c1.1 .5 2.3 1 3.5 1.5c5.5 .5 5.5 .5 10 .5c2.5 .9 5 1.9 7.5 3c3.5 .5 3.5 .5 8-.5c4.1-3.4 5.6-6.8 8-11.5c2.7-3.8 5.6-7.3 8.5-11c1.9-3.7 1.4-5.9 .5-10c-3-3.5-3-3.5-7-5.5c.1-.5 .4-1 .5-1.5c3.1-1.3 6.4-2.7 9.5-4c12.1-6.8 21.1-16.6 30-26.9c4.1-4.3 8.5-7.5 13.5-10.7c4.5-3.1 5.8-5.7 7.5-10.9c10-11 22.4-17.4 36.6-21.1c5.4-1.4 5.4-1.4 8.4-3.4c13.1-6.5 28.2-8.5 42.5-5c2.7 1 5.4 2 8 3c4.5 .7 6.8 0 10.5-2.5c.8-.7 1.6-1.3 2.5-2c9.5-4.8 9.5-4.8 12.5-4c31.5 0 31.5 0 38.5 4z" fill="#FAF9F8" stroke="#FAF9F8" stroke-width="36" stroke-linejoin="round"/><g clip-path="url(#jack-dark-head)"><g id="jack"><path fill="#212E34" d="M694 108c.6 .6 .6 .6 4 3.5c2.5 1 2.5 1 6.6 1.6c11.2 2 19 6.7 26.4 15.3c3.3 3.4 7.2 4.9 11.5 7c12.2 6.4 19.5 15.5 25.5 27.6c-.1 1.3-.4 2.7-.5 4c0 10.8 7.3 19.8 14.3 27.6c4.9 4.4 10.5 7.6 16.2 10.9c.5 .8 .5 .8 3 5c.7 .8 1.4 1.7 2.1 2.6c7.5 9.3 12.5 20.2 11.4 32.4c-1.3 1.2-2.6 2.3-4 3.5c-1.4 3.6-1.5 6.4-1.2 10.2c.6 12.6-3.4 22.9-10.3 33.3c-5.1 4.4-5.1 4.4-10.5 5c-4.5 2.8-7 6-10.5 10c-6.9 5.3-12.2 8-21 8c-1.8-.8-3.6-1.6-5.5-2.5c-5-1.6-7.1-2.1-12 0c-8.3 6.2-8.3 6.2-10 10.5c-.6 11.3 .1 16.9 5.4 26.7c6.9 13.8 11.5 30.9 9.1 46.3c-1 .6-2 1.3-3 2c-3.4 4.2-3 7.1-3 12.4c.1 7.5-.4 13.7-3.5 20.6c-.5 1.3-1.1 2.6-1.7 3.9c-4.8 10.7-11.7 16.6-21.8 22.1c-4 3-4 3-7.5 6.5c-.5 .4-.5 .4-3 2.5c-.5 .6-.5 .6-3 4c-1.3 1.1-2.6 2.3-4 3.5c-6.3 5.6-6.3 5.6-7 11c1.4 5.4 3 7 7 11c2 3 2 3 3 8c.5 1.2 1 2.4 1.5 3.6c3.1 8 3.5 13.7 .5 21.9c-6.6 9.9-15.4 13.7-26.7 16.8c-6.4 1.2-12.4 1.5-18.9 .9c-3.6-.2-7.2-.2-10.8-.1c-9.2 0-15.9-2.1-24.1-6.1c-4-.5-4-.5-10.5 0c-10.9-2.4-19.9-9.1-27.4-17.2c-4.1-4.4-5.6-5.2-11.6-5.8c-2.5 .1-4.9 .3-7.3 .5c-34.1 2.3-34.1 2.3-46.2-7c-3-3.5-3-3.5-6.5-8.5c-2.5-2-2.5-2-6.5-4c-2.3-5.5-3.3-10-3.9-15.9c-.6-3.6-.6-3.6-3.6-7.6c-5.9-3.5-5.9-3.5-12.5-3.5c-3 1.5-3 1.5-6.8 5.3c-5.1 5-9.8 8.1-16.2 11.2c-3.6 4.3-4 4.9-4 10.5c.4 1.1 .6 2.3 1 3.5c.1 .6 .1 .6 .4 3.9c.7 4.8 1.7 8 5.6 11.1c-.1 .6-.4 1.3-.5 2c-5.1 1.9-10.1 2.8-15.5 3.5c-1.3 .4-2.6 .6-3.9 1c-7.9 1.9-15.6 2.1-23.6 2.5c-2.1 .1-4.3 .4-6.5 .5c-13.9-1.1-26.6-3.5-36.5-14c-3-6-3-6-4-10.9c-1.1-5.2-2.9-7-6.5-10.6c-3.2-16.4 1.4-30.4 10.5-44c1-1.5 2-2.9 3-4.5c.5-1.5 .5-1.5 .5-7.5c-1.5-3-1.5-3-6-8c-1-2-1-2-1-4c1.8-.6 3.6-1.3 5.5-2c9.9-5.7 16.1-13.1 19.5-24c.4-1.1 .7-2.1 1.1-3.3c3.9-12.8 3.9-12.8 .9-18.7c-3-2.5-3-2.5-6.1-4.1c-6.5-3.6-11.1-8.9-13.9-15.9c0-1.8 0-3.6 0-5.5c-1.8-4.1-3.1-5.5-7-7.5c-7.4-12.1-10.1-24.1-7.1-37.9c2-7.4 5-13.2 9.1-19.6c3.1-4.9 3.2-6.3 2-12c0-11.8 7.7-21.9 15.4-30.2c11.9-11.1 26.8-16.8 43.1-16.3c1.1 .5 2.3 1 3.5 1.5c5.5 .5 5.5 .5 10 .5c2.5 .9 5 1.9 7.5 3c3.5 .5 3.5 .5 8-.5c4.1-3.4 5.6-6.8 8-11.5c2.7-3.8 5.6-7.3 8.5-11c1.9-3.7 1.4-5.9 .5-10c-3-3.5-3-3.5-7-5.5c.1-.5 .4-1 .5-1.5c3.1-1.3 6.4-2.7 9.5-4c12.1-6.8 21.1-16.6 30-26.9c4.1-4.3 8.5-7.5 13.5-10.7c4.5-3.1 5.8-5.7 7.5-10.9c10-11 22.4-17.4 36.6-21.1c5.4-1.4 5.4-1.4 8.4-3.4c13.1-6.5 28.2-8.5 42.5-5c2.7 1 5.4 2 8 3c4.5 .7 6.8 0 10.5-2.5c.8-.7 1.6-1.3 2.5-2c9.5-4.8 9.5-4.8 12.5-4c31.5 0 31.5 0 38.5 4z"/><path fill="#4A4E51" d="M651.5 106.5c.6 .2 1.3 .3 2 .5c-.6 .6-.6 .6-4 3.5c-1.4 2.3-2.7 4.7-4 7c-.4 .2-.4 .2-2 1c-.6-.3-1.3-.7-2-1c-.6 .5-1.3 1-2 1.5c.1 .2 .1 .2 .5 1c-2-.5-2-.5-7-4.5c-4.8-2.5-9.4-4.2-14.5-5.5c-1-.3-2-.7-3-1c4.7-1.2 10.2 1.8 14.5 3.5c1.1 .3 2.3 .7 3.5 1c.6 .3 1.3 .7 2 1c.6-.8 1.3-1.7 2-2.5c.6-.2 1.3-.3 2-.5c-.6 1.3-1.3 2.7-2 4c.1 .2 .4 .3 .5 .5c.8 0 1.6 0 2.5 0c1-1.3 2-2.7 3-4c.6-.3 1.3-.7 2-1c0 .8 0 1.7 0 2.5c1-1.5 2-3 3-4.5c1-.8 2-1.7 3-2.5zM606.5 127c0 .5 0 1 0 1.5c-1.4 .4-2.7 .7-4.2 1.2c-8.1 2.7-14.3 6.7-20.3 12.8c1.4-.3 2.7-.7 4.1-.9c4.9-1.1 4.9-1.1 11.4-1.6c1 1 1 1 1 2.5c1 0 2 0 3 0c0 .5 0 1 0 1.5c-1 .5-2 1.1-3 1.6c-12.6 7-19.1 17.5-23.5 30.9c-.5 .3-1 .7-1.5 1c-.5-.2-1-.3-1.5-.5c-.7 1.4-1.4 2.9-2 4.4c-2.5 5.1-2.5 5.1-5.5 10.1c-.5 .2-1 .3-1.5 .5c0-6.8 1.7-12.2 4-18.5c-.4 0-.6 0-1 0c-.1 .3-.1 .3-1 2c-.6 1.2-1.2 2.3-1.8 3.6c-3.2 6.3-5.8 12.7-7.9 19.5c-1.7 5-3.7 8.9-6.3 13.4c-3 7.1-4.5 13.8-4.5 21.5c.4-1.3 .8-2.7 1.2-4.1c4.8-14.4 4.8-14.4 7.8-16.4c.1 1.1 .2 2.2 .3 3.3c1.8 12.3 6.3 24 10.4 35.6c4.8 15 3.9 29.5-1.7 44.1c-1.3 2.4-2.6 4.7-4 7c-.5 0-1 0-1.5 0c0-2.1 0-4.1 .1-6.2c-.1-10.5-2.4-18.9-7.1-28.3c-.4-.1-.6-.4-1-.5c.1 .6 .4 1.3 .5 2c1.3 16-1.7 32.7-11 46c-2.9-2.9-4.4-6.8-6.3-10.4c-2.2-3.8-4.6-7.5-7.2-11.1c-11-16.4-13.9-35.1-10.5-54.5c2.2-7.7 4.8-14.9 8.5-22c-14.6 14.6-18.4 34.4-19.1 54.2c.3 8.1 2.5 15.6 4.6 23.3c0 .5 0 1 0 1.5c-7.4-2.9-12.6-11.8-16.4-18.4c-6.3-14.7-5.4-28.9 .4-43.6c1-1.7 2-3.3 3-5c.4-.7 .6-1.3 1-2c-8 5.9-15 11.4-19.5 20.5c-.4 0-.6 0-1 0c0-13.1 7.6-26.4 16-36c.7-.9 1.4-1.8 2.2-2.7c4.6-5.6 9.7-10.7 14.8-15.8c1.9-2.2 3.7-4.4 5.4-6.7c2.6-3.3 2.6-3.3 7.1-7.8c-3.4 0-7 2.9-10 4.5c-.3 .1-.3 .1-1.5 .5c.5-2 .5-2 4.5-6c.7-1 1.4-2 2-3.1c11.5-15.4 27.5-26.1 46.5-29.4c2.5-.3 5-.7 7.5-1c-2.7-2.8-7.7-2.6-11.5-3c1.3-3.9 7.6-6.3 11.1-8.3c8.1-3.5 16.7-6.2 25.4-7.7c3.4-1.7 7.6 0 11 1zM743.5 149c4 1 6.9 4 10 6.5c.6-.3 1.3-.7 2-1c.1 .7 .4 1.3 .5 2c.1 .3 .4 .7 .5 1c.5 0 1 0 1.5 0c5 5.7 8.4 11.4 11 18.5c6.6 12.3 13.5 20.3 25.5 27.5c-.1 .1-.1 .1-1 .5c-1.3-.7-2.6-1.3-4-2c1 3.5 1 3.5 5 8.5c7 8.8 7 8.8 8 14.5c-3.7 .9-6.9-2.2-10-4c-.6-.4-.6-.4-4-2.5c-.5-.2-1-.3-1.5-.5c1.5 3 1.5 3 5.5 7c3.9 5.3 5 10.1 6 16.6c.3 1.6 .6 3.4 .9 5.2c1.6 13.3 1.6 13.3 .1 16.2c-.4 0-.6 0-1 0c-.3-1.1-.5-2.2-.9-3.3c-3.8-14-11.6-25.8-21.1-36.7c-7.5-8.8-7.5-8.8-7.5-13.5c-2.2-3.4-4.6-6.7-7-10c-1.8-4.4-3.1-8.9-4.5-13.5c-.5-1.2-1-2.3-1.5-3.5c0-.5 0-1 0-1.5c1.5 .5 1.5 .5 5 5.5c3.5 4 3.5 4 5 4.5c-.6-1.2-1.2-2.3-1.8-3.6c-3-6-5.2-12-7.2-18.4c-3.5-5-3.5-5-6-7.5c-1.5-2-1.5-2-4-7c-1.1-1.5-2.3-2.9-3.5-4.5c0-.3 0-.7 0-1zM720.5 140.5c1 0 2 0 3 0c0 .5 0 1 0 1.5c-.4 0-.6 0-1 0c0 .3 0 .7 0 1c1 .3 2 .7 3 1c10.4 9.3 15.4 19 16.5 33c-.6 .2-1.3 .3-2 .5c-1-.8-2-1.7-3-2.5c-8-4.7-14.1-7.7-23.5-6.5c0-3.9 6.3-8.3 9-11c0-.3 0-.7 0-1c-2-1-2-1-7-1c-6.4 2.6-10 6.3-14.5 11.5c.5 2 .5 2 3 4.5c.2 .1 .2 .1 1.5 .5c.1 .5 .4 1 .5 1.5c-1.5 2.8-3 5.7-4.5 8.5c-.4 .8-.6 1.7-1 2.5c-2 .5-4 1-6 1.5c-.1 .2-.1 .2-1 1c-.4 0-.6 0-1 0c1-2.5 1-2.5 2-3.5c.8-.2 .8-.2 5-1c.6-1.7 1.3-3.3 2-5c.6-1 1.3-2 2-3c0-.5 0-1 0-1.5c-1-.8-2-1.7-3-2.5c-.5-.7-1-1.3-1.5-2c1.6-6.6 9.1-11.3 14.5-14.5c3.3-.8 5.9-.8 9 .5c.4 .5 .4 .5 2.5 3c-2.6 2.7-5.3 5.3-8 8c2 .2 4 .3 6 .5c12.9 5.3 12.9 5.3 16.5 9c-.9-9.9-3.9-18.4-10.5-26c-5.5-4-5.5-4-9.5-6c0-1.5 0-1.5 1-2.5zM634 169.5c.8 .1 .8 .1 5 .5c6 2.5 6 2.5 9.5 6c.5 3.5 .5 3.5-2 7c-.4 0-.6 0-1 0c2.5 2.5 2.5 2.5 5.9 5.1c3.1 2.4 3.1 2.4 4.1 4.4c-2.5 2.1-4.3 3.3-7.5 4c.5 1.8 1 3.7 1.5 5.5c.8-.8 1.6-1.6 2.4-2.3c3.7-3.2 7.6-4.8 12.1-6.7c1 .5 2 1 3 1.5c.1 .5 .4 1 .5 1.5c-1.5 2-1.5 2-5 4c-4 3.2-6.9 6.1-10 10c-.4-.2-.6-.3-1-.5c2.9-5.8 8.1-9.7 13.5-13c0-.2 0-.3 0-.5c-6 .8-10 3.1-14.5 7c-.5 .7-1 1.3-1.5 2c-.2-.1-.2-.1-1.5-.5c-.1-1-.1-1-1-6c-.1-.4-.1-.4-1-2.5c.1-.8 .4-1.7 .5-2.5c1.3 .2 2.6 .3 4 .5c1-.5 2-1 3-1.5c-1.2-3.8-7.1-6.5-10.5-8.5c0-.3 0-.7 0-1c1.1-.7 2.3-1.3 3.5-2c1-2 1-2 .5-4.5c-4.1-4.1-8-4.3-13.5-5c-1.6-.5-3.3-1-5-1.5c-6.5 0-10.4 .3-16 3.5c-.1 .3-.4 .7-.5 1c1.3 0 2.6 0 4 0c6 1.6 10.8 4.3 16 7.5c-.1 .2-.1 .2-.5 1.5c-1.6 .2-3.3 .3-5 .5c-13 3.7-20.4 11.7-29 21.5c-3.5 3-3.5 3-5.5 4c10.9 .3 10.9 .3 15-2.5c.1-.2 .1-.2 .5-1.5c.5 0 1 0 1.5 0c-.1 .8-.4 1.7-.5 2.5c-5.5 3.6-8.5 4-15 4c-1.1-.3-2.3-.7-3.5-1c-.1-.7-.4-1.3-.5-2c1.5-1.5 1.5-1.5 5.5-3.5c3.7-3.7 7.1-7.6 10.5-11.5c6.5-6.2 13.2-10.1 22-12.5c-10.5-5.5-10.5-5.5-15-5.5c-.4-.1-.4-.1-2.5-.5c0-.7 0-1.3 0-2c7.7-6.2 15.2-6.8 24.5-4zM740.5 183c.4 .2 .6 .3 1 .5c.5 1.8 .9 3.7 1.3 5.5c1.4 5.5 3.1 10 5.7 15c.1 .5 .4 1 .5 1.5c-4.5-1-4.5-1-8.5-3c0-.3 0-.7 0-1c1.6 .3 3.3 .7 5 1c-6.5-3.3-10.8-4.5-18-4.5c-.1-.3-.4-.7-.5-1c.6-1 1.3-2 2-3c.4 .3 .6 .7 1 1c-.1 .3-.4 .7-.5 1c.6-.1 .6-.1 3.5-.5c3.6 .8 7 1.7 10.5 3c-.4-1.3-.8-2.4-1.2-3.7c-2.3-7.6-2.3-7.6-2.3-11.3c.1-.2 .4-.3 .5-.5zM578 181c.5 0 1 0 1.5 0c-1 3-1 3-2.7 6.9c-6.1 13.9-5.7 28.5-.7 42.7c.5 1.2 .9 2.2 1.4 3.4c1 0 1 0 5.5-4c10-7.4 20.1-11.8 32.5-12.5c.8-.2 1.6-.3 2.5-.5c1.1 .3 2.3 .7 3.5 1c1 0 2 0 3 0c.1 .3 .4 .7 .5 1c-.1 .3-.4 .7-.5 1c-1.3 0-2.6 0-4 0c0 .3 0 .7 0 1c1.4 0 2.7-.1 4.1-.1c4.4 .1 4.4 .1 6.9 1.1c-1.8 2.7-5.2 3.6-8 5c-9.7 6.1-17.5 13.3-24.5 22.5c.5-.3 1-.7 1.5-1c.6-.2 1.3-.3 2-.5c.4 .3 .6 .7 1 1c-.9 3.4-3.1 6.1-5 9c-5.1 10.3-6.7 19.6-6.5 31c-.5 0-1 0-1.5 0c0-.5 0-1 0-1.5c-.4 0-.6 0-1 0c0 1.8 0 3.6 0 5.5c-.4 1-.6 2-1 3c-1.1 4.7 1.1 10.2 3 14.5c-2-.5-2-.5-7-4.5c-1.1-.8-2.3-1.6-3.5-2.4c-3-2.1-3-2.1-5-4.1c.8-2.7 1.6-5.4 2.5-8c2.6-15.9 2.3-29.4-3.7-44.6c-7.5-19.6-7.5-19.6-5.8-29.9c.1 0 .4 0 .5 0c.4 1.7 .6 3.3 1 5c.4 0 .6 0 1 0c-.4-1.3-.6-2.7-1-4c-1.1-13.7 .6-23.9 7-36c.1-.3 .4-.7 .5-1zM621.5 219c0 .2 0 .3 0 .5c.8 0 1.6 0 2.5 0c0-.2 0-.3 0-.5c-.8 0-1.6 0-2.5 0zM591 268c-1.5 5.5-1.5 5.5-1 8c.1 0 .4 0 .5 0c1-5 1-5 1-8c-.1 0-.4 0-.5 0zM452.5 289.5c2.6 .2 5.4 .4 8 .5c.1-.1 .4-.4 .5-.5c.9 0 1.6 0 2.5 0c.1 .3 .1 .3 1 1.5c.4 .1 .4 .1 2 1c1.3 1.5 2.6 2.9 4 4.5c4 2.5 4 2.5 9.5 2.5c3.5 4 3.5 4 7.5 9.5c-1 2-1 2-1.5 2.5c-2.1 0-4.3 0-6.5 0c0-.5 0-1 0-1.5c.5 0 1 0 1.5 0c0-.4 0-.6 0-1c-.9-.1-1.6-.4-2.5-.5c-2.5-1-5-2-7.5-3c-8.3-2.1-16.2-.1-23.5 4c-4.4 3.6-7.6 8.4-9 14c0 6 0 6 1 10c-2.6 2.1-5.3 4.1-7.9 6.1c-6.5 5.3-11.5 11-16.1 17.9c-2.5-.5-2.5-.5-6.5-3c-.6-.1-1.3-.4-2-.5c0-.5 0-1 0-1.5c-.5 0-1 0-1.5 0c-5.6-1.4-9.9-6.9-13-11.5c.1-.5 .4-1 .5-1.5c2.2 2.4 4.4 4.9 6.5 7.5c1.5 1 2.9 2 4.5 3c-.2-.7-.2-.7-1.5-4.5c-.7-4.1-.6-8.3-.5-12.5c.9 .1 1.6 .4 2.5 .5c0-.5 0-1 0-1.5c-.9 0-1.6 0-2.5 0c0-8.6 0-8.6 2.5-12c2 .5 2 .5 3 1.5c.6 2.1 1.3 4.3 2 6.5c3 6 3 6 5.5 9c-.1-1.9-.1-3.7-.1-5.7c.2-14.2 4.5-22.7 14.6-32.8c16.6-10.6 16.6-10.6 23-8.5zM448 290c0 .1 0 .4 0 .5c1 0 2 0 3 0c0-.1 0-.4 0-.5c-1 0-2 0-3 0zM461 290c2 .5 2 .5 2 .5zM405 322c-.4 .4-.6 .6-1 1c.4 0 .6 0 1 0c.1 1.1 .4 2.3 .5 3.5c.4 .1 .6 .4 1 .5c-.5-4-.5-4-1.5-5zM403.5 323.5c.5 2 .5 2 .5 2zM403 326.5c.5 1 .5 1 .5 1zM551.5 328.5c1 1.5 2 2.9 3 4.5c14.9 20 14.9 20 24.5 25.5c1.9 1.5 3.7 3 5.5 4.5c1 .8 2 1.6 3.1 2.4c.4 .4 .4 .4 2.4 2.1c0 .5 0 1 0 1.5c-3.4-2.2-6.6-4.6-9.5-7.5c-.2 6.6 .5 12.5 2 19c.4 1.5 .6 2.9 1 4.5c-1.5 1.5-1.5 1.5-7 3c-.8 .8-1.6 1.6-2.5 2.5c-.8 .8-1.6 1.6-2.5 2.4c-2.5 2.6-2.5 2.6-5 5.6c-.6-.1-1.3-.4-2-.5c-.5-2-.5-2 1-7.5c-2.5 .5-2.5 .5-5.5 4c-.4 0-.6 0-1 0c-.4 .6-.6 1.3-1 2c-1.1 1.3-2.3 2.6-3.5 4c-8.5 12.7-11.5 27.9-11.5 43c-.4 .1-.6 .4-1 .5c-5-4-5.8-10.6-7-16.5c-.1-2.1-.4-4.3-.5-6.5c-.1 0-.4 0-.5 0c-2.1 9.9-2.8 19.9-3.5 30c.4 0 .6 0 1 0c.5 1 .5 1 .5 5c-.5 0-1 0-1.5 0c0-1.1 0-2.3 0-3.5c-1.8-.1-3.6-.4-5.5-.5c0-.4 0-.6 0-1c-1.6 0-3.3 0-5 0c-8.6-2.6-8.6-2.6-12.5-6.5c-1.5-1.2-3-2.4-4.5-3.5c-.4 .4-.4 .4-2.5 2c-6.1-3.3-10.1-6.6-15-11.5c-.6-.5-1.3-1-2-1.5c-.1-.8-.4-1.6-.5-2.5c-1.1-1.3-2.3-2.6-3.5-4c-1.4-2.6-2.7-5.3-4-8c-.4-.4-.6-.6-1-1c-1-1.5-1-1.5-1-5c-.4 0-.6 0-1 0c-.4-.6-.6-1.3-1-2c-1 5-1 5-1.5 11c-.1 .1-.4 .4-.5 .5c.9 6 1.7 11.7 3.5 17.5c0 .6 0 1.3 0 2c-.4 .1-.4 .1-2 1c-3.6-3-6.9-5.9-10-9.5c-.5-.5-1-1-1.5-1.5c0-.4 0-.6 0-1c-.4-.1-.4-.1-2-1c-9.1-13.1-10.6-25.7-9-41.5c-.6 .8-.6 .8-4 4.5c-.6-.1-1.3-.4-2-.5c-1.8-3.6 1.1-10.7 2.1-14.4c3.6-9.5 8.7-16.9 15.9-24.1c-16.7 3.4-31.6 13.6-42 27c-.4-.1-.6-.4-1-.5c1-2.5 1-2.5 2.5-4.5c.1-.8 .4-1.6 .5-2.5c10.4-16.9 22.1-29.1 41-36c15.8-3.5 29.7-1.9 45 3c.7 .1 .7 .1 4.5 1c-.1 .5-.4 1-.5 1.5c-2.5 .9-5 1.7-7.5 2.5c-3.5 1.5-3.5 1.5-4 2.5c1.5 .1 2.9 .4 4.5 .5c1 .2 1 .2 6 1.5c3.1 .5 6.4 1 9.5 1.5c-.6 1.6-1.4 3.4-2 5c8.8-6.4 14.8-11.9 20-21.5c1-.5 1-.5 4.3-.9c3.6-.6 6.7-3 10.2-2.1zM462 348c-.4 .4-.6 .6-1 1c.4 .1 .4 .1 2 1c0-.6 0-1.3 0-2c-.4 0-.6 0-1 0zM562.5 387.5c.5 1 .5 1 .5 1zM471 404c-.1 .6-.4 1.3-.5 2c1.5 0 1.5 0 2.5-.5c-.1-.5-.4-1-.5-1.5c-.5 0-1 0-1.5 0zM472.5 406c.5 1.5 .5 1.5 .5 1.5zM470 407.5c.5 2 .5 2 .5 2zM469.5 410c.5 2 .5 2 .5 2zM474 411.5c.5 1 .5 1 .5 1zM469 423.5c0 1 0 2 0 3c.1 0 .4 0 .5 0c0-1 0-2 0-3c-.1 0-.4 0-.5 0zM469.5 427c.5 1.5 .5 1.5 .5 1.5zM470 430c.5 1.5 .5 1.5 .5 1.5zM470.5 432c.5 1 .5 1 .5 1zM498.5 432.5c.5 3.5 .5 3.5 1.5 5c0-.5 0-1 0-1.5c.5 .1 1 .4 1.5 .5c-.5-1.5-1-2.9-1.5-4.5c-.5 0-.5 0-1.5 .5zM471 433.5c.5 1.5 .5 1.5 .5 1.5zM471.5 435.5c.5 1 .5 1 .5 1zM500.5 436.5c.5 1 .5 1 .5 1zM520.5 450c2 .5 2 .5 2 .5zM528 451.5c0 .1 0 .4 0 .5c.8 0 1.6 0 2.5 0c0-.1 0-.4 0-.5c-.8 0-1.6 0-2.5 0zM531 452.5c0 1 0 2 0 3c.1 0 .4 0 .5 0c0-1 0-2 0-3c-.1 0-.4 0-.5 0z"/><path fill="#9B9D9E" d="M720.5 140.5c.1 .3 .1 .3 .5 2c1.3 .7 2.6 1.3 4 2c9.5 7.6 13.6 15.7 16 27.5c0 1.5 0 2.9 0 4.5c-1.5 0-1.5 0-4-2c-7.4-4.3-13.2-7.4-22-7c0-1.5 0-1.5 2.9-4.4c1-1 2.1-2 3.1-3.1c.8-1 1.6-2 2.5-3c-1.5-1.5-1.5-1.5-3.5-2c-6.6 0-9.4 1.8-14.5 6c-2.9 3.1-2.9 3.1-5 6c.5 2 .5 2 3 4.5c.5 .5 1 1 1.5 1.5c-.5 2-.5 2-3.5 7.5c-.5 1.2-1 2.3-1.5 3.5c-.9 .2-.9 .2-5.5 1c-.1 .2-.1 .2-1 1c-3-3.1-2.1-7.8-2.1-11.9c0-1.4 0-2.9 0-4.3c.1-5.1 .5-9.9 3.1-14.3c.5-.2 1-.3 1.5-.5c.4 .2 .6 .3 1 .5c.1-.8 .4-1.7 .5-2.5c6-7.7 10.4-10.9 20-12.5c1-.5 1-.5 3 0zM642.5 160c1.3 1 2.6 2 4 3c.1-.7 .4-1.3 .5-2c2 0 2 0 5.1 2.8c7.5 8.1 12.9 19 12.9 30.2c-2 .8-4 1.7-6 2.5c-4.5 2.5-4.5 2.5-9.5 7c-.5-.2-1-.3-1.5-.5c-.5-2.2-1-4.3-1.5-6.5c-.1-.5-.4-1-.5-1.5c1.5-.5 1.5-.5 4.5-.5c1.1-.7 2.3-1.3 3.5-2c-1.2-3.7-7-6.3-10-8.5c.1-.5 .4-1 .5-1.5c.6-.2 1.3-.3 2-.5c1-2.5 1-2.5 .5-6c-4.2-4.2-8.7-4.6-14.5-5.5c-1.1-.3-2.3-.7-3.5-1c-7.4-.3-12.1 .6-18.5 4.5c-.1 .2-.1 .2-.5 1c.6-.1 .6-.1 3.5-.5c5.6 1.4 10.5 3.7 15.5 6.5c0 .7 0 1.3 0 2c-1.8 .3-3.6 .7-5.5 1c-10.5 3.8-17.5 9.9-24.5 18.5c-2.6 3.1-4.6 5.2-8.5 6.5c-.1 .2-.1 .2-.5 1.5c6.9 .1 11.9-.1 18-3.5c1.4 2.1 2.8 4.3 4 6.5c1.3 1 2.6 2 4 3c.1 .3 .1 .3 1 1.5c-1.5 1-1.5 1-4.9 1.2c-11.6 .9-21.9 6.4-30.7 13.9c-2.9 2.4-2.9 2.4-4.9 2.4c-6.7-17.5-7.6-33.2 0-50.5c2.2-4.4 4.1-7.2 7.5-11c-.4-.2-.6-.3-1-.5c3.9-7.7 16.8-12.7 24.5-16c12.7-3.5 23.3-3.8 35 2.5zM739.5 180.5c.6 .3 1.3 .7 2 1c0 1.5 0 2.9 0 4.5c2.5 10.3 2.5 10.3 4 14c-1.5 .5-1.5 .5-6-1.5c-2.6-.7-5.3-1.3-8-2c.4-1.3 .6-2.7 1-4c0-5 0-5-.5-10c.5-.3 1-.7 1.5-1c.1 .2 .4 .3 .5 .5c3.5 0 3.5 0 5.5-1.5z"/><path fill="#C4CBCE" d="M722 379.5c2.5 .5 2.5 .5 4.5 2.5c1.3 5 1.3 5 .5 8c-2 1-2 1-4 1.5c-1.5-1.5-1.5-1.5-2.5-3.5c-.6-.1-1.3-.4-2-.5c-.4-.6-.6-1.3-1-2c3-4.5 3-4.5 4.5-6z"/><path fill="#FAF9F8" d="M654.5 104c.9 .1 .9 .1 5.5 .5c-2.3 1.5-4.6 3-7 4.5c-3 3.5-3 3.5-3.5 5.5c1.1-.7 2.3-1.3 3.5-2c7.5-3.7 13.6-4.8 22-5c.1 .1 .1 .1 1 .5c0 .3 0 .7 0 1c-1.5 .8-3 1.7-4.5 2.5c-2.3 2.3-4.5 4.5-6 7.5c2-.5 2-.5 4-2c.8-.2 1.6-.3 2.5-.5c.9-.3 .9-.3 5.5-1.5c9.4 0 17.9 2.5 26.5 6c0 .5 0 1 0 1.5c-5 2-5 2-6 2c.1 .7 .4 1.3 .5 2c.4-.2 .6-.3 1-.5c8.9-.5 14.6 2 21.5 7.5c6 6.5 6 6.5 6 8.5c-1.3-.1-2.6-.2-4-.3c-7.5-.3-13.2 1.6-19.5 5.8c-3.5 4-3.5 4-6 9c-.8 0-1.6 0-2.5 0c-1.6 6.1-2.1 11.2-2.1 17.5c0 1.5 0 3 0 4.6c.1 3.9 .1 3.9 1.1 7.4c-1.5 1.5-1.5 1.5-5 2c-.4 .7-.6 1.3-1 2c1.8-.2 3.6-.3 5.5-.5c11 .8 18.8 4.6 27 12c5.3 .7 5.3 .7 10.5 1c15.8 3.9 26.5 12.1 35.1 25.8c2 3.9 3.3 7.5 4.4 11.7c-.1 .5-.4 1-.5 1.5c-1.5-.5-1.5-.5-5-5c-4.5-4.5-4.5-4.5-6-5c1 3 1 3 2.9 6.1c5.7 10.6 6.6 21.6 4.6 33.4c-3.4 8.7-7.5 15-14 21.5c-.5-.1-1-.4-1.5-.5c.1-1.1 .4-2.3 .5-3.5c.1-5.9 .2-10.7-1.5-16.5c-.4-.1-.6-.4-1-.5c-.2 1.3-.4 2.6-.5 4c-2.1 11.1-7 19.5-15 27.5c-.5 .1-1 .4-1.5 .5c0 .4 0 .6 0 1c-12.6 9.5-12.6 9.5-18.5 9.5c1-2.5 1-2.5 3.5-6c2.5-5.5 2.5-5.5 2.5-8c-.4-.1-.6-.4-1-.5c-.4 .6-.6 1.3-1 2c-8.9 10.1-18.4 16.1-31.5 19c-11.5 .7-20.9-3.4-29.5-11c0-.4 0-.6 0-1c-.4 0-.6 0-1 0c.8 6.3 3.4 9.9 7.5 14.5c0 .4 0 .6 0 1c-14.5 0-27.2-5.3-37.5-15.5c-.6-.8-.6-.8-3.5-5c-.5-.5-1-1-1.5-1.5c-.5 0-1 0-1.5 0c1 6.3 2.6 11.2 5.5 17c0 .6 0 1.3 0 2c-10.8-2.1-20.7-7.8-27.4-16.7c-6-9.4-7.6-18.9-6-29.8c1.9-8.3 4.9-14.9 9.4-22c.6-1.2 1.3-2.3 2-3.5c-2 1-2 1-5 4.5c-.5 0-1 0-1.5 0c1.6-6.3 7.7-12.3 12.5-16.5c.8-.8 1.6-1.6 2.5-2.4c9.6-8.4 20.5-13.8 32.8-17.3c3.7-1.3 3.7-1.3 5.2-3.8c4.5-7 9-12.1 16-16.5c.4 0 .6 0 1 0c.1-.7 .4-1.3 .5-2c-.8-.2-1.6-.3-2.5-.5c-.1-1.4-.3-2.9-.5-4.4c-2-10.8-6.9-20.7-15-28.1c-.4-.2-.6-.3-1-.5c.5 1 1 2 1.5 3c-.1 .5-.4 1-.5 1.5c-.7-.6-.7-.6-4.5-3.5c-10-6-18.7-7.2-30.2-5.2c-15.8 4.4-29.1 13.5-39.3 26.2c-.8 .8-1.6 1.7-2.5 2.5c0-11.4 6-22.8 13-31.5c5.9-5.9 11.7-9.8 19.5-13c0-.2 0-.3 0-.5c-8 0-15.4 1.2-23 3.5c-.5 0-1 0-1.5 0c1.2-4.9 6.6-8.1 10.6-10.7c5.5-3.2 10.6-5 16.9-6.3c0-.5 0-1 0-1.5c-8-2.2-15.3-3.3-23.5-4c-.1-.3-.4-.7-.5-1c19.3-6.7 36.5-6.3 55.5 1c.5 .2 1 .3 1.5 .5c-1.5-2.5-1.5-2.5-3.5-4.5c0-.3 0-.7 0-1c1 0 2 0 3 0c.5-.3 1-.7 1.5-1c.5 0 1 0 1.5 0c.1 .2 .1 .2 1 1c1.6-2.2 3-4.6 4.5-7c1.5-1.3 3-2.7 4.5-4c.5-.7 1-1.3 1.5-2zM640.5 117.5c0 .7 0 1.3 0 2c.5 .3 .5 .3 3 2c.4-.7 .6-1.3 1-2c-1.5-1.5-1.5-1.5-4-2zM679.5 200c-4 4.6-4 4.6-4 9.5c3.6 4.3 6.6 5.1 12 6.5c.4 .3 .4 .3 2.5 2c1.5 .5 3 1 4.5 1.5c.9 5.3 .5 7.1-2 12c-8.1 8.2-15 9.1-26 9.5c-.1 .7-.4 1.3-.5 2c7.6 3.8 14.5 3 22.5 .5c4.7-2.1 7.3-3.7 11-7.5c3.5 .5 3.5 .5 7 1.5c4.3 1.1 7.5 .7 11.5-1c2.5-2.5 2.5-2.5 3.5-4.5c-2-.5-2-.5-5.8-.4c-5.2-.1-8.4-1-12.7-4.1c-1.5-4.5-1.5-4.5-2-10c1.6-1.5 3.3-2.9 5-4.5c2.5-5 2.5-5 3-9c-1.5-4.5-1.5-4.5-4.5-7c-8.7-3.2-17.1-1.4-25 3zM555.5 134.5c-2.5 2.5-2.5 2.5-6.5 5.5c-.8 .8-1.6 1.7-2.5 2.5c-3.3 1.1-6.6 2.1-10 3c-1.5 .7-3 1.3-4.5 2c-.4-.2-.6-.3-1-.5c4.2-7.8 9.8-9 18-11.6c4-.9 4-.9 6.5-.9zM728 178c2.5 1 2.5 1 5.5 3.5c0 1.3 0 2.7 0 4c.1 1.5 .4 2.9 .5 4.5c0 3.5 0 3.5-1.5 6.5c-.6 .1-.6 .1-3.5 .5c-.5-2-.5-2 1-7c0-4.1-.4-6.8-2-10.5c0-.5 0-1 0-1.5zM495.5 193c-2 4-5.7 6.6-9 9.5c-1.7 1.7-3.4 3.3-5 5c-.5-1-.5-1 0-4.5c-1-4.5-1-4.5-4-8.5c.9-.1 .9-.1 5.2-.2c4.3-.2 8.5-1.3 12.8-1.3zM618.5 196.5c-.1 2-.4 3.9-.5 6c1.2 6.3 2.9 9.4 7.5 14c.1 .1 .1 .1 1 .5c0 .5 0 1 0 1.5c-3.3 1.6-7.2 .3-10.5-.5c-4.1-2.9-6.2-5.5-8.5-10c.5-3.5 .5-3.5 5-8c5-3.5 5-3.5 6-3.5zM797 228.5c4.8 1.2 8.5 6.9 11.5 10.5c0 .3 0 .7 0 1c.6 .3 1.3 .7 2 1c.8 1.3 1.6 2.7 2.5 4c.4 .3 .6 .7 1 1c-1.1 1-2.3 2-3.5 3c-1 2.5-1 2.5-1.5 5.5c-1-1.5-1-1.5-1.9-4.7c-2.1-7.4-5.3-13-9.6-19.3c-.1-.7-.4-1.3-.5-2zM466 229c.5 1.5 .5 1.5-2 7.5c-3.6 14.2-3.6 27.9 3.1 41.1c2.9 4.9 2.9 4.9 7.4 9.9c.5 0 1 0 1.5 0c-.2-1.1-.4-2.1-.6-3.2c-.7-7.2-.2-13.9 2.1-20.8c.4 0 .6 0 1 0c.5 1 .5 1 .9 5.3c2 17.8 9.7 30.6 22.6 42.7c7.4 5.1 14.9 8.5 23.5 11c0 .5 0 1 0 1.5c-8.2 2.2-14.6 3.1-22.8 .8c-14.9-3.8-28.8-3.3-43.7 .2c-.5-2-.5-2 1.5-6.5c5.3-6.6 12.7-7.7 20.8-8.6c1.6-.1 3.1-.3 4.7-.4c-1.6-3.1-3.5-6.5-6-9c-1.8 0-3.6 0-5.5 0c-5-3.5-5-3.5-7.9-6.6c-2.6-2.9-2.6-2.9-5.6-3.9c-12.9-.4-23.7 2.9-33.5 11.5c-.5-.1-1-.4-1.5-.5c2-2.5 2-2.5 4-4.5c-1.5-.5-1.5-.5-3-.5c-1.5 3-1.5 3-1.5 6c0 1.5 0 1.5-3.5 6.5c-5.6 10.3-5.9 18.9-5 30.5c-5.1-2.6-7.3-7.6-9.5-12.5c-.5-1.8-1-3.6-1.5-5.5c-.5-.1-1-.4-1.5-.5c-2.1 10.5-2.2 19.9 1.5 30c0 .5 0 1 0 1.5c-9.8-4.9-14.8-13.2-18.5-23c-1.6-6-2.4-11.9-1.5-18c.4-.1 .4-.1 2.5-1c2.5-5 2.5-5 3-7.5c-.9 .5-1.6 1-2.5 1.5c-2.1-4.2-.2-10.6 1-15c5.6-14.8 16.3-25.1 30-32.5c10.8-4.5 10.8-4.5 13-4.5c-.1-.5-.4-1-.5-1.5c-11.3-2.5-21.3-.6-32 3c-.4 0-.6 0-1 0c1.3-5.2 8.4-9 12.5-12c13.6-7.4 26-10.4 41.5-9c.1-.3 .4-.7 .5-1c1.6 .2 3.3 .3 5 .5c4.5-1 4.5-1 7-3zM396.5 263c-2.5 2.5-2.5 2.5-6.5 5.5c-4.8 4.3-9.2 8.4-13 13.5c-.5-1-.5-1 0-4.5c-.1-1.5-.4-2.9-.5-4.5c2-2 2-2 5.5-4c.5-.3 .5-.3 3.1-1.9c8.5-5.1 8.5-5.1 11.4-4.1zM792 278.5c1.5 .5 1.5 .5 2 1.5c.1 5.6-.2 10.9-1 16.5c-1.8 .5-3.6 1-5.5 1.5c-.8 .4-1.6 .6-2.5 1c.9-2.2 2-4.4 3-6.5c2-4.8 3.1-8.9 4-14zM374.5 324c.4 .1 .6 .4 1 .5c.2 1.4 .4 2.9 .6 4.4c2 12.2 7.7 20.3 16.9 28.6c-.1 .3-.1 .3-.5 1.5c-9.6-2.8-9.6-2.8-13-5c-.4-2-.6-3.9-1-6c-2.5-3.5-2.5-3.5-6-5.5c.1-.4 .4-.6 .5-1c.1-4.1 .2-8.3 .3-12.4c.2-3.6 .2-3.6 1.2-5.1zM677.5 347c4.5 1.5 4.5 1.5 5.5 3.5c-1.6 6.6-3.8 13.1-6 19.5c-.6 2-1.2 3.9-1.9 6c-2.1 6.5-2.1 6.5-4.1 8.5c-4.5-.5-4.5-.5-6.5-2.5c1.5-5.3 3.1-10.4 5-15.6c.5-1.8 1.1-3.6 1.8-5.5c.6-1.5 1.1-3.2 1.7-4.9c.5-1.4 .9-2.6 1.4-4c1.1-3 1.1-3 3.1-5zM462.5 349c-1 2-1 2-3.3 4.2c-9.5 9.7-13.8 21.6-15.7 34.8c2-1 2-1 6-6.5c.4 0 .6 0 1 0c-.3 2.9-.6 5.7-1 8.5c-.3 13.1 .6 23.4 8 34.5c.9 .8 1.6 1.6 2.5 2.5c4.5 5 4.5 5 5.5 7c.6 .4 1.3 .6 2 1c1.1 1 2.3 2 3.5 3c.4 0 .6 0 1 0c-.8-3.6-1.6-7.4-2.5-11c-1-7.1-.1-14.1 1-21c.5-.4 1-.6 1.5-1c.5 1.6 1 3.1 1.5 4.8c3.4 9.6 7.3 16.4 15 23.2c1.6 1.5 3.3 2.9 5 4.5c2.3 1.4 4.6 2.7 7 4c.1-.5 .4-1 .5-1.5c-.4-.6-.6-1.3-1-2c0-.6 0-1.3 0-2c2 1 2 1 3.9 3.9c5.4 8 13.2 9.3 22.2 11.3c.8 .2 .8 .2 4.4 .8c1 3.5 1 3.5 2 9.5c1.4 7.6 1.4 7.6 2 10c-7.3 1.4-16-2-22.7-4.7c-1.1-.4-2.2-.9-3.3-1.3c-.4 .1-.4 .1-2 1c-14.9-3-28.1-16-39-26c-1.3 5.2 3.1 11.6 5.5 16c-1.9 9.5-11.4 17.4-19 22.5c-10.2 5.4-20.5 7.6-32 9c3.1 3.1 8.4 3.4 12.5 4.5c6.2 2.1 8.1 4.1 11 10c.5 4.5 .5 4.5-1 8c-1.3 .8-2.6 1.6-4 2.5c-.5-.1-1-.4-1.5-.5c-.4-2-.6-3.9-1-6c-.4-.8-.6-1.6-1-2.5c-1-.1-2-.4-3-.5c.4 1 .6 2 1 3c0 3.5 0 3.5-1.5 7c-3.9 2.6-9.1 2-13.5 2c-.6-.1-1.3-.4-2-.5c-.5-2-1-3.9-1.5-6c-.5-.8-1-1.6-1.5-2.5c0-.4 0-.6 0-1c-.5 0-1 0-1.5 0c-.1 2.1-.4 4.3-.5 6.5c-.4 .8-.6 1.6-1 2.5c-5.5 2.2-11.6-.4-17-2c-7.4-5.2-10.6-9.4-12.5-18c0-4.7 .1-9.3 .5-14c-.5 0-1 0-1.5 0c-.4 .6-.4 .6-2.5 4c-.4 0-.6 0-1 0c-.4 .8-.6 1.6-1 2.5c-.6 1.2-1.3 2.4-2 3.7c-1.9 4-2.6 6.9-3 11.3c-.1 0-.4 0-.5 0c-.4-1.5-.6-2.9-1-4.5c-3-3.5-3-3.5-4-4.5c.4 0 .6 0 1 0c.1-1.1 .4-2.3 .5-3.5c.4 0 .6 0 1 0c.4-1 .6-2 1-3c2.2-4.2 4.6-7.7 7.5-11.5c.5-2.6 1-5.4 1.5-8c5-12.6 12.4-23.1 21-33.5c2.8-3.4 5.4-6.9 8-10.5c-3.8-.9-7.8 1-11.5 2c-1.6 .1-3.3 .4-5 .5c0-.6 0-1.3 0-2c1.3-1.1 2.6-2.3 4-3.5c7.9-8.1 13.4-17.6 18-28c-.3-.1-.3-.1-1.5-.5c1.8-12.7 10.6-21.6 20.1-29.4c8.2-5.9 20.1-13.1 30.4-13.1zM476 422.5c-.4 .8-.6 1.6-1 2.5c-.4 .4-.6 .6-1 1c.5 2.5 .5 2.5 1.5 4c1 0 2 0 3 0c0-.5 0-1 0-1.5c.1-.1 .4-.4 .5-.5c.6 .4 1.3 .6 2 1c-1-2.5-1-2.5-4-6.5c-.4 0-.6 0-1 0zM660.5 353.5c2 1 2 1 5 5c-2.1 3.5-5.2 5.1-8.5 7.5c-.1 .5-.4 1-.5 1.5c1 .6 1.9 1.2 2.9 1.8c3.1 2.2 3.1 2.2 5.6 5.2c-2 4-2 4-3 5c-6-1.5-12.3-7-17-11c1-5.8 7-8.7 11.5-12c1.3-1 2.6-2 4-3zM686 353.5c5.9 1.1 11.4 6.9 15.5 11c-.7 5.1-3.6 6.4-7.5 9.5c-.5 .4-.5 .4-3.1 2.6c-2.4 1.9-2.4 1.9-3.9 2.4c-1.4-1.4-2.6-2.6-4-4c.9-3.4 3.9-5.2 6.5-7.5c.6-.6 1.3-1.3 2-2c-.6-.4-1.3-.6-2-1c-5.9-4.1-5.9-4.1-7-6.5c1.5-2.5 1.5-2.5 3.5-4.5zM579 360c2 .5 2 .5 6.5 4.5c2 1.5 4 3 6 4.5c.9 .6 1.6 1.3 2.5 1.9c4.6 3.5 9.4 6.7 14.2 9.9c1.8 1.3 3.7 2.5 5.6 3.8c4.1 2.6 8.1 5 12.2 7.4c.8 .6 1.6 1.3 2.5 2c2.5 1.5 5 3 7.5 4.5c6.6 4.2 13.1 8.4 19.5 13c.8 .6 .8 .6 4.9 3.3c3.7 2.6 7.1 5.3 10.6 8.2c3.2 2.6 3.2 2.6 6 4.5c6-2 11-6.6 15.5-11c.6-.4 1.3-.6 2-1c.4-.6 .6-1.3 1-2c.4-.4 .4-.4 2-2c.4 0 .6 0 1 0c.4-.6 .6-1.3 1-2c.8-1 1.6-2 2.5-3c.4 0 .6 0 1 0c.4-.8 .6-1.6 1-2.5c1.5-1.6 3-3.3 4.5-5c1.1-1.8 2.3-3.6 3.5-5.5c1-1.4 1.9-2.9 2.9-4.4c4.6-7.2 7.3-14.8 9.6-23.1c3.5 1.7 4.4 5.6 6 9c1.5 2.5 3 5 4.5 7.5c5.4 9.9 5.4 9.9 7 13c.5 .4 1 .6 1.5 1c-1.3 1.1-2.6 2.3-4 3.5c-.4 .9-.4 .9-2 5.5c-.1 0-.4 0-.5 0c-.1-1.5-.4-2.9-.5-4.5c-2-6.4-4.3-11.6-7.5-17.5c-.5-.1-1-.4-1.5-.5c.1 .4 .4 .6 .5 1c1.4 23.4-1.7 45.9-17 64.5c-1.1 1.1-2.3 2.3-3.5 3.5c-.1 0-.4 0-.5 0c.1-1.1 .4-2.3 .5-3.5c.9-6.9 1.7-13.6 1.5-20.5c-1.5 1.5-1.5 1.5-3.1 6c-5 13.5-14.3 24.7-25.9 33c-.6 .5-1.3 1-2 1.5c-.5 0-1 0-1.5 0c-1.1-1.6-2.3-3.3-3.5-5c-4-4.5-7.8-8.4-12.6-12c-4.4-3.4-6-6.4-8.6-11.2c-2.3-4.3-2.3-4.3-5.8-9.3c0-.4 0-.6 0-1c-.4 .1-.4 .1-2 1c-.6 0-1.3 0-2 0c-2.5-2.5-2.5-2.5-3-3.5c-.6-.4-1.3-.6-2-1c-1.1-1-2.3-2-3.5-3c2.5 6.3 5.1 12.3 8.3 18.2c.6 1.1 1.1 2.2 1.7 3.3c-.1 .5-.4 1-.5 1.5c-1.2-.4-2.5-.8-3.7-1.3c-3.8-1.2-3.8-1.2-4.8-1.2c1.3 3.8 7 6.2 10.3 8.4c12.8 9.3 21.8 20.7 28.2 35.1c0 .4 0 .6 0 1c-1.5-.1-3-.4-4.5-.5c0 .5 0 1 0 1.5c3.1 2.4 6.3 4.7 9.5 7c7.2 6.4 10.8 12.6 12.5 22c-1.6 5-2.9 6.9-7 10.5c-1.5-1.5-1.5-1.5-2.5-6.5c-2.5-5-2.5-5-5-7.5c-.4 0-.6 0-1 0c.5 2.5 1.2 5 2 7.5c0 5.5 0 5.5-2 9.5c-5 2.5-5 2.5-10 3.5c-1.3 .4-2.6 .6-4 1c-1.8-.1-3.6-.4-5.5-.5c-1-1.5-1-1.5-1.5-6c-2-5-2-5-3.5-6.5c-.4 .1-.6 .4-1 .5c0 2.1 0 4.3 0 6.5c-1.5 3.5-1.5 3.5-4 5c-9 1.8-17.6-1.5-25.5-6c-4.2-3-7.7-5.5-11-9.5c-.8-.4-1.6-.6-2.5-1c.3 1.2 .6 2.4 1 3.6c.5 2.1 1 4.3 1.5 6.4c-.4 .1-.6 .4-1 .5c-15.7-7.7-24.2-21-30-37c-1.6-5.3-2.6-10.1-3.5-15.5c-.6 .1-1.3 .4-2 .5c-1.9 6.4-2.6 12-2.5 18.6c0 1.4 0 2.8 0 4.2c0 1.1 0 2.1 0 3.2c-8-4-13-13.3-16.5-21c-.7-2.3-1.4-4.6-2-7c-.4-1.1-.6-2.3-1-3.5c-1-11-.6-22.1 3.5-32.5c-.6-.1-1.3-.4-2-.5c-.4 .8-.6 1.6-1 2.5c-.5 .7-.5 .7-3 4.5c-1.9 4.3-3.8 8.6-5.5 13c-.5 .1-1 .4-1.5 .5c0-4.6 .1-8.9 .5-13.5c0-1.2 0-2.4-.1-3.7c.4-19.4 5.6-36.9 19.6-50.8c2.5-2 2.5-2 6-3c-.5 4-.5 4-2 9c2-1 2-1 5.5-5c.8-.8 1.6-1.6 2.5-2.5c.8-1 1.6-2 2.5-3c4-1.5 4-1.5 6.5-2c-.7-3.1-1.4-6.1-2-9.1c-1-5.5-1.1-10.4-1-15.9zM722 380.5c-1.5 1.5-1.5 1.5-3.5 5c.1 .5 .4 1 .5 1.5c.4 .1 .4 .1 2 .5c.4 .5 .4 .5 2 3c1.1-.4 2.3-.6 3.5-1c-.1-.6-.4-1.3-.5-2c0-1 0-1 .5-2.5c-1.5-3.5-1.5-3.5-3-4.5c-.5 0-1 0-1.5 0zM709.5 400c-.5 .5-1 1-1.5 1.5c.1 .5 .4 1 .5 1.5c1-.4 2-.6 3-1c-.4-.6-.6-1.3-1-2c-.4 0-.6 0-1 0zM714.5 428.5c-.6 .6-1.3 1.3-2 2c-.4 0-.6 0-1 0c.1 .6 .4 1.3 .5 2c.4-.1 .6-.4 1-.5c.4 1.1 .6 2.3 1 3.5c2.5 .5 2.5 .5 4 0c.4-1 .6-2 1-3c-1.1-1.3-2.3-2.6-3.5-4c-.4 0-.6 0-1 0z"/><path fill="#03B5BD" d="M569 311.5c3.5 2 3.5 2 7 4.5c4.8 2.3 9.6 4.4 14.5 6.5c1.1 .5 2.3 1 3.5 1.5c28 9.3 28 9.3 42.6 9c4 0 7.5 .9 11.4 2c8.4 1.4 16.6 1.2 25 .5c1.8 0 3.6 0 5.5 0c9.7-.8 19.1-3.5 28-7.5c3.5-1.1 7-2.1 10.5-3c0 10.4-6.5 17.6-12.5 25.5c4.8-2.6 8.6-5.1 12.5-9c.6 0 1.3 0 2 0c0 13-4.1 24.7-10.5 36c-.7 1.4-1.5 2.7-2.2 4.1c-6.8 11.4-16.3 21.3-25.8 30.4c-.8 .1-1.6 .4-2.5 .5c-2.9-2.1-5.7-4.3-8.5-6.5c-5.3-3.8-10.6-7.4-16-11c-1.5-1-3-2-4.5-3c-5.8-3.8-11.6-7.4-17.5-11c-4.3-2.6-8.4-5.2-12.5-8c-1.3-.8-2.6-1.6-4-2.4c-6.4-4.1-12.5-8.5-18.5-13.1c-1-.8-2.1-1.6-3.2-2.4c-12.3-9.6-24-20.3-32.3-33.6c1-2.5 1-2.5 2.5-4c.4 0 .6 0 1 0c.4-.6 .6-1.3 1-2c2.5-3.5 2.5-3.5 3.5-4zM677.5 348c-4 6.8-6 15.1-8.5 22.5c-.8 2.1-1.5 4.2-2.2 6.4c-1.3 4.1-1.3 4.1-.3 6.1c1.5 .1 3 .4 4.5 .5c2.1-3.6 3.1-7.4 4.4-11.3c1.6-5.3 3.4-10.6 5.1-15.9c1-3.3 1-3.3 1.5-6.8c-1.5-.5-3-1-4.5-1.5zM647 365c-.4 .5-.6 1-1 1.5c1 4.7 6.5 6.6 10.2 9.3c1.4 .8 2.8 1.8 4.3 2.7c.4-.1 .4-.1 2-.5c.5-1.3 1-2.6 1.5-4c-2.8-2.3-5.6-4.4-8.5-6.5c0-.5 0-1 0-1.5c2.8-2.1 5.6-4.1 8.5-6c.1-.5 .4-1 .5-1.5c-1-1.3-2-2.6-3-4c-4 0-11.5 8.1-14.5 10.5zM686 354.5c-2 2-2 2-2.5 4c2.5 2.5 2.5 2.5 5.6 4.8c2.9 2.2 2.9 2.2 3.4 3.7c-.4 .1-.4 .1-2 1c-5 4-5 4-6.5 5.5c.5 2 .5 2 3 4.5c3.5-1.7 6.1-4.5 9-7c1.3-1 2.6-2 4-3c.1-1.1 .4-2.3 .5-3.5c-11.3-10-11.3-10-14.5-10z"/><g id="face-lights" fill="#FAF9F8"><circle cx="628.3" cy="198" r="3.4"/><circle cx="714.2" cy="182.6" r="3"/><ellipse cx="692.6" cy="201.6" rx="4.2" ry="1.6" transform="rotate(-12 692.6 201.6)"/></g><path fill="#03B5BD" d="M640 345H706L700 388L645 390Z"/><path d="M660.5 356.5L648 366L660.5 375.5M680.5 350.5L669 382.5M685.5 356.5L698.5 365.5L686 375" fill="none" stroke="#FAF9F8" stroke-width="4.6" stroke-linecap="round" stroke-linejoin="round"/></g><g id="face-lights" fill="#FAF9F8"><circle cx="628.3" cy="198" r="3.4"/><circle cx="714.2" cy="182.6" r="3"/><ellipse cx="692.6" cy="201.6" rx="4.2" ry="1.6" transform="rotate(-12 692.6 201.6)"/></g></g></svg><g stroke="#21BEC5"><path d="M19.4 1.9a2.6 2.6 0 0 1 0 3.6"/><path d="M21.3 0.6a4.4 4.4 0 0 1 0 6.2"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-1.292 -1.073) scale(1.058)" stroke-width="1.655"><path fill="#21BEC5" opacity=".85" d="M4.6 14.5h14.8l-1.1 4.5H5.7z"/><g stroke="#E6EDF3"><path d="M2.5 11.5h19l-2.3 8H4.8z"/><path d="M9 8.5c0-1 .8-1.5 1.5-1h3c.7-.5 1.5 0 1.5 1s-.8 1.5-1.5 1h-3c-.7.5-1.5 0-1.5-1z"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-2.489 -0.233) scale(1.111)" stroke-width="1.575"><g stroke="#E6EDF3"><path d="M12 3v18"/><path d="M9 21h6"/><path d="M12 5H5.5L3.5 7l2 2H12"/></g><path stroke="#21BEC5" d="M12 11h6.5l2 2-2 2H12"/></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-2.562 1.465) scale(1.058)" stroke-width="1.655"><g stroke="#E6EDF3"><path d="M4 10a8 8 0 0 0 10 10z"/><path d="M9 15l3.5-3.5"/><path d="M7.5 20.5h5"/></g><circle fill="#E6EDF3" cx="13" cy="11" r="1.2"/><g stroke="#21BEC5"><path d="M15.5 4.5a4 4 0 0 1 4 4"/><path d="M15.5 1.5a7 7 0 0 1 7 7"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-1.726 0.346) scale(1.063)" stroke-width="1.647"><g stroke="#21BEC5"><path d="M5.8 12C11 12 12.5 5 18.2 5"/><path d="M5.8 12H18.2"/><path d="M5.8 12C11 12 12.5 19 18.2 19"/></g><circle fill="#E6EDF3" cx="4" cy="12" r="2"/><g stroke="#E6EDF3"><circle cx="20" cy="5" r="1.8"/><circle cx="20" cy="12" r="1.8"/><circle cx="20" cy="19" r="1.8"/></g></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30.55 52.37" width="28" height="48" fill="none" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(-5.267 -0.233) scale(1.111)" stroke-width="1.575"><g stroke="#E6EDF3"><path d="M6 3h8l4 4v14H6z"/><path d="M14 3v4h4"/><path d="M9 11h6"/></g><path stroke="#21BEC5" d="M9 16l2 2 4-4"/></g></svg>
|