adsa-cli 0.1.1 → 0.1.4

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/lib/config.mjs CHANGED
@@ -3,12 +3,15 @@
3
3
  * repo is never asked for: the config exists for the handful of things detection
4
4
  * gets wrong in a repo laid out unusually.
5
5
  */
6
- import { join } from "node:path";
6
+ import { basename, dirname, join } from "node:path";
7
7
  import { readdirSync } from "node:fs";
8
- import { exists, isDir, read, readJson } from "./fsx.mjs";
8
+ import { exists, isDir, read, readJson, rel, walk } from "./fsx.mjs";
9
9
 
10
10
  export const CONFIG_NAME = "adsa.config.json";
11
11
 
12
+ /** Never a workspace package, whatever a glob says. */
13
+ const IGNORED_DIRS = new Set(["node_modules", "dist", "build", "out", ".git", "coverage", "storybook-static"]);
14
+
12
15
  export const DEFAULTS = {
13
16
  /** Directories that hold component guides, in priority order. */
14
17
  guides: null,
@@ -24,80 +27,153 @@ export const DEFAULTS = {
24
27
  skip: [],
25
28
  };
26
29
 
27
- const GUIDE_DIRS = ["guidelines", "docs/components", "docs/guides", "documentation/components", "docs", "documentation", "www/content", "apps/docs/content"];
30
+ /** A guide living next to the component it documents: `Button/Button.spec.md`. */
31
+ export const COLOCATED_GUIDE = /\.(spec|docs?|guide)\.mdx?$/i;
32
+
33
+ /** Catch-alls: real guides often live here, but so does everything else. */
34
+ const BROAD_GUIDE_DIRS = new Set(["docs", "documentation"]);
35
+ const GUIDE_DIRS = ["guidelines", "docs/components", "docs/guides", "documentation/components", "www/content", "apps/docs/content", "docs", "documentation"];
28
36
  const SOURCE_DIRS = ["src/components", "Sources", "src/main/kotlin", "src", "packages/ui/src", "lib/components", "components"];
29
37
 
30
38
  export function loadConfig(root) {
31
39
  const file = readJson(join(root, CONFIG_NAME)) || {};
32
40
  const config = { ...DEFAULTS, ...file };
33
- config.guides = normalizeDirs(root, config.guides, GUIDE_DIRS);
34
- config.source = normalizeDirs(root, config.source, SOURCE_DIRS);
41
+ config.guidesExplicit = Boolean(file.guides);
42
+ config.guides = config.guides ? listed(root, config.guides) : defaultGuideDirs(root);
43
+ config.source = config.source ? listed(root, config.source) : defaultSourceDirs(root);
35
44
  config.configFile = exists(join(root, CONFIG_NAME)) ? CONFIG_NAME : null;
36
45
  return config;
37
46
  }
38
47
 
39
- function normalizeDirs(root, value, candidates) {
40
- const list = value ? (Array.isArray(value) ? value : [value]) : candidates;
41
- const found = list.filter((d) => isDir(join(root, d)));
42
- // Only the first match from the candidate list, so `docs` does not shadow `guidelines`.
43
- return value ? found : found.slice(0, 1);
48
+ function listed(root, value) {
49
+ const list = Array.isArray(value) ? value : [value];
50
+ return list.filter((d) => isDir(join(root, d)));
44
51
  }
45
52
 
46
53
  /**
47
- * Design systems usually live in a monorepo. When the given directory is a workspace
48
- * root with nothing to audit in it, pick the package that looks most like the design
49
- * system the one with the most guides and exported subpaths — and say so.
54
+ * Every specific guide directory that exists a repo can keep `guidelines/` and
55
+ * `docs/components/` and mean both. `docs/` and `documentation/` are catch-alls, so
56
+ * they are only believed when nothing more specific was found.
57
+ */
58
+ export function defaultGuideDirs(root) {
59
+ const found = GUIDE_DIRS.filter((d) => isDir(join(root, d)));
60
+ const specific = found.filter((d) => !BROAD_GUIDE_DIRS.has(d));
61
+ return specific.length ? specific : found.slice(0, 1);
62
+ }
63
+
64
+ function defaultSourceDirs(root) {
65
+ // A registry is a distribution format, not a folder somebody happened to name that:
66
+ // `registry.json` is the manifest that says so, and the components it ships are the
67
+ // system. The app's own `components/` is the site's chrome and would win otherwise.
68
+ if (exists(join(root, "registry.json")) && isDir(join(root, "registry"))) return ["registry"];
69
+
70
+ return SOURCE_DIRS.filter((d) => isDir(join(root, d))).slice(0, 1);
71
+ }
72
+
73
+ /**
74
+ * The monorepo root that declares `pkgDir` as one of its own workspaces, or null.
50
75
  *
51
- * @returns {{ dir:string, note:string|null, candidates:Array<{dir:string,name:string,score:number}> }}
76
+ * This is the only ancestor whose AGENTS.md, CI and agent surface belong to the
77
+ * package being audited. An ancestor that merely happens to contain the directory —
78
+ * a design system vendored inside an unrelated repository, a fixture inside this
79
+ * one — declares nothing, and lending it its parent's files would invent a score.
52
80
  */
53
- export function resolveTarget(root) {
54
- const pkg = readJson(join(root, "package.json"));
55
- if (pkg && (pkg.exports || isDir(join(root, "guidelines")))) return { dir: root, note: null, candidates: [] };
56
-
57
- const patterns = workspacePatterns(root, pkg);
58
- if (!patterns.length) return { dir: root, note: null, candidates: [] };
59
-
60
- const candidates = [];
61
- for (const pattern of patterns) {
62
- for (const dir of expand(root, pattern)) {
63
- const p = readJson(join(dir, "package.json"));
64
- if (!p || p.private === true) continue;
65
- const guides = countGuides(dir);
66
- const exports_ = Object.keys(p.exports || {}).filter((k) => k.startsWith("./")).length;
67
- const score = guides * 3 + exports_;
68
- if (score > 0) candidates.push({ dir, name: p.name || dir, score, guides, exports: exports_ });
69
- }
81
+ export function resolveRepoRoot(pkgDir) {
82
+ let dir = dirname(pkgDir);
83
+ for (let i = 0; i < 8; i++) {
84
+ const parent = dirname(dir);
85
+ if (declaresWorkspace(dir, pkgDir)) return dir;
86
+ if (parent === dir) return null;
87
+ dir = parent;
70
88
  }
71
- candidates.sort((a, b) => b.score - a.score);
72
- if (!candidates.length) return { dir: root, note: null, candidates: [] };
73
- return { dir: candidates[0].dir, note: `workspace: audited ${candidates[0].name}`, candidates };
89
+ return null;
90
+ }
91
+
92
+ /** Every workspace directory `root` declares, absolute. Empty when it declares none. */
93
+ export function workspaceDirs(root) {
94
+ const { include, exclude } = workspacePatterns(root, readJson(join(root, "package.json")));
95
+ const excluded = new Set(exclude.flatMap((pattern) => expand(root, pattern)));
96
+ return [...new Set(include.flatMap((pattern) => expand(root, pattern)))].filter((dir) => !excluded.has(dir));
74
97
  }
75
98
 
99
+ function declaresWorkspace(root, pkgDir) {
100
+ return workspaceDirs(root).includes(pkgDir);
101
+ }
102
+
103
+ /**
104
+ * The workspace globs a repository declares, negations kept: `!**\/fixtures/**` is
105
+ * how a repo says a directory full of package.json files is not a package.
106
+ *
107
+ * The pnpm file is read as its own `packages:` block rather than as every list item
108
+ * in the document — `onlyBuiltDependencies` is a list of dependency names, and
109
+ * reading it as workspace globs invents packages the repo does not have.
110
+ */
76
111
  function workspacePatterns(root, pkg) {
77
112
  const fromPkg = Array.isArray(pkg?.workspaces) ? pkg.workspaces : pkg?.workspaces?.packages || [];
78
113
  const yaml = read(join(root, "pnpm-workspace.yaml")) || "";
79
- const fromYaml = [...yaml.matchAll(/^\s*-\s*["']?([^"'\n]+)["']?/gm)].map((m) => m[1].trim());
80
- return [...new Set([...fromPkg, ...fromYaml])].filter(Boolean);
114
+ // Everything indented under `packages:`, comments and blank lines included — a
115
+ // comment between two entries used to end the list, and half the globs vanished.
116
+ const block = (yaml.match(/^packages:[ \t]*\n((?:(?:[ \t]+[^\n]*)?\n)*)/m) || [, ""])[1];
117
+ const fromYaml = [...block.matchAll(/^\s*-\s*["']?([^"'\n]+)["']?/gm)].map((m) => m[1].trim());
118
+ const all = [...new Set([...fromPkg, ...fromYaml])].filter(Boolean);
119
+ return { include: all.filter((g) => !g.startsWith("!")), exclude: all.filter((g) => g.startsWith("!")).map((g) => g.slice(1)) };
81
120
  }
82
121
 
83
122
  /** Only the `dir/*` and `dir` shapes real workspace globs use. */
123
+ /**
124
+ * A workspace glob as the package managers read it, segment by segment: `packages/*`,
125
+ * `packages/**`, `packages/*\/*` for a nested layout, `packages/@scope/*` for a scope
126
+ * folder. Reading only the first segment before the star — what this used to do —
127
+ * returns the scope folders themselves, which have no package.json, so a repo like
128
+ * Mantine or Radix looked like a repo with no packages at all.
129
+ */
84
130
  function expand(root, pattern) {
85
- const clean = pattern.replace(/\/\*\*$/, "/*");
86
- if (!clean.includes("*")) return isDir(join(root, clean)) ? [join(root, clean)] : [];
87
- const base = join(root, clean.slice(0, clean.indexOf("*")).replace(/\/$/, ""));
88
- if (!isDir(base)) return [];
131
+ let dirs = [root];
132
+ for (const segment of pattern.split("/").filter((s) => s && s !== ".")) {
133
+ const next = [];
134
+ for (const dir of dirs) {
135
+ if (segment === "**") next.push(dir, ...descend(dir, 2));
136
+ else if (segment.includes("*")) next.push(...children(dir).filter((d) => segmentMatch(segment, basename(d))));
137
+ else if (isDir(join(dir, segment))) next.push(join(dir, segment));
138
+ }
139
+ dirs = [...new Set(next)];
140
+ if (dirs.length > 4000) break;
141
+ }
142
+ return dirs;
143
+ }
144
+
145
+ function children(dir) {
89
146
  try {
90
- return readdirSync(base, { withFileTypes: true })
91
- .filter((e) => e.isDirectory() && !e.name.startsWith("."))
92
- .map((e) => join(base, e.name));
147
+ return readdirSync(dir, { withFileTypes: true })
148
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && !IGNORED_DIRS.has(e.name))
149
+ .map((e) => join(dir, e.name));
93
150
  } catch {
94
151
  return [];
95
152
  }
96
153
  }
97
154
 
98
- function countGuides(dir) {
99
- for (const candidate of ["guidelines", "docs", "documentation"]) {
100
- if (isDir(join(dir, candidate))) return 1;
155
+ /** Every directory under `dir`, `depth` levels down. `**` in a glob means this. */
156
+ function descend(dir, depth, level = 0, out = []) {
157
+ if (level >= depth) return out;
158
+ for (const child of children(dir)) {
159
+ out.push(child);
160
+ descend(child, depth, level + 1, out);
101
161
  }
102
- return 0;
162
+ return out;
163
+ }
164
+
165
+ function segmentMatch(segment, name) {
166
+ return new RegExp("^" + segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$").test(name);
167
+ }
168
+
169
+ /**
170
+ * How many guides this package actually has. The presence of a `docs/` folder says
171
+ * nothing — a package with one README-shaped page and a package with ninety component
172
+ * guides both used to score 1, and the wrong one won.
173
+ */
174
+ export function countGuides(dir) {
175
+ let n = 0;
176
+ for (const d of defaultGuideDirs(dir)) n += walk(join(dir, d), [".md", ".mdx"], 6).length;
177
+ for (const s of defaultSourceDirs(dir)) n += walk(join(dir, s), [".md", ".mdx"], 6).filter((f) => COLOCATED_GUIDE.test(f)).length;
178
+ return n;
103
179
  }
package/lib/history.mjs CHANGED
@@ -5,22 +5,24 @@ import { readJson } from "./fsx.mjs";
5
5
 
6
6
  export const DIR = ".adsa";
7
7
 
8
- export function historyPath(root) {
9
- return join(root, DIR, "history.json");
8
+ /** Takes the directory the run writes into, not the repository root: with --out
9
+ * that is somewhere else entirely, and the history has to travel with the score. */
10
+ export function historyPath(dir) {
11
+ return join(dir, "history.json");
10
12
  }
11
13
 
12
- export function readHistory(root) {
13
- const json = readJson(historyPath(root));
14
+ export function readHistory(dir) {
15
+ const json = readJson(historyPath(dir));
14
16
  return Array.isArray(json) ? json : [];
15
17
  }
16
18
 
17
19
  /** Appends a run, collapsing same-day reruns so the file does not grow per invocation. */
18
- export function appendHistory(root, entry) {
19
- const history = readHistory(root);
20
+ export function appendHistory(dir, entry) {
21
+ const history = readHistory(dir);
20
22
  const day = entry.date.slice(0, 10);
21
23
  const filtered = history.filter((h) => h.date.slice(0, 10) !== day);
22
24
  filtered.push(entry);
23
- write(historyPath(root), filtered);
25
+ write(historyPath(dir), filtered);
24
26
  return filtered;
25
27
  }
26
28
 
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The measured field. A score out of 45 means nothing on its own — the question a
3
+ * reader always asks first is "is that good?" — so every place a score is shown can
4
+ * answer it with the same public design systems, audited at a named commit.
5
+ *
6
+ * This is not a ranking of design systems. It measures one thing: what a coding agent
7
+ * can find in the repository. A system whose documentation lives on a website scores
8
+ * low here and may still be a fine design system for people.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import { fileURLToPath } from "node:url";
12
+ import { dirname, join } from "node:path";
13
+
14
+ const here = dirname(fileURLToPath(import.meta.url));
15
+ export const REFERENCE = JSON.parse(readFileSync(join(here, "..", "rubric", "reference.json"), "utf8"));
16
+
17
+ /** Where a score sits in the field: how many of the reference systems it beats. */
18
+ export function standing(total) {
19
+ const systems = REFERENCE.systems;
20
+ const below = systems.filter((s) => s.total < total).length;
21
+ const best = systems[0];
22
+ return {
23
+ count: systems.length,
24
+ below,
25
+ best,
26
+ median: systems[Math.floor(systems.length / 2)].total,
27
+ sentence:
28
+ total >= best.total
29
+ ? `${total}/45 is at or above ${best.name} (${best.total}), the highest of the ${systems.length} public design systems measured with this rubric.`
30
+ : `${total}/45 sits above ${below} of the ${systems.length} public design systems measured with this rubric; ${best.name} leads at ${best.total}.`,
31
+ };
32
+ }
package/lib/report.mjs CHANGED
@@ -10,7 +10,8 @@
10
10
  * against frozen real audits, and ported back here as a copy.
11
11
  */
12
12
  import { RUBRIC } from "./score.mjs";
13
- import { badgeColor, badgeMarkdown } from "./badge.mjs";
13
+ import { band as scoreBand, badgeColor, badgeMarkdown } from "./badge.mjs";
14
+ import { standing } from "./reference.mjs";
14
15
 
15
16
  /** What the accessibility dimension is called on each platform. */
16
17
  const A11Y_STAT_LABEL = {
@@ -30,16 +31,14 @@ const PLATFORM_LABEL = {
30
31
  const MARK = `<svg class="mark" viewBox="0 0 214 135" fill="currentColor" aria-hidden="true"><path d="M211.6 71C211.3 71.1 210.8 71.1 210.5 71.1C210.1 71.1 209.9 71.2 209.8 71.6C208.4 78.1 206.1 82.6 201.9 86.9C196.9 92.3 190.7 95.4 183.8 96.1C181.2 96.4 180.3 96.6 179.1 97.3C176.7 98.6 175.3 100.8 175 103.8C174.5 108.8 173.8 111.7 172.2 114.9C169.5 120.4 165 124.9 159.5 127.7C156.5 129.2 154 130 150 130.9L148.5 131.2L148.6 132.3L148.6 133.5L150 133.4C150.7 133.3 152.1 133.1 153 132.9C163.5 130.5 172 123.3 175.7 113.7C176.9 110.7 177.4 108.3 177.7 104.8C177.9 102.3 178.2 101.6 179.4 100.4C180.5 99.4 181.2 99.1 183.3 98.9C194.4 98 204.5 91.1 209.6 80.8C210.9 78.2 212.2 73.7 212.2 71.7L212.2 70.9L211.6 71Z"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M182.5 58.5C185.2 59.4 187.4 61.6 188.3 64.4C188.9 65.9 188.9 68.8 188.3 70.4C187.8 71.8 186.9 73.2 185.8 74.2C184.1 75.9 182.5 76.5 179.1 76.9C174.8 77.3 171.3 78.4 167.6 80.5C161.8 83.8 157.4 89 155.4 95.1C154.6 97.4 154.3 99.2 154.1 101.9C154 103.1 153.9 104.7 153.7 105.4C152.9 108.8 149.8 112 146.4 112.8C145.3 113 142.7 113 141.5 112.8C138.8 112.2 136.1 110.2 134.8 107.8C134 106.1 133.6 104.7 133.5 102.3C133.1 96.5 130.9 91.1 126.9 86.4C123.8 82.8 119.1 79.8 114.1 78.2C103.7 74.8 92 77.5 85 84.9C81.1 88.9 78.8 94.6 78.8 99.8C78.8 102.8 77.8 105.3 75.9 107.1C74.1 108.9 72.3 109.7 69.6 109.9C66.3 110.1 63.7 109.1 61.7 107.1C60 105.4 59.3 103.8 58.8 100.5C58.1 96.6 56.9 93.6 54.5 90C53.1 87.8 49.4 84.2 47.1 82.7C41.9 79.2 36.7 77.5 30.3 77.1C28.5 77 26.6 76.9 26.2 76.8C23.2 76 20.6 73.4 19.9 70.7C19.5 69.2 19.5 66.8 20 65.3C20.5 63.6 21.7 61.9 23.2 60.8C25.1 59.5 26.3 59.1 30.3 58.9C34.1 58.7 36.2 58.3 39.5 57.1C49.4 53.4 56.6 45.2 58 35.9C58.7 31.9 59.5 30 61.5 28.2C63.4 26.5 65.4 25.8 68.2 25.8C71.2 25.8 73.3 26.6 75.3 28.5C77.2 30.4 78 32.2 78.2 35.7C78.3 37.1 78.5 38.9 78.7 39.7C79.2 41.5 80.3 44.2 81.3 45.9C84.8 51.6 91.2 55.8 99 57.5C100.7 57.8 101.6 57.9 104.5 57.9C108.5 57.9 110.4 57.6 114.1 56.4C122.6 53.6 129.6 47 132.4 39C133.2 36.7 133.5 34.9 133.7 31.8C133.9 30 134 28.7 134.3 27.9C135.7 23.7 139.8 21.1 144.3 21.5C147.1 21.7 149 22.7 150.8 24.6C152.8 26.7 153.4 28.8 153.4 32.8C153.5 35.5 153.8 37.8 154.5 39.8C156.9 46.6 161.5 51.7 168.1 54.9C171.8 56.8 175.1 57.7 179.3 58C180.6 58.1 182 58.3 182.5 58.5ZM184.8 69.4L185.9 70L187 70.5L186.7 71.2C186.1 72.7 184.6 74.1 182.8 75C182 75.4 181.1 75.5 179 75.7C173.6 76.3 169.4 77.9 164.7 81C162.4 82.6 159.2 85.9 157.6 88.1C154.6 92.7 153.4 96.3 152.9 102.9C152.8 104.3 152.6 105.5 152.3 106.2C151.5 108.3 149.3 110.6 147.7 111C147.1 111.2 147.1 111.2 146.6 110.1C146.3 109.6 146.2 109.1 146.3 109C146.4 109 146.9 108.6 147.6 108.1C148.4 107.5 148.8 107.1 149.3 106.1C149.9 104.8 149.9 104.7 150.4 99.8C151.1 93.1 154.2 87 159.5 81.9C164.6 77 171.2 73.9 178.4 73.1C179.8 72.9 181.1 72.7 181.5 72.5C182.6 72.1 183.7 71.2 184.3 70.3L184.8 69.4ZM181.9 59.5C183.9 60.1 185.6 61.5 186.6 63.4C186.9 64 186.8 64.1 185.7 64.7L184.8 65.2L184.1 64.3C182.7 62.5 181.6 62 178.7 61.8C170.5 61.1 163.1 57.7 157.7 52.1C155.5 49.8 153.9 47.7 152.6 45.2C150.4 40.8 149.9 38.7 149.7 33.7C149.4 28.8 149 27.6 146.7 26.1C146.1 25.7 145.7 25.3 145.7 25.2C145.7 25.1 145.9 24.7 146.3 24.2L146.8 23.3L147.5 23.6C148.7 24.1 149.7 24.9 150.4 25.9C152 28 152.3 29 152.4 33.4C152.5 36.8 152.9 38.8 154 41.6C157.9 51.2 168.1 58.4 178.8 59C179.8 59.1 181.2 59.3 181.9 59.5ZM182.5 64.2C182.8 64.5 183.3 65.2 183.5 65.7C184.3 67.2 184 68.8 182.8 70.2C181.8 71.3 180.7 71.8 178.3 72C175.6 72.3 173.9 72.6 171.2 73.6C160 77.3 151.9 86.2 149.7 97.2C149.5 98.2 149.2 100.2 149.1 101.6C148.9 104.5 148.6 105.6 147.5 106.7C145.1 109.2 140.7 108.5 139.2 105.3C138.8 104.6 138.6 103.9 138.5 102C138.1 97.9 137.4 94.9 135.9 91.5C132.1 82.9 125.1 76.6 115.8 73.6C111.6 72.2 110.2 72 105 71.9C100 71.9 98.4 72.2 94.5 73.4C85.9 76.2 79.3 82 75.9 89.8C74.7 92.7 74.1 95.2 73.8 99C73.6 102.1 73.3 102.9 72.1 104C70.1 105.7 66.9 105.5 65.2 103.7C64.3 102.7 64 102 63.7 99.8C62.7 93.4 59.2 86.8 54.2 82.1C47.8 76.1 39.8 72.7 30.7 72.2C28 72.1 27.3 72 26.7 71.7C25.1 70.9 24.2 69.1 24.4 67.5C24.6 66.4 25.5 65.1 26.6 64.5C27.4 64 27.6 64 30.7 63.9C35.5 63.7 39.3 62.8 43.7 60.7C52.1 56.9 58.6 50.1 61.6 42.1C62.3 40 62.7 38.4 63.1 35.8C63.4 33.6 63.8 32.7 64.8 31.8C65.9 30.9 66.7 30.6 68.2 30.6C69.4 30.6 69.8 30.7 70.6 31.2C72.4 32.2 73 33.4 73.3 36.6C73.5 39.4 73.9 41.3 74.8 43.7C76.2 47.6 78.3 50.8 81.3 53.7C91.1 63.3 107.2 65.6 120.2 59.2C126.9 55.9 132.2 50.7 135.5 44.2C137.4 40.5 138.4 37 138.7 32.5C138.8 29.7 139.1 28.7 140.2 27.7C141.1 26.8 142.2 26.3 143.4 26.3C144.9 26.3 145.7 26.6 146.8 27.6C148.2 28.9 148.4 29.4 148.5 33.6C148.7 37.8 149.2 40.2 150.6 43.4C154.8 53.6 165 61.1 176.5 62.6C177.7 62.8 179 62.9 179.5 62.9C180.4 62.9 181.8 63.5 182.5 64.2ZM139.6 107.8C140.2 108.3 140.9 108.8 141.1 108.9C141.5 109 141.5 109 141 110C140.7 110.6 140.4 111.1 140.3 111.1C139.7 111.1 138 110 137.1 109.1C135.4 107.2 134.9 105.9 134.6 102.1C133.7 92.8 129.3 85.5 121.6 80.5C118.6 78.6 114.3 76.8 110.5 76.1C107.9 75.5 101.7 75.6 99.1 76.2C93.1 77.5 88.6 79.8 84.8 83.6C82.6 85.8 81.2 87.7 80 90.3C78.5 93.5 77.9 95.8 77.6 100C77.5 102.4 77.4 102.7 76.8 104C76.1 105.4 74.8 106.9 73.6 107.6C72.8 108 71.7 108.3 71.8 108.1C71.9 108 71.7 107.5 71.5 107L71.1 106.1L72.1 105.3C73.4 104.5 74.3 103.3 74.6 102C74.7 101.5 74.8 100.2 74.9 99.1C75.4 90.5 79.8 83 87.3 77.9C88.8 76.9 92.2 75.3 94.3 74.6C101.3 72.3 108.4 72.3 115.5 74.6C123.8 77.3 130.7 83.2 134.3 90.7C136.2 94.5 136.9 97.3 137.4 102.2C137.7 105.4 138.1 106.3 139.6 107.8ZM140 23.3L140.7 24.3L141.3 25.3L140.7 25.8C138.7 27.3 137.7 29 137.7 30.9C137.7 32.7 137.3 35.7 136.8 37.8C134.8 45.8 129.2 52.9 121.6 57.2C118.1 59.2 114.4 60.6 110 61.3C107.8 61.7 101.9 61.8 99.7 61.4C92.7 60.3 86.3 57.2 81.9 52.8C77.5 48.3 74.9 42.8 74.4 36.8C74.3 35.5 74.1 34.1 74 33.6C73.7 32.5 72.6 31.2 71.4 30.3C70.4 29.7 70.3 29.6 70.5 29.4C70.7 29.2 70.8 29 70.8 28.9C70.8 28.8 71 28.5 71.3 28.1L71.7 27.6L72.8 28.1C74.1 28.8 75.6 30.4 76.3 31.9C76.8 32.9 76.9 33.6 77.1 35.1C77.5 40 78.1 42.2 79.6 45C81.5 48.5 83.5 50.8 86.5 53.1C92.3 57.4 99.7 59.6 106.9 58.9C111.8 58.5 115.7 57.3 120.1 54.9C125.1 52.2 129.6 47.5 132.1 42.4C133.8 38.8 134.6 35.8 134.8 32.1C134.9 29.6 135.1 28.5 135.8 27.2C136.4 25.9 137.9 24.4 139.1 23.8L140 23.3ZM62.7 100.2C63 102.8 63.8 104.3 65.5 105.4C66 105.8 66.4 106.1 66.4 106.1C66.4 106.2 65.5 107.8 65.4 108C65.2 108.3 63.2 107.1 62.4 106.1C60.9 104.6 60.3 103.2 59.8 100.3C59.3 97.3 58.6 95.1 57.1 92.3C55.7 89.5 54.1 87.4 51.5 84.9C48.2 81.7 44.1 79.2 39.5 77.7C36.4 76.7 33.9 76.2 30.3 76C27 75.8 26.1 75.7 24.8 75C23.9 74.5 22.3 73 21.9 72.2C21.5 71.4 21.5 71.3 22.9 70.5L23.6 70.1L24.1 70.9C25.4 72.6 26.5 73.1 29.7 73.3C34.8 73.5 38.5 74.3 42.5 75.9C48.7 78.4 53.9 82.5 57.6 87.7C60.2 91.4 62.1 96.2 62.7 100.2ZM64.1 27.9C64.7 27.7 64.7 27.8 65.2 28.7C65.6 29.2 65.8 29.7 65.8 29.7C65.8 29.7 65.2 30.1 64.6 30.6C63.1 31.7 62.3 33.1 62.1 35.5C61.4 41.3 58.5 47.4 54.1 52C50.2 56.1 45.5 59.1 40.1 61C36.7 62.2 34.6 62.6 30.9 62.7C26.6 62.9 25.5 63.4 24.1 65.3C23.8 65.8 23.5 66.2 23.4 66.2C23.3 66.2 22.9 65.8 22.4 65.4C21.8 64.8 21.7 64.6 21.8 64.2C22 63.4 23.7 61.7 24.9 61.1C26.3 60.3 27.3 60.1 30.5 60C34.9 59.9 38.5 59 42.7 57C49 54 53.8 49.3 56.8 43.4C57.7 41.6 58.7 38.5 59 36.9C59.1 36.2 59.3 35.1 59.4 34.4C59.5 33.7 59.8 32.7 60 32.2C60.7 30.3 62.6 28.3 64.1 27.9Z"/> <path d="M141.7 131.3C141.5 131.2 140.4 131 139.1 130.7C129.3 128.5 121.4 122.8 117.3 114.7C115.5 111.4 114.6 108.4 114.1 104.6C113.8 102.2 113.5 101.3 112.8 100.1C110.1 95.9 104 95.2 100.5 98.8C99.2 100.2 98.7 101.4 98.3 103.9C97.8 108 96.6 112 94.9 115.1C90.4 123.2 82.6 128.8 72.7 130.9L71.2 131.2L71.3 131.9C71.5 133.4 71.5 133.5 72.4 133.4C72.8 133.4 74.1 133.2 75.2 133C80.6 131.8 85.4 129.3 90 125.5C95.9 120.5 100.1 112.7 101 105.1C101.2 102.8 101.6 101.8 102.3 100.9C104.1 98.8 107.1 98.6 109.3 100.3C110.5 101.3 111.1 102.5 111.4 104.9C112 109.9 113.7 114.7 116.7 119.2C118.6 122.1 122.4 125.8 125.4 127.8C129.1 130.3 133.8 132.3 137.7 133.1C140.1 133.6 141.4 133.6 141.6 133.1C141.9 132.1 141.9 131.4 141.7 131.3Z"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M213.6 65.3C213.1 60.4 211.3 55.6 208.5 51.2C206.6 48.3 204.7 46.1 202.1 43.8C196.2 38.7 188.9 35.3 181.8 34.5C179.7 34.2 178.6 33.7 177.9 32.7C177.3 31.9 177.3 31.8 177 28.3C176.4 21.5 174.3 16.1 170.3 11.2C167 7.2 162.1 3.7 156.8 1.9C151.1 -0.2 143.5 -0.6 136.5 0.8C128.2 2.5 120.7 7.4 115.7 14.4C112.5 18.9 110.3 24.9 109.7 30.3C109.6 32 109.4 32.6 108.6 33.5C107 35.4 103.8 35.4 102.1 33.5C101.4 32.7 101.2 32.1 100.9 30C100 22.5 96.6 15.8 91.1 10.6C82.7 2.5 71.1 -0.8 59.4 1.5C55.5 2.3 50.9 4.2 47.6 6.3C39.4 11.7 34 20.7 33.3 30.1C33.2 32.4 33 33 32.2 34C31.2 35 30.6 35.2 28.4 35.6C21.2 36.6 14.9 39.7 9.7 45C6.8 47.9 5.1 50.1 3.4 53.4C2 56.2 1.1 58.8 0.4 61.6C0 63.4 -0 64.1 0 67.9C0 71.5 0.1 72.4 0.4 74C3 85.7 11.8 95.2 23.7 99.4C25.9 100.1 29.2 100.9 31.4 101.2C33.2 101.4 34.4 102 35.1 103.1C35.5 103.8 35.6 104.2 35.8 106C36 108.9 36.2 110.6 36.9 113.1C39.3 122.7 46.4 130.2 56.2 133.5C59.5 134.6 62.2 135 66.3 135C74.5 135 81.2 133.1 87.5 128.8C95.8 123.2 101.1 114.4 102.2 104.3C102.5 101.5 104.1 100.1 106.5 100.3C108.7 100.5 109.9 102 110.2 104.7C111.6 116.4 118.3 125.8 129 131.2C134.2 133.8 138.7 134.8 144.8 134.8C150.9 134.8 155.4 133.8 160.6 131.3C171.3 126.2 177.9 116.5 178.8 104.7C178.9 102.8 179.3 101.9 180.3 101.1C181.1 100.4 181.7 100.2 183.6 100C185.7 99.8 188.7 99.2 190.7 98.6C193.2 97.7 197.1 95.8 199.4 94.2C201.7 92.6 205 89.6 206.7 87.4C210.2 83.1 212.6 77.7 213.3 72.6C213.6 70.7 213.7 67.2 213.6 65.3ZM210.5 71.1C210.8 71.1 211.3 71.1 211.6 71L212.2 70.9L212.2 71.7C212.2 73.7 210.9 78.2 209.6 80.8C204.5 91.1 194.4 98 183.3 98.9C181.2 99.1 180.5 99.4 179.4 100.4C178.2 101.6 177.9 102.3 177.7 104.8C177.4 108.3 176.9 110.7 175.7 113.7C172 123.3 163.5 130.5 153 132.9C152.1 133.1 150.7 133.3 150 133.4L148.6 133.5L148.6 132.3L148.5 131.2L150 130.9C154 130 156.5 129.2 159.5 127.7C165 124.9 169.5 120.4 172.2 114.9C173.8 111.7 174.5 108.8 175 103.8C175.3 100.8 176.7 98.6 179.1 97.3C180.3 96.6 181.2 96.4 183.8 96.1C190.7 95.4 196.9 92.3 201.9 86.9C206.1 82.6 208.4 78.1 209.8 71.6C209.9 71.2 210.1 71.1 210.5 71.1ZM196.9 41.3C204.3 46.1 209.5 53 211.6 60.7C211.8 61.7 212 62.8 212 63.2C212 63.9 212 63.9 211.4 63.9C211 63.9 210.5 64 210.2 64.1C209.8 64.2 209.8 64.2 209.6 63.4C208.3 58.2 206.1 53.9 202.7 49.9C197.7 44.1 190.4 39.9 183.1 38.7C181.9 38.5 180.4 38.2 179.8 38.1C177.4 37.7 175.1 35.9 174 33.6C173.3 32.3 173.3 32.1 173.1 29.5C172.7 22 170.4 16.7 165.5 11.8C161.4 7.7 156.6 5.2 150.8 4.2C149.6 3.9 148.4 3.7 148.2 3.7C147.8 3.6 147.8 3.5 148 3C148.1 2.6 148.1 2.1 148.1 1.8L148.1 1.3L149.9 1.4C154.5 1.7 159.9 3.9 164.5 7.2C166.2 8.4 169.3 11.6 170.6 13.4C172.8 16.5 174.5 20.3 175.2 23.9C175.7 26 175.8 26.9 176 29.8C176.1 31.7 176.2 32.1 176.7 32.9C177.7 34.6 178.7 35.2 181.9 35.6C187.5 36.4 192.2 38.2 196.9 41.3ZM206.9 58.8C207.6 60.4 208.5 63.6 208.7 65.1C208.7 65.4 208.7 67 208.7 68.7C208.7 71.2 208.6 72.1 208.2 73.7C206.2 81.5 200.5 88.5 193.1 92.2C189.8 93.9 186.7 94.8 182.9 95.1C181.9 95.2 180.6 95.5 180 95.7C177.7 96.4 175.4 98.6 174.5 101C174.2 101.7 174 102.8 173.9 104.3C173.6 108.5 172.5 112.1 170.8 115.4C166.8 122.8 159.1 128.1 150.1 129.7C148.2 130 147.4 130.1 144.2 130C140 129.9 138.9 129.7 135.4 128.5C130.3 126.8 125.5 123.7 122.2 120C118.8 116.2 116.2 110.8 115.5 106C115.3 104.9 115.1 103.4 115 102.7C114.4 99.4 111.7 96.5 108.4 95.6C104.2 94.5 100.1 96.4 98.2 100.3C97.7 101.4 97.5 102.1 97.2 103.9C96.7 108 95.6 111.5 93.8 114.8C89.7 122.4 81.3 128.2 72.5 129.7C66.5 130.8 61.8 130.4 56.9 128.5C47.4 124.8 41.6 117 40.8 106.9C40.6 103.5 40.2 102.1 39.3 100.7C37.8 98.4 35.5 96.8 32.9 96.5C29.9 96 28.1 95.6 26.2 95C15.5 91.6 7.2 82.7 5.1 72.3C4.6 69.7 4.6 65.5 5.2 62.9C7.1 54.3 12.8 47 20.7 43C23 41.9 26 40.8 28 40.6C28.9 40.4 30.1 40.3 30.7 40.2C33.4 39.8 36.2 37.6 37.4 35C37.9 33.9 38.1 33.4 38.2 31.4C38.4 28.7 38.9 25.8 39.6 23.8C43.3 13.7 52.9 6.7 64.5 5.6C73.1 4.9 83 8.8 89 15.4C90.7 17.2 92.3 19.6 93.4 21.9C95 25.3 95.6 27.3 96.2 31.7C96.3 32.4 96.6 33.6 96.8 34.1C97.6 36.4 100 38.6 102.4 39.4C104.2 40.1 106.8 40 108.4 39.4C112.2 38 114.3 35.1 114.7 30.7C114.9 27.5 115.8 24.4 117.4 21.1C121.9 11.9 130.8 6 142 4.9C150.8 4 159 6.8 164.7 12.6C167.6 15.5 169.5 18.4 170.7 22.1C171.4 24.1 172 27.6 172 29.3C172 32.4 172.8 34.5 174.7 36.5C176.2 38.1 178 39 180 39.3C180.8 39.4 182.3 39.6 183.3 39.8C193.5 41.7 203.1 49.4 206.9 58.8ZM139.1 130.7C140.4 131 141.5 131.2 141.7 131.3C141.9 131.4 141.9 132.1 141.6 133.1C141.4 133.6 140.1 133.6 137.7 133.1C133.8 132.3 129.1 130.3 125.4 127.8C122.4 125.8 118.6 122.1 116.7 119.2C113.7 114.7 112 109.9 111.4 104.9C111.1 102.5 110.5 101.3 109.3 100.3C107.1 98.6 104.1 98.8 102.3 100.9C101.6 101.8 101.2 102.8 101 105.1C100.1 112.7 95.9 120.5 90 125.5C85.4 129.3 80.6 131.8 75.2 133C74.1 133.2 72.8 133.4 72.4 133.4C71.5 133.5 71.5 133.4 71.3 131.9L71.2 131.2L72.7 130.9C82.6 128.8 90.4 123.2 94.9 115.1C96.6 112 97.8 108 98.3 103.9C98.7 101.4 99.2 100.2 100.5 98.8C104 95.2 110.1 95.9 112.8 100.1C113.5 101.3 113.8 102.2 114.1 104.6C114.6 108.4 115.5 111.4 117.3 114.7C121.4 122.8 129.3 128.5 139.1 130.7ZM139.9 1.5C140.7 1.5 140.7 1.5 140.8 2.2C140.9 3.4 140.9 3.8 140.9 3.8C140.6 3.8 136 4.8 135 5.1C127 7.4 120.2 13.1 116.5 20.6C114.8 23.9 114.1 26.6 113.6 30.3C113.4 32.4 113 33.9 112.5 34.7C110.6 37.9 106.9 39.5 103.5 38.6C101.1 38 99 36.3 98.1 34.3C97.6 33.3 97.4 32.7 96.9 29.2C95.9 22.9 92.6 16.9 87.6 12.5C83.4 8.8 77.9 6.1 71.7 4.8L70.3 4.5L70.5 3.9C71.1 2.1 70.9 2.2 72.2 2.4C75.2 2.7 80.3 4.4 83.4 6.2C92.7 11.5 98.6 20 99.9 30.4C100.2 32.7 100.6 33.5 101.6 34.5C103.2 36.1 105.5 36.5 107.5 35.6C109.4 34.8 110.8 32.7 110.8 30.7C110.8 29.7 111.3 26.8 111.8 25C113.3 19.6 116 15 120.1 11C122.9 8.2 125.2 6.5 128.5 4.8C132.3 2.9 137.2 1.5 139.9 1.5ZM62.3 131.2C63.5 131.4 64.4 131.6 64.5 131.6C64.5 131.6 64.4 131.9 64.3 132.4C64.2 132.8 64.2 133.3 64.2 133.5C64.3 133.9 64.2 133.9 62.8 133.8C59.4 133.5 56.3 132.6 52.7 130.8C49.7 129.4 47.4 127.7 45.1 125.4C40.1 120.4 37.4 114.3 36.9 106.4C36.7 103.5 36.4 102.6 35.2 101.6C34.2 100.7 33.3 100.3 31 100C21.3 98.6 12.7 93.6 7.1 86C4.2 82.1 1.8 76.6 1.5 72.9L1.4 71.7L2.5 71.6C3.6 71.4 3.9 71.6 3.9 72.3C3.9 73 5.3 77.2 6.3 79.2C8.8 84.5 12.3 88.5 17.4 91.9C21.3 94.6 25.5 96.3 29.8 97C31 97.3 32.5 97.5 33 97.6C35.4 98 37.6 99.7 38.7 101.9C39.2 102.8 39.4 103.4 39.5 105C40 110.4 40.9 114 42.3 116.9C46 124.3 53.4 129.6 62.3 131.2ZM61.7 2.3C63.6 2.2 63.5 2.1 63.6 3.7C63.7 4 63.7 4.3 63.6 4.4C63.5 4.4 62.5 4.7 61.3 4.9C53.1 6.5 46 11 41.8 17.3C38.8 21.7 37.7 25.3 37.1 31.7C36.9 33.8 36.4 35 35.1 36.5C33.5 38.3 32 38.9 28.2 39.4C25.3 39.8 20.7 41.5 17.6 43.5C13.4 46.1 8.9 51 6.6 55.5C5.7 57.3 4.7 60.1 4.1 62.4C3.9 63.5 3.6 64.4 3.5 64.5C3.5 64.5 2.9 64.6 2.3 64.7C1.2 64.8 1.2 64.8 1.2 64.2C1.2 63.4 1.9 60 2.4 58.6C3 56.7 5 52.6 6.1 50.9C9.5 46 13.6 42.3 18.8 39.7C22.2 38 25.3 37.1 29 36.6C32.8 36.1 34.2 34.4 34.5 30.1C34.9 21.4 40.1 12.7 47.8 7.5C51.9 4.7 57.6 2.6 61.7 2.3Z"/> <path d="M64.5 131.6C64.4 131.6 63.5 131.4 62.3 131.2C53.4 129.6 46 124.3 42.3 116.9C40.9 114 40 110.4 39.5 105C39.4 103.4 39.2 102.8 38.7 101.9C37.6 99.7 35.4 98 33 97.6C32.5 97.5 31 97.3 29.8 97C25.5 96.3 21.3 94.6 17.4 91.9C12.3 88.5 8.8 84.5 6.3 79.2C5.3 77.2 3.9 73 3.9 72.3C3.9 71.6 3.6 71.4 2.5 71.6L1.4 71.7L1.5 72.9C1.8 76.6 4.2 82.1 7.1 86C12.7 93.6 21.3 98.6 31 100C33.3 100.3 34.2 100.7 35.2 101.6C36.4 102.6 36.7 103.5 36.9 106.4C37.4 114.3 40.1 120.4 45.1 125.4C47.4 127.7 49.7 129.4 52.7 130.8C56.3 132.6 59.4 133.5 62.8 133.8C64.2 133.9 64.3 133.9 64.2 133.5C64.2 133.3 64.2 132.8 64.3 132.4C64.4 131.9 64.5 131.6 64.5 131.6Z"/> <path d="M63.6 3.7C63.5 2.1 63.6 2.2 61.7 2.3C57.6 2.6 51.9 4.7 47.8 7.5C40.1 12.7 34.9 21.4 34.5 30.1C34.2 34.4 32.8 36.1 29 36.6C25.3 37.1 22.2 38 18.8 39.7C13.6 42.3 9.5 46 6.1 50.9C5 52.6 3 56.7 2.4 58.6C1.9 60 1.2 63.4 1.2 64.2C1.2 64.8 1.2 64.8 2.3 64.7C2.9 64.6 3.5 64.5 3.5 64.5C3.6 64.4 3.9 63.5 4.1 62.4C4.7 60.1 5.7 57.3 6.6 55.5C8.9 51 13.4 46.1 17.6 43.5C20.7 41.5 25.3 39.8 28.2 39.4C32 38.9 33.5 38.3 35.1 36.5C36.4 35 36.9 33.8 37.1 31.7C37.7 25.3 38.8 21.7 41.8 17.3C46 11 53.1 6.5 61.3 4.9C62.5 4.7 63.5 4.4 63.6 4.4C63.7 4.3 63.7 4 63.6 3.7Z"/> <path d="M140.8 2.2C140.7 1.5 140.7 1.5 139.9 1.5C137.2 1.5 132.3 2.9 128.5 4.8C125.2 6.5 122.9 8.2 120.1 11C116 15 113.3 19.6 111.8 25C111.3 26.8 110.8 29.7 110.8 30.7C110.8 32.7 109.4 34.8 107.5 35.6C105.5 36.5 103.2 36.1 101.6 34.5C100.6 33.5 100.2 32.7 99.9 30.4C98.6 20 92.7 11.5 83.4 6.2C80.3 4.4 75.2 2.7 72.2 2.4C70.9 2.2 71.1 2.1 70.5 3.9L70.3 4.5L71.7 4.8C77.9 6.1 83.4 8.8 87.6 12.5C92.6 16.9 95.9 22.9 96.9 29.2C97.4 32.7 97.6 33.3 98.1 34.3C99 36.3 101.1 38 103.5 38.6C106.9 39.5 110.6 37.9 112.5 34.7C113 33.9 113.4 32.4 113.6 30.3C114.1 26.6 114.8 23.9 116.5 20.6C120.2 13.1 127 7.4 135 5.1C136 4.8 140.6 3.8 140.9 3.8C140.9 3.8 140.9 3.4 140.8 2.2Z"/> <path d="M140.7 24.3L140 23.3L139.1 23.8C137.9 24.4 136.4 25.9 135.8 27.2C135.1 28.5 134.9 29.6 134.8 32.1C134.6 35.8 133.8 38.8 132.1 42.4C129.6 47.5 125.1 52.2 120.1 54.9C115.7 57.3 111.8 58.5 106.9 58.9C99.7 59.6 92.3 57.4 86.5 53.1C83.5 50.8 81.5 48.5 79.6 45C78.1 42.2 77.5 40 77.1 35.1C76.9 33.6 76.8 32.9 76.3 31.9C75.6 30.4 74.1 28.8 72.8 28.1L71.7 27.6L71.3 28.1C71 28.5 70.8 28.8 70.8 28.9C70.8 29 70.7 29.2 70.5 29.4C70.3 29.6 70.4 29.7 71.4 30.3C72.6 31.2 73.7 32.5 74 33.6C74.1 34.1 74.3 35.5 74.4 36.8C74.9 42.8 77.5 48.3 81.9 52.8C86.3 57.2 92.7 60.3 99.7 61.4C101.9 61.8 107.8 61.7 110 61.3C114.4 60.6 118.1 59.2 121.6 57.2C129.2 52.9 134.8 45.8 136.8 37.8C137.3 35.7 137.7 32.7 137.7 30.9C137.7 29 138.7 27.3 140.7 25.8L141.3 25.3L140.7 24.3Z"/> <path d="M65.2 28.7C64.7 27.8 64.7 27.7 64.1 27.9C62.6 28.3 60.7 30.3 60 32.2C59.8 32.7 59.5 33.7 59.4 34.4C59.3 35.1 59.1 36.2 59 36.9C58.7 38.5 57.7 41.6 56.8 43.4C53.8 49.3 49 54 42.7 57C38.5 59 34.9 59.9 30.5 60C27.3 60.1 26.3 60.3 24.9 61.1C23.7 61.7 22 63.4 21.8 64.2C21.7 64.6 21.8 64.8 22.4 65.4C22.9 65.8 23.3 66.2 23.4 66.2C23.5 66.2 23.8 65.8 24.1 65.3C25.5 63.4 26.6 62.9 30.9 62.7C34.6 62.6 36.7 62.2 40.1 61C45.5 59.1 50.2 56.1 54.1 52C58.5 47.4 61.4 41.3 62.1 35.5C62.3 33.1 63.1 31.7 64.6 30.6C65.2 30.1 65.8 29.7 65.8 29.7C65.8 29.7 65.6 29.2 65.2 28.7Z"/> <path d="M65.5 105.4C63.8 104.3 63 102.8 62.7 100.2C62.1 96.2 60.2 91.4 57.6 87.7C53.9 82.5 48.7 78.4 42.5 75.9C38.5 74.3 34.8 73.5 29.7 73.3C26.5 73.1 25.4 72.6 24.1 70.9L23.6 70.1L22.9 70.5C21.5 71.3 21.5 71.4 21.9 72.2C22.3 73 23.9 74.5 24.8 75C26.1 75.7 27 75.8 30.3 76C33.9 76.2 36.4 76.7 39.5 77.7C44.1 79.2 48.2 81.7 51.5 84.9C54.1 87.4 55.7 89.5 57.1 92.3C58.6 95.1 59.3 97.3 59.8 100.3C60.3 103.2 60.9 104.6 62.4 106.1C63.2 107.1 65.2 108.3 65.4 108C65.5 107.8 66.4 106.2 66.4 106.1C66.4 106.1 66 105.8 65.5 105.4Z"/> <path d="M141.1 108.9C140.9 108.8 140.2 108.3 139.6 107.8C138.1 106.3 137.7 105.4 137.4 102.2C136.9 97.3 136.2 94.5 134.3 90.7C130.7 83.2 123.8 77.3 115.5 74.6C108.4 72.3 101.3 72.3 94.3 74.6C92.2 75.3 88.8 76.9 87.3 77.9C79.8 83 75.4 90.5 74.9 99.1C74.8 100.2 74.7 101.5 74.6 102C74.3 103.3 73.4 104.5 72.1 105.3L71.1 106.1L71.5 107C71.7 107.5 71.9 108 71.8 108.1C71.7 108.3 72.8 108 73.6 107.6C74.8 106.9 76.1 105.4 76.8 104C77.4 102.7 77.5 102.4 77.6 100C77.9 95.8 78.5 93.5 80 90.3C81.2 87.7 82.6 85.8 84.8 83.6C88.6 79.8 93.1 77.5 99.1 76.2C101.7 75.6 107.9 75.5 110.5 76.1C114.3 76.8 118.6 78.6 121.6 80.5C129.3 85.5 133.7 92.8 134.6 102.1C134.9 105.9 135.4 107.2 137.1 109.1C138 110 139.7 111.1 140.3 111.1C140.4 111.1 140.7 110.6 141 110C141.5 109 141.5 109 141.1 108.9Z"/> <path d="M185.9 70L184.8 69.4L184.3 70.3C183.7 71.2 182.6 72.1 181.5 72.5C181.1 72.7 179.8 72.9 178.4 73.1C171.2 73.9 164.6 77 159.5 81.9C154.2 87 151.1 93.1 150.4 99.8C149.9 104.7 149.9 104.8 149.3 106.1C148.8 107.1 148.4 107.5 147.6 108.1C146.9 108.6 146.4 109 146.3 109C146.2 109.1 146.3 109.6 146.6 110.1C147.1 111.2 147.1 111.2 147.7 111C149.3 110.6 151.5 108.3 152.3 106.2C152.6 105.5 152.8 104.3 152.9 102.9C153.4 96.3 154.6 92.7 157.6 88.1C159.2 85.9 162.4 82.6 164.7 81C169.4 77.9 173.6 76.3 179 75.7C181.1 75.5 182 75.4 182.8 75C184.6 74.1 186.1 72.7 186.7 71.2L187 70.5L185.9 70Z"/> <path d="M186.6 63.4C185.6 61.5 183.9 60.1 181.9 59.5C181.2 59.3 179.8 59.1 178.8 59C168.1 58.4 157.9 51.2 154 41.6C152.9 38.8 152.5 36.8 152.4 33.4C152.3 29 152 28 150.4 25.9C149.7 24.9 148.7 24.1 147.5 23.6L146.8 23.3L146.3 24.2C145.9 24.7 145.7 25.1 145.7 25.2C145.7 25.3 146.1 25.7 146.7 26.1C149 27.6 149.4 28.8 149.7 33.7C149.9 38.7 150.4 40.8 152.6 45.2C153.9 47.7 155.5 49.8 157.7 52.1C163.1 57.7 170.5 61.1 178.7 61.8C181.6 62 182.7 62.5 184.1 64.3L184.8 65.2L185.7 64.7C186.8 64.1 186.9 64 186.6 63.4Z"/> <path d="M211.6 60.7C209.5 53 204.3 46.1 196.9 41.3C192.2 38.2 187.5 36.4 181.9 35.6C178.7 35.2 177.7 34.6 176.7 32.9C176.2 32.1 176.1 31.7 176 29.8C175.8 26.9 175.7 26 175.2 23.9C174.5 20.3 172.8 16.5 170.6 13.4C169.3 11.6 166.2 8.4 164.5 7.2C159.9 3.9 154.5 1.7 149.9 1.4L148.1 1.3L148.1 1.8C148.1 2.1 148.1 2.6 148 3C147.8 3.5 147.8 3.6 148.2 3.7C148.4 3.7 149.6 3.9 150.8 4.2C156.6 5.2 161.4 7.7 165.5 11.8C170.4 16.7 172.7 22 173.1 29.5C173.3 32.1 173.3 32.3 174 33.6C175.1 35.9 177.4 37.7 179.8 38.1C180.4 38.2 181.9 38.5 183.1 38.7C190.4 39.9 197.7 44.1 202.7 49.9C206.1 53.9 208.3 58.2 209.6 63.4C209.8 64.2 209.8 64.2 210.2 64.1C210.5 64 211 63.9 211.4 63.9C212 63.9 212 63.9 212 63.2C212 62.8 211.8 61.7 211.6 60.7Z"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M208.7 65.1C208.5 63.6 207.6 60.4 206.9 58.8C203.1 49.4 193.5 41.7 183.3 39.8C182.3 39.6 180.8 39.4 180 39.3C178 39 176.2 38.1 174.7 36.5C172.8 34.5 172 32.4 172 29.3C172 27.6 171.4 24.1 170.7 22.1C169.5 18.4 167.6 15.5 164.7 12.6C159 6.8 150.8 4 142 4.9C130.8 6 121.9 11.9 117.4 21.1C115.8 24.4 114.9 27.5 114.7 30.7C114.3 35.1 112.2 38 108.4 39.4C106.8 40 104.2 40.1 102.4 39.4C100 38.6 97.6 36.4 96.8 34.1C96.6 33.6 96.3 32.4 96.2 31.7C95.6 27.3 95 25.3 93.4 21.9C92.3 19.6 90.7 17.2 89 15.4C83 8.8 73.1 4.9 64.5 5.6C52.9 6.7 43.3 13.7 39.6 23.8C38.9 25.8 38.4 28.7 38.2 31.4C38.1 33.4 37.9 33.9 37.4 35C36.2 37.6 33.4 39.8 30.7 40.2C30.1 40.3 28.9 40.4 28 40.6C26 40.8 23 41.9 20.7 43C12.8 47 7.1 54.3 5.2 62.9C4.6 65.5 4.6 69.7 5.1 72.3C7.2 82.7 15.5 91.6 26.2 95C28.1 95.6 29.9 96 32.9 96.5C35.5 96.8 37.8 98.4 39.3 100.7C40.2 102.1 40.6 103.5 40.8 106.9C41.6 117 47.4 124.8 56.9 128.5C61.8 130.4 66.5 130.8 72.5 129.7C81.3 128.2 89.7 122.4 93.8 114.8C95.6 111.5 96.7 108 97.2 103.9C97.5 102.1 97.7 101.4 98.2 100.3C100.1 96.4 104.2 94.5 108.4 95.6C111.7 96.5 114.4 99.4 115 102.7C115.1 103.4 115.3 104.9 115.5 106C116.2 110.8 118.8 116.2 122.2 120C125.5 123.7 130.3 126.8 135.4 128.5C138.9 129.7 140 129.9 144.2 130C147.4 130.1 148.2 130 150.1 129.7C159.1 128.1 166.8 122.8 170.8 115.4C172.5 112.1 173.6 108.5 173.9 104.3C174 102.8 174.2 101.7 174.5 101C175.4 98.6 177.7 96.4 180 95.7C180.6 95.5 181.9 95.2 182.9 95.1C186.7 94.8 189.8 93.9 193.1 92.2C200.5 88.5 206.2 81.5 208.2 73.7C208.6 72.1 208.7 71.2 208.7 68.7C208.7 67 208.7 65.4 208.7 65.1ZM182.5 58.5C185.2 59.4 187.4 61.6 188.3 64.4C188.9 65.9 188.9 68.8 188.3 70.4C187.8 71.8 186.9 73.2 185.8 74.2C184.1 75.9 182.5 76.5 179.1 76.9C174.8 77.3 171.3 78.4 167.6 80.5C161.8 83.8 157.4 89 155.4 95.1C154.6 97.4 154.3 99.2 154.1 101.9C154 103.1 153.9 104.7 153.7 105.4C152.9 108.8 149.8 112 146.4 112.8C145.3 113 142.7 113 141.5 112.8C138.8 112.2 136.1 110.2 134.8 107.8C134 106.1 133.6 104.7 133.5 102.3C133.1 96.5 130.9 91.1 126.9 86.4C123.8 82.8 119.1 79.8 114.1 78.2C103.7 74.8 92 77.5 85 84.9C81.1 88.9 78.8 94.6 78.8 99.8C78.8 102.8 77.8 105.3 75.9 107.1C74.1 108.9 72.3 109.7 69.6 109.9C66.3 110.1 63.7 109.1 61.7 107.1C60 105.4 59.3 103.8 58.8 100.5C58.1 96.6 56.9 93.6 54.5 90C53.1 87.8 49.4 84.2 47.1 82.7C41.9 79.2 36.7 77.5 30.3 77.1C28.5 77 26.6 76.9 26.2 76.8C23.2 76 20.6 73.4 19.9 70.7C19.5 69.2 19.5 66.8 20 65.3C20.5 63.6 21.7 61.9 23.2 60.8C25.1 59.5 26.3 59.1 30.3 58.9C34.1 58.7 36.2 58.3 39.5 57.1C49.4 53.4 56.6 45.2 58 35.9C58.7 31.9 59.5 30 61.5 28.2C63.4 26.5 65.4 25.8 68.2 25.8C71.2 25.8 73.3 26.6 75.3 28.5C77.2 30.4 78 32.2 78.2 35.7C78.3 37.1 78.5 38.9 78.7 39.7C79.2 41.5 80.3 44.2 81.3 45.9C84.8 51.6 91.2 55.8 99 57.5C100.7 57.8 101.6 57.9 104.5 57.9C108.5 57.9 110.4 57.6 114.1 56.4C122.6 53.6 129.6 47 132.4 39C133.2 36.7 133.5 34.9 133.7 31.8C133.9 30 134 28.7 134.3 27.9C135.7 23.7 139.8 21.1 144.3 21.5C147.1 21.7 149 22.7 150.8 24.6C152.8 26.7 153.4 28.8 153.4 32.8C153.5 35.5 153.8 37.8 154.5 39.8C156.9 46.6 161.5 51.7 168.1 54.9C171.8 56.8 175.1 57.7 179.3 58C180.6 58.1 182 58.3 182.5 58.5Z"/></svg>`;
31
32
  const SIGNATURE = "From one designer to all designers, with love &lt;3";
32
33
 
33
- const BAND = (n, max) => {
34
- const r = max ? n / max : 0;
35
- return r >= 0.85 ? "strong" : r >= 0.6 ? "fair" : r >= 0.35 ? "weak" : "poor";
36
- };
34
+ /** The four bands live in lib/badge.mjs; these are their class names in this file. */
35
+ const BAND_CLASS = { ready: "strong", good: "fair", gaps: "weak", unready: "poor" };
37
36
 
38
37
  const VERDICT = {
39
- strong: ["Agent-ready", "Agents can work in this system. Keep the gates that make that true."],
40
- fair: ["Good foundation", "The bones are right. The gaps left are the ones that let an agent invent things."],
41
- weak: ["Gaps to address", "An agent will produce plausible, wrong UI here more often than not."],
42
- poor: ["Not ready", "There is nothing here for an agent to follow, so it will follow its own habits."],
38
+ ready: ["Agent-ready", "Agents can work in this system. Keep the gates that make that true."],
39
+ good: ["Good foundation", "The bones are right. The gaps left are the ones that let an agent invent things."],
40
+ gaps: ["Gaps to address", "An agent will produce plausible, wrong UI here more often than not."],
41
+ unready: ["Not ready", "There is nothing here for an agent to follow, so it will follow its own habits."],
43
42
  };
44
43
 
45
44
  /* Mirrors FIXES in lib/fix.mjs. The kind is the honest half of this table: an
@@ -64,8 +63,10 @@ const KIND_WORD = { apply: "adsa writes it", brief: "brief for your agent" };
64
63
  export function html(facts, scored, history = []) {
65
64
  const rubricVersion = RUBRIC.version;
66
65
  const date = new Date().toISOString().slice(0, 10);
67
- const band = BAND(scored.total, scored.max);
68
- const [verdictTitle, verdictLine] = VERDICT[band];
66
+ const verdict = scoreBand(scored.total, scored.max);
67
+ const band = BAND_CLASS[verdict.id];
68
+ const [verdictTitle, verdictLine] = VERDICT[verdict.id];
69
+ const field = standing(scored.total);
69
70
  const dims = scored.dimensions;
70
71
  const live = dims.filter((d) => !d.skipped);
71
72
  const weakest = [...live].sort((a, b) => a.score - b.score).slice(0, 3);
@@ -187,6 +188,7 @@ main{padding:26px clamp(18px,3vw,38px) 60px;display:flex;flex-direction:column;g
187
188
  .verdict .ring{flex:none}
188
189
  .verdict-copy b{display:block;font-size:17px;letter-spacing:-0.02em}
189
190
  .verdict-copy p{font-size:13.5px;color:var(--ink-2);margin-top:3px}
191
+ .verdict-copy .field{font-size:12.5px;color:var(--ink-3);margin-top:6px;max-width:56ch}
190
192
  .verdict-copy em{font-style:normal;display:block;font-family:var(--mono);font-size:11.5px;color:var(--ink-3);margin-top:8px}
191
193
 
192
194
  .ring{width:112px;height:112px;border-radius:50%;display:grid;place-items:center;print-color-adjust:exact;-webkit-print-color-adjust:exact}
@@ -384,6 +386,7 @@ ol.todo .dim-of{font-size:12.5px;color:var(--ink-3)}
384
386
  <div class="verdict-copy">
385
387
  <b>${verdictTitle}</b>
386
388
  <p>${verdictLine}</p>
389
+ <p class="field">${esc(field.sentence)}</p>
387
390
  <em>${delta === null ? `first run · ${live.length} dimensions` : `${delta >= 0 ? "+" : ""}${delta} since the last run${gained.length ? ` · ${gained.map((d) => esc(d.title.toLowerCase())).join(", ")}` : ""}`}</em>
388
391
  </div>
389
392
  </div>
@@ -687,7 +690,7 @@ function esc(value) {
687
690
 
688
691
  /** The markdown twin: the same facts, for a pull request or a terminal. */
689
692
  export function markdown(facts, scored) {
690
- const band = BAND(scored.total, scored.max);
693
+ const band = scoreBand(scored.total, scored.max).id;
691
694
  const lines = [
692
695
  `# ${facts.name} — agent readiness`,
693
696
  "",
@@ -695,6 +698,8 @@ export function markdown(facts, scored) {
695
698
  "",
696
699
  VERDICT[band][1],
697
700
  "",
701
+ standing(scored.total).sentence,
702
+ "",
698
703
  `| Dimension | Score | Evidence |`,
699
704
  `| :-- | :-- | :-- |`,
700
705
  ];