shakers-cli-beta 0.8.0-beta.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.
Files changed (218) hide show
  1. package/LICENSE +59 -0
  2. package/README.md +1175 -0
  3. package/bin/add-agent.js +88 -0
  4. package/bin/add-project.js +88 -0
  5. package/bin/add-role.js +61 -0
  6. package/bin/add-skill.js +88 -0
  7. package/bin/ai-usage.js +81 -0
  8. package/bin/alma.js +99 -0
  9. package/bin/applications.js +69 -0
  10. package/bin/availability.js +70 -0
  11. package/bin/certifications.js +69 -0
  12. package/bin/certify.js +73 -0
  13. package/bin/change-role.js +61 -0
  14. package/bin/config.js +120 -0
  15. package/bin/experiences.js +69 -0
  16. package/bin/find-projects.js +89 -0
  17. package/bin/invitations.js +69 -0
  18. package/bin/lang.js +70 -0
  19. package/bin/login.js +283 -0
  20. package/bin/mcp.js +98 -0
  21. package/bin/me.js +69 -0
  22. package/bin/onboarding.js +134 -0
  23. package/bin/portfolios.js +69 -0
  24. package/bin/rate.js +70 -0
  25. package/bin/register.js +325 -0
  26. package/bin/report-html.js +134 -0
  27. package/bin/save.js +77 -0
  28. package/bin/shakers.js +344 -0
  29. package/bin/share.js +72 -0
  30. package/bin/show-project.js +72 -0
  31. package/bin/socials.js +70 -0
  32. package/bin/superadmin.js +351 -0
  33. package/package.json +40 -0
  34. package/src/agent-category.js +13 -0
  35. package/src/agent-evaluation.js +177 -0
  36. package/src/agent-org-chart.js +355 -0
  37. package/src/agent-synthesis.js +132 -0
  38. package/src/agent-usage.js +118 -0
  39. package/src/agents-client.js +165 -0
  40. package/src/ai-fluency-cell.js +72 -0
  41. package/src/ai-profile-client.js +45 -0
  42. package/src/ai-profile-preview.js +154 -0
  43. package/src/ai.js +66 -0
  44. package/src/alma-client.js +123 -0
  45. package/src/alma-flow.js +32 -0
  46. package/src/alma-repo-context.js +47 -0
  47. package/src/alma-shell.js +161 -0
  48. package/src/ansi.js +62 -0
  49. package/src/applications-client.js +79 -0
  50. package/src/applications-flow.js +58 -0
  51. package/src/auth-client.js +144 -0
  52. package/src/auth-device-client.js +118 -0
  53. package/src/auth-method-picker.js +44 -0
  54. package/src/auth-methods.js +35 -0
  55. package/src/auth-session-store.js +214 -0
  56. package/src/authorship.js +203 -0
  57. package/src/automations-detector.js +170 -0
  58. package/src/availability-client.js +92 -0
  59. package/src/availability-flow.js +105 -0
  60. package/src/backend-request.js +170 -0
  61. package/src/blind-approval-extract.js +48 -0
  62. package/src/brand-ansi.js +14 -0
  63. package/src/brand-svg.js +9 -0
  64. package/src/browser-tools-detector.js +21 -0
  65. package/src/build-next-level.js +56 -0
  66. package/src/certifications-client.js +89 -0
  67. package/src/certifications-flow.js +75 -0
  68. package/src/certify-dimension-client.js +217 -0
  69. package/src/certify-dimension-flow.js +193 -0
  70. package/src/certify-disclaimer.js +51 -0
  71. package/src/certify-remediation-prompt.js +23 -0
  72. package/src/cli-args.js +83 -0
  73. package/src/config-dir.js +120 -0
  74. package/src/config.js +778 -0
  75. package/src/consent-flow.js +115 -0
  76. package/src/consent-skip.js +27 -0
  77. package/src/decisions-extract.js +43 -0
  78. package/src/detect-repos.js +145 -0
  79. package/src/detected-tools.js +15 -0
  80. package/src/detection-evidence.js +105 -0
  81. package/src/detection-noise.js +132 -0
  82. package/src/detectors.js +221 -0
  83. package/src/device-auth-store.js +100 -0
  84. package/src/device-login.js +118 -0
  85. package/src/email-verification.js +152 -0
  86. package/src/env-paths.js +16 -0
  87. package/src/find-projects-cache.js +89 -0
  88. package/src/find-projects-client.js +138 -0
  89. package/src/find-projects-flow.js +164 -0
  90. package/src/git-activity.js +123 -0
  91. package/src/graph-certs.js +58 -0
  92. package/src/graph-generator.js +230 -0
  93. package/src/graph-scan.js +239 -0
  94. package/src/i18n.js +3264 -0
  95. package/src/inter-fonts.js +6 -0
  96. package/src/interactive-select.js +183 -0
  97. package/src/interview-client.js +226 -0
  98. package/src/interview-transcript.js +56 -0
  99. package/src/inventory-client.js +90 -0
  100. package/src/invitations-client.js +66 -0
  101. package/src/invitations-flow.js +50 -0
  102. package/src/lang-client.js +147 -0
  103. package/src/lang-codes.js +14 -0
  104. package/src/lang-flow.js +85 -0
  105. package/src/legal-notice.js +53 -0
  106. package/src/locale.js +51 -0
  107. package/src/markdown-ansi.js +50 -0
  108. package/src/maturity.js +111 -0
  109. package/src/mcp-ai-usage-result-tool.js +109 -0
  110. package/src/mcp-ai-usage-tool.js +333 -0
  111. package/src/mcp-alma-tools.js +91 -0
  112. package/src/mcp-applications-tools.js +42 -0
  113. package/src/mcp-ask-tools.js +50 -0
  114. package/src/mcp-auth-form.js +282 -0
  115. package/src/mcp-availability-tools.js +43 -0
  116. package/src/mcp-certifications-tools.js +42 -0
  117. package/src/mcp-certify-dimension-tools.js +58 -0
  118. package/src/mcp-detector.js +223 -0
  119. package/src/mcp-device-login-tools.js +173 -0
  120. package/src/mcp-find-projects-tools.js +67 -0
  121. package/src/mcp-install.js +245 -0
  122. package/src/mcp-invitations-tools.js +42 -0
  123. package/src/mcp-lang-tools.js +39 -0
  124. package/src/mcp-onboarding-tools.js +388 -0
  125. package/src/mcp-portfolios-tools.js +46 -0
  126. package/src/mcp-profile-tools.js +46 -0
  127. package/src/mcp-rate-tools.js +42 -0
  128. package/src/mcp-register-tools.js +517 -0
  129. package/src/mcp-report-tools.js +101 -0
  130. package/src/mcp-role-tools.js +112 -0
  131. package/src/mcp-saved-positions-tools.js +55 -0
  132. package/src/mcp-server.js +170 -0
  133. package/src/mcp-show-project-tools.js +50 -0
  134. package/src/mcp-socials-tools.js +39 -0
  135. package/src/memory-structure-detector.js +108 -0
  136. package/src/model-call-record.js +198 -0
  137. package/src/onboarding-availability.js +26 -0
  138. package/src/onboarding-client.js +364 -0
  139. package/src/onboarding-flow.js +1078 -0
  140. package/src/onboarding-livekit-client.js +102 -0
  141. package/src/onboarding-status-client.js +42 -0
  142. package/src/open-file.js +32 -0
  143. package/src/osc-link.js +16 -0
  144. package/src/portfolio-client.js +164 -0
  145. package/src/portfolio-git-info.js +61 -0
  146. package/src/portfolios-client.js +85 -0
  147. package/src/portfolios-flow.js +79 -0
  148. package/src/profile-client.js +125 -0
  149. package/src/profile-flow.js +115 -0
  150. package/src/profile-switch.js +46 -0
  151. package/src/prompt-select.js +29 -0
  152. package/src/rate-client.js +82 -0
  153. package/src/rate-flow.js +137 -0
  154. package/src/render-ai-fluency.js +266 -0
  155. package/src/render-certification.js +177 -0
  156. package/src/render-html.js +140 -0
  157. package/src/render-report-text.js +27 -0
  158. package/src/render-sheet.js +261 -0
  159. package/src/render-terminal.js +109 -0
  160. package/src/render-usage-report-text.js +155 -0
  161. package/src/repo-context.js +215 -0
  162. package/src/report-gating.js +49 -0
  163. package/src/report-store.js +226 -0
  164. package/src/report-theme.js +177 -0
  165. package/src/report-view-client.js +50 -0
  166. package/src/reveal-output.js +29 -0
  167. package/src/roadmap-content.js +453 -0
  168. package/src/roadmap-personalization.js +132 -0
  169. package/src/roadmap-prompt.js +154 -0
  170. package/src/roles-client.js +114 -0
  171. package/src/roles-flow.js +192 -0
  172. package/src/sanitize-network-text.js +36 -0
  173. package/src/saved-positions-client.js +71 -0
  174. package/src/saved-positions-flow.js +50 -0
  175. package/src/scan-exclusions.js +50 -0
  176. package/src/scanner.js +612 -0
  177. package/src/scope-signals.js +148 -0
  178. package/src/secret-prompt.js +72 -0
  179. package/src/session-gate.js +30 -0
  180. package/src/session-refresh.js +51 -0
  181. package/src/session-scan.js +336 -0
  182. package/src/session-signals.js +36 -0
  183. package/src/shakers-wordmark.js +17 -0
  184. package/src/share-card.js +362 -0
  185. package/src/share-certification.js +111 -0
  186. package/src/share-consent-state.js +157 -0
  187. package/src/share-payload.js +300 -0
  188. package/src/share.js +184 -0
  189. package/src/show-project-client.js +58 -0
  190. package/src/show-project-flow.js +119 -0
  191. package/src/signup-client.js +91 -0
  192. package/src/skills-client.js +102 -0
  193. package/src/socials-client.js +91 -0
  194. package/src/socials-flow.js +83 -0
  195. package/src/specificity-extract.js +41 -0
  196. package/src/start-add-agents.js +306 -0
  197. package/src/start-add-portfolio.js +388 -0
  198. package/src/start-add-skills.js +260 -0
  199. package/src/stdin-ask.js +129 -0
  200. package/src/steering-extract.js +43 -0
  201. package/src/strip-internal-comments.js +45 -0
  202. package/src/tech-detector.js +295 -0
  203. package/src/templates/report-sheet.html +572 -0
  204. package/src/terminal-agents.js +199 -0
  205. package/src/terminal-format.js +85 -0
  206. package/src/terminal-progress.js +106 -0
  207. package/src/terminal-sections.js +219 -0
  208. package/src/tier-analysis.js +126 -0
  209. package/src/tier-engine.js +114 -0
  210. package/src/tier-projection.js +117 -0
  211. package/src/transcript-turns.js +49 -0
  212. package/src/translations-client.js +84 -0
  213. package/src/typewriter.js +154 -0
  214. package/src/usage-model-calls.js +119 -0
  215. package/src/usage-oneshot-commands.js +180 -0
  216. package/src/usage-repo-scope.js +162 -0
  217. package/src/usage-run-steps.js +338 -0
  218. package/src/work-streams.js +74 -0
@@ -0,0 +1,572 @@
1
+ <!doctype html>
2
+ <html lang="__LANG__" data-theme="light">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <!-- Restrictive CSP: self-contained, zero-network report (see render-sheet.js
6
+ docstring). No connect-src, no remote script-src, img-src limited to
7
+ data:. script-src-attr is 'none' — every handler here is wired via
8
+ addEventListener, never an inline on*= attribute. -->
9
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; script-src-attr 'none'; style-src 'unsafe-inline'; img-src data:; connect-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none';">
10
+ <meta name="viewport" content="width=device-width, initial-scale=1">
11
+ <title>Shakers · __REPORT_TITLE__</title>
12
+ <style>
13
+ /* ============================================================
14
+ report-sheet template — the shareable report (`report`/`sheet`) design,
15
+ ported from mockup-report.html. Self-contained: no external CSS/JS/fonts/
16
+ network. Content is injected server-side from LIVE data (src/render-sheet.js).
17
+ ============================================================ */
18
+
19
+ /* --- Tokens (Shakers DS: teal/lime/zinc) --------------------- */
20
+ :root{
21
+ --lime:#d8e637; --lime-soft:#eef3a8;
22
+ --teal-500:#0e7d69; --teal-700:#08473c; --teal-900:#06332b;
23
+ --ink:#181B1A;
24
+ --white:#ffffff;
25
+ --zinc-50:#fafafa; --zinc-100:#f4f4f5; --zinc-200:#e4e4e7; --zinc-300:#d4d4d8;
26
+ --zinc-400:#a1a1aa; --zinc-500:#71717a; --zinc-600:#52525b; --zinc-700:#3f3f46; --zinc-800:#27272a;
27
+ --band-high-fg:#0e7d69; --band-high-bg:#dcfce7;
28
+ --band-mid-fg:#a16207; --band-mid-bg:#fef9c3;
29
+ --band-low-fg:#b91c1c; --band-low-bg:#fee2e2;
30
+ /* --- Agent category badges ---------------------------------------------
31
+ * One colour per catalog category, built like the --band-* pairs above: soft
32
+ * tint background, dark ink foreground, so they sit with the rest of the
33
+ * document instead of shouting over it. Every pair meets WCAG AA (>= 4.5:1) in
34
+ * both themes, verified by test. The badge always carries its category NAME:
35
+ * the colour accompanies the label and never replaces it, so the section is
36
+ * legible with no colour at all.
37
+ */
38
+ --cat-developer-fg:#4338ca; --cat-developer-bg:#e0e7ff;
39
+ --cat-product-fg:#a21caf; --cat-product-bg:#fae8ff;
40
+ --cat-designer-fg:#be185d; --cat-designer-bg:#fce7f3;
41
+ --cat-marketing-fg:#c2410c; --cat-marketing-bg:#ffedd5;
42
+ --cat-data-fg:#0369a1; --cat-data-bg:#e0f2fe;
43
+ /* Two more specialities, and with seven the hue budget is genuinely spent:
44
+ * the nearest pair (sales amber / marketing orange) is now about 20 degrees
45
+ * apart, where the closest pair used to be about 44. That is acceptable ONLY
46
+ * because every badge always carries its own name, so colour accompanies the
47
+ * label instead of carrying the meaning — the same reasoning already written
48
+ * above. An eighth speciality should question the colour scheme, not squeeze
49
+ * in another hue. Finance is green for the obvious reason; sales sits next to
50
+ * marketing on purpose, since they are the two commercial families. */
51
+ --cat-finance-fg:#047857; --cat-finance-bg:#d1fae5;
52
+ --cat-sales-fg:#854d0e; --cat-sales-bg:#fef3c7;
53
+ /* No category, AND the FLOOR category (`other`): the document's neutral chip
54
+ * colours plus a dashed border. Neutral so it cannot be mistaken for a
55
+ * speciality, dashed so the difference survives greyscale, and never a silent
56
+ * gap. It is a common state, not an edge case.
57
+ *
58
+ * There is deliberately NO `--cat-other-*` pair and NO `.cat-other` rule: the
59
+ * floor means "the catalog has nowhere to put this", so giving it a colour of
60
+ * its own would promote it to a speciality in the reading, which is the exact
61
+ * false statement the explicit floor exists to avoid. `render-sheet.js` routes
62
+ * it here on purpose — see `isFloorCategory` there. */
63
+ --cat-none-fg:var(--zinc-600); --cat-none-bg:var(--zinc-100);
64
+ }
65
+ [data-theme="light"]{
66
+ --bg:var(--white); --bg-elev:var(--zinc-50); --card:var(--white);
67
+ --card-2:var(--zinc-50); --border:var(--zinc-200); --border-strong:var(--zinc-300);
68
+ --fg:var(--ink); --fg-muted:var(--zinc-500); --fg-soft:var(--zinc-600);
69
+ --accent:var(--teal-500); --accent-ink:#053b32;
70
+ --track:var(--zinc-200); --chip-bg:var(--zinc-100); --chip-fg:var(--zinc-700);
71
+ --wordmark:var(--ink); --header-bg:rgba(255,255,255,.82);
72
+ --ring-track:var(--zinc-200);
73
+ }
74
+ [data-theme="dark"]{
75
+ --bg:#0c1210; --bg-elev:#111a17; --card:#111a17;
76
+ --card-2:#0f1613; --border:#1f2d28; --border-strong:#2b3d37;
77
+ --fg:#e7f0ec; --fg-muted:#7d938b; --fg-soft:#a6bab1;
78
+ --accent:var(--lime); --accent-ink:#0c1210;
79
+ --track:#1c2b26; --chip-bg:#16211d; --chip-fg:#bcd0c8;
80
+ --wordmark:#e7f0ec; --header-bg:rgba(12,18,16,.82);
81
+ --ring-track:#1c2b26;
82
+ --band-high-bg:#0d2b1e; --band-high-fg:#4ade80;
83
+ --band-mid-bg:#2d2510; --band-mid-fg:#facc15;
84
+ --band-low-bg:#2d1414; --band-low-fg:#f87171;
85
+ /* Same five hues inverted for the dark theme (deep tint background, light ink),
86
+ * the same way the bands above are. All >= 6:1 measured. */
87
+ --cat-developer-fg:#a5b4fc; --cat-developer-bg:#1e1b4b;
88
+ --cat-product-fg:#e879f9; --cat-product-bg:#3b0764;
89
+ --cat-designer-fg:#f9a8d4; --cat-designer-bg:#4c0519;
90
+ --cat-marketing-fg:#fdba74; --cat-marketing-bg:#431407;
91
+ --cat-data-fg:#7dd3fc; --cat-data-bg:#082f49;
92
+ --cat-finance-fg:#6ee7b7; --cat-finance-bg:#022c22;
93
+ --cat-sales-fg:#fcd34d; --cat-sales-bg:#422006;
94
+ --cat-none-fg:var(--chip-fg); --cat-none-bg:var(--chip-bg);
95
+ }
96
+
97
+ *{box-sizing:border-box}
98
+ html{scroll-behavior:smooth}
99
+ body{
100
+ margin:0; background:var(--bg); color:var(--fg);
101
+ font:15px/1.55 Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
102
+ -webkit-font-smoothing:antialiased;
103
+ transition:background .35s ease, color .35s ease;
104
+ }
105
+ a{color:inherit}
106
+ .mono{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace}
107
+
108
+ /* --- Header -------------------------------------------------- */
109
+ header.top{
110
+ position:sticky; top:0; z-index:40;
111
+ display:flex; align-items:center; justify-content:space-between; gap:20px;
112
+ padding:14px clamp(16px,4vw,40px);
113
+ background:var(--header-bg); backdrop-filter:saturate(140%) blur(10px);
114
+ border-bottom:1px solid var(--border);
115
+ }
116
+ .brand{display:flex; align-items:center; gap:12px}
117
+ .brand .powered{font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--fg-muted)}
118
+ .brand svg.wordmark{height:19px; width:auto; display:block}
119
+ .brand svg.wordmark path{fill:var(--wordmark); transition:fill .35s ease}
120
+ .brand .bolt{width:9px;height:9px;border-radius:2px;background:var(--lime);
121
+ box-shadow:0 0 0 0 rgba(216,230,55,.6); animation:pulse 3s infinite}
122
+ @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(216,230,55,.5)}70%{box-shadow:0 0 0 8px rgba(216,230,55,0)}100%{box-shadow:0 0 0 0 rgba(216,230,55,0)}}
123
+ .head-meta{display:flex; align-items:center; gap:16px}
124
+ .head-title{text-align:right}
125
+ .head-title .t{font-weight:650; font-size:14px}
126
+ .head-title .s{font-size:12px; color:var(--fg-muted)}
127
+ .theme-toggle{
128
+ appearance:none; border:1px solid var(--border-strong); background:var(--card);
129
+ color:var(--fg); border-radius:999px; padding:7px 12px; font-size:12px; cursor:pointer;
130
+ display:inline-flex; align-items:center; gap:7px; transition:.2s;
131
+ }
132
+ .theme-toggle:hover{border-color:var(--accent); transform:translateY(-1px)}
133
+
134
+ /* the animated accent sweep under the header */
135
+ .accent-sweep{height:2px; background:linear-gradient(90deg,transparent,var(--lime),var(--teal-500),transparent);
136
+ background-size:200% 100%; animation:sweep 6s linear infinite}
137
+ @keyframes sweep{0%{background-position:200% 0}100%{background-position:-200% 0}}
138
+
139
+ /* --- Layout -------------------------------------------------- */
140
+ .wrap{max-width:1320px; margin:0 auto; padding:clamp(18px,3vw,34px) clamp(16px,4vw,40px) 80px}
141
+ .grid{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.05fr); gap:26px; align-items:start}
142
+ @media (max-width:960px){.grid{grid-template-columns:1fr}}
143
+ .col-left{position:sticky; top:80px}
144
+ @media (max-width:960px){.col-left{position:static}}
145
+
146
+ .col-head{display:flex; align-items:baseline; gap:10px; margin:2px 0 14px}
147
+ .col-head h2{margin:0; font-size:13px; letter-spacing:.12em; text-transform:uppercase; color:var(--fg-muted); font-weight:650}
148
+ .col-head .dot{width:6px;height:6px;border-radius:50%; background:var(--accent)}
149
+ /* Column note (issue 096): one line under the column head, for copy that has to
150
+ be read before the numbers below it. `.sub` only exists inside `.card`. */
151
+ .col-note{margin:-8px 0 14px; font-size:12.5px; line-height:1.5; color:var(--fg-muted)}
152
+ .col-note b{color:var(--fg); font-weight:650}
153
+
154
+ /* --- Cards --------------------------------------------------- */
155
+ .card{
156
+ background:var(--card); border:1px solid var(--border); border-radius:16px;
157
+ padding:18px 18px; margin-bottom:16px;
158
+ box-shadow:0 1px 2px rgba(0,0,0,.03);
159
+ transition:transform .3s cubic-bezier(.2,.7,.2,1), box-shadow .3s, border-color .3s, background .35s;
160
+ }
161
+ .card:hover{border-color:var(--border-strong); box-shadow:0 8px 26px rgba(0,0,0,.06)}
162
+ .card h3{margin:0 0 4px; font-size:14px; font-weight:650}
163
+ .card .sub{color:var(--fg-muted); font-size:12.5px; margin:0 0 12px}
164
+
165
+ /* reveal-on-load stagger */
166
+ .reveal{opacity:0; transform:translateY(14px); transition:opacity .6s ease, transform .6s cubic-bezier(.2,.7,.2,1)}
167
+ .reveal.in{opacity:1; transform:none}
168
+
169
+ /* --- Hero score --------------------------------------------- */
170
+ .hero{display:flex; gap:20px; align-items:center}
171
+ .ring{--p:88; position:relative; width:132px; height:132px; flex:none}
172
+ .ring svg{transform:rotate(-90deg)}
173
+ .ring .track{fill:none; stroke:var(--ring-track); stroke-width:11}
174
+ .ring .fill{fill:none; stroke:var(--accent); stroke-width:11; stroke-linecap:round;
175
+ stroke-dasharray:339.29; stroke-dashoffset:339.29;
176
+ transition:stroke-dashoffset 1.4s cubic-bezier(.16,1,.3,1)}
177
+ .ring .num{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center}
178
+ .ring .num b{font-size:34px; font-weight:750; letter-spacing:-.02em}
179
+ .ring .num small{font-size:11px; color:var(--fg-muted)}
180
+ .hero-meta{min-width:0}
181
+ .tier-chip{display:inline-flex; align-items:center; gap:8px; background:var(--teal-700); color:#eafff7;
182
+ border-radius:999px; padding:6px 13px; font-size:12.5px; font-weight:600}
183
+ .tier-chip .k{background:var(--lime); color:var(--ink); border-radius:5px; padding:1px 6px; font-size:11px; font-weight:750}
184
+ .hero-meta .band-line{margin-top:10px; font-size:13px; color:var(--fg-soft)}
185
+ .hero-meta .band-line b{color:var(--fg)}
186
+ .lvl-pills{display:flex; gap:5px; margin-top:12px}
187
+ .lvl-pills span{flex:1; height:7px; border-radius:4px; background:var(--track); transition:background .5s}
188
+ .lvl-pills span.on{background:linear-gradient(90deg,var(--teal-500),var(--lime))}
189
+
190
+ /* --- Tier ladder (stepper) ---------------------------------- */
191
+ .ladder{list-style:none; margin:6px 0 0; padding:0; position:relative}
192
+ .ladder:before{content:""; position:absolute; left:11px; top:6px; bottom:6px; width:2px; background:var(--border); }
193
+ .ladder li{position:relative; padding:7px 0 7px 34px; font-size:13px}
194
+ .ladder .node{position:absolute; left:3px; top:9px; width:18px; height:18px; border-radius:50%;
195
+ background:var(--card); border:2px solid var(--border-strong); display:grid; place-items:center; font-size:10px}
196
+ .ladder li.done .node{background:var(--teal-500); border-color:var(--teal-500); color:#fff}
197
+ .ladder li.current .node{background:var(--lime); border-color:var(--lime); color:var(--ink);
198
+ box-shadow:0 0 0 0 rgba(216,230,55,.55); animation:pulse 2.6s infinite}
199
+ .ladder li .name{font-weight:600}
200
+ .ladder li.pending{color:var(--fg-muted)}
201
+ .ladder li .k{font-size:11px; color:var(--fg-muted)}
202
+ .ladder li .desc{font-size:12px; color:var(--fg-muted); margin-top:1px}
203
+ .ladder li .unlock{font-size:11.5px; color:var(--accent); margin-top:2px}
204
+
205
+ /* --- chips --------------------------------------------------- */
206
+ .chips{display:flex; flex-wrap:wrap; gap:8px}
207
+ .chip{display:inline-flex; align-items:center; gap:7px; background:var(--chip-bg); color:var(--chip-fg);
208
+ border:1px solid var(--border); border-radius:999px; padding:5px 12px; font-size:12.5px;
209
+ transition:transform .2s, border-color .2s}
210
+ .chip:hover{transform:translateY(-2px); border-color:var(--accent)}
211
+ .chip .d{width:7px;height:7px;border-radius:50%; background:var(--accent)}
212
+
213
+ /* --- Agent tree --------------------------------------------- *
214
+ * ORPHANED BUT OWNED (issue 090, kept for issue 089).
215
+ *
216
+ * Nothing emits `class="agent"` today: `render-sheet.js` builds no agent tree, so
217
+ * this block styles markup that does not exist. It is NOT dead by accident — it
218
+ * came with the approved mockup and it is exactly the material issue 089 (the
219
+ * shareable report has no agents at all) needs, so that design does not have to
220
+ * re-invent the nesting rail. Recovering the markup that used to pair with it:
221
+ * `git show 7fae2c8^:src/render-html.js` (see `agentNodeHtml`/`agentCardsSection`).
222
+ *
223
+ * If 089 is dropped or solved another way, this goes with it. What it must not
224
+ * do is sit here with no owner, which is how it got orphaned in the first place.
225
+ * ------------------------------------------------------------ */
226
+ .tree{margin:0; padding:0; list-style:none}
227
+ .agent{border:1px solid var(--border); border-radius:12px; padding:11px 13px; margin-bottom:9px; background:var(--card-2);
228
+ transition:border-color .25s, transform .25s}
229
+ .agent:hover{border-color:var(--border-strong); transform:translateX(2px)}
230
+ .agent.child{margin-left:22px; position:relative}
231
+ .agent.child:before{content:""; position:absolute; left:-14px; top:20px; width:12px; height:2px; background:var(--border-strong)}
232
+ .agent.child:after{content:""; position:absolute; left:-14px; top:-9px; bottom:20px; width:2px; background:var(--border-strong)}
233
+ .agent-row{display:flex; align-items:center; gap:10px}
234
+ .agent-row .an{font-weight:600; font-size:13.5px}
235
+ .agent-row .model{font-size:11px; color:var(--fg-muted); background:var(--chip-bg); border-radius:5px; padding:1px 7px}
236
+ .agent-row .spacer{flex:1}
237
+ .score-badge{font-weight:700; font-size:12px; padding:3px 10px; border-radius:999px}
238
+ .band-high{background:var(--band-high-bg); color:var(--band-high-fg)}
239
+ .band-mid{background:var(--band-mid-bg); color:var(--band-mid-fg)}
240
+ .band-low{background:var(--band-low-bg); color:var(--band-low-fg)}
241
+ .usage{font-size:11px; color:var(--fg-muted)}
242
+ .agent .whatdo{font-size:12px; color:var(--fg-soft); margin:6px 0 0}
243
+ /* Category badge. Built on `.chip` so it inherits the document's chip geometry
244
+ * (radius, padding, hover) and only overrides the colour pair — one place to
245
+ * change the shape, five tokens to change the palette. */
246
+ .cat{background:var(--cat-none-bg); color:var(--cat-none-fg); border-color:transparent; font-weight:600}
247
+ .cat-developer{background:var(--cat-developer-bg); color:var(--cat-developer-fg)}
248
+ .cat-product{background:var(--cat-product-bg); color:var(--cat-product-fg)}
249
+ .cat-designer{background:var(--cat-designer-bg); color:var(--cat-designer-fg)}
250
+ .cat-marketing{background:var(--cat-marketing-bg); color:var(--cat-marketing-fg)}
251
+ .cat-data{background:var(--cat-data-bg); color:var(--cat-data-fg)}
252
+ .cat-finance{background:var(--cat-finance-bg); color:var(--cat-finance-fg)}
253
+ .cat-sales{background:var(--cat-sales-bg); color:var(--cat-sales-fg)}
254
+ /* Not a category: neutral, and dashed so the difference survives greyscale, a
255
+ * screenshot and anyone who does not distinguish colours. */
256
+ .cat-none{border:1px dashed var(--border-strong); font-weight:500}
257
+
258
+ /* --- Right column: tabs + accordions ------------------------ */
259
+ .tabs{display:inline-flex; background:var(--card-2); border:1px solid var(--border); border-radius:999px; padding:4px; gap:4px; margin-bottom:16px; position:relative}
260
+ .tab{appearance:none; border:0; background:transparent; color:var(--fg-soft); cursor:pointer;
261
+ padding:8px 18px; border-radius:999px; font-size:13px; font-weight:600; position:relative; z-index:2; transition:color .25s}
262
+ .tab.active{color:var(--accent-ink)}
263
+ .tab .cnt{font-size:11px; opacity:.7; margin-left:6px}
264
+ .tab-ind{position:absolute; z-index:1; top:4px; bottom:4px; border-radius:999px; background:var(--accent);
265
+ transition:left .3s cubic-bezier(.2,.7,.2,1), width .3s cubic-bezier(.2,.7,.2,1)}
266
+ .panel-controls{display:flex; justify-content:flex-end; margin:-6px 0 10px}
267
+ .mini-btn{appearance:none; background:transparent; border:0; color:var(--accent); font-size:12px; cursor:pointer}
268
+ .mini-btn:hover{text-decoration:underline}
269
+
270
+ .tabpanel{display:none}
271
+ .tabpanel.show{display:block; animation:fadein .4s ease}
272
+ @keyframes fadein{from{opacity:0; transform:translateY(8px)}to{opacity:1; transform:none}}
273
+
274
+ /* accordion */
275
+ .acc{border:1px solid var(--border); border-radius:14px; margin-bottom:12px; background:var(--card); overflow:hidden;
276
+ transition:border-color .25s, box-shadow .25s}
277
+ .acc:hover{border-color:var(--border-strong)}
278
+ .acc.open{border-color:var(--accent); box-shadow:0 6px 22px rgba(0,0,0,.05)}
279
+ .acc-head{width:100%; text-align:left; appearance:none; border:0; background:transparent; cursor:pointer;
280
+ display:flex; align-items:center; gap:12px; padding:14px 16px; color:var(--fg)}
281
+ .acc-head .caret{transition:transform .3s; color:var(--fg-muted); flex:none}
282
+ .acc.open .acc-head .caret{transform:rotate(90deg)}
283
+ .acc-head .title{font-weight:650; font-size:14px}
284
+ .acc-head .meta{font-size:12px; color:var(--fg-muted)}
285
+ .acc-head .spacer{flex:1}
286
+ .level-chip{font-size:12px; font-weight:700; padding:4px 11px; border-radius:999px; white-space:nowrap}
287
+ .lvl-P5{background:var(--band-high-bg); color:var(--band-high-fg)}
288
+ .lvl-P4{background:var(--band-high-bg); color:var(--band-high-fg)}
289
+ .lvl-P3{background:var(--band-mid-bg); color:var(--band-mid-fg)}
290
+ .lvl-P2{background:var(--band-mid-bg); color:var(--band-mid-fg)}
291
+ .lvl-P1{background:var(--band-low-bg); color:var(--band-low-fg)}
292
+
293
+ /* the grid-rows 0fr->1fr trick = smooth height animation, no JS measuring */
294
+ .acc-body-wrap{display:grid; grid-template-rows:0fr; transition:grid-template-rows .35s ease}
295
+ .acc.open .acc-body-wrap{grid-template-rows:1fr}
296
+ .acc-body-inner{overflow:hidden; min-height:0}
297
+ .acc-body{padding:2px 16px 16px}
298
+ .acc-body .blk{margin-top:12px}
299
+ .acc-body .blk h5{margin:0 0 5px; font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--fg-muted)}
300
+ .acc-body p{margin:0; font-size:13px; color:var(--fg-soft)}
301
+ .acc-body ul{margin:4px 0 0; padding-left:18px; font-size:13px; color:var(--fg-soft)}
302
+ .acc-body ul li{margin:3px 0}
303
+
304
+ /* areas */
305
+ .areas{display:flex; flex-direction:column; gap:6px; margin-top:4px}
306
+ .area{display:flex; align-items:center; gap:10px; font-size:13px; padding:6px 10px; border-radius:9px; background:var(--card-2)}
307
+ .area .nm{flex:1}
308
+ .area .tag{font-size:11px; font-weight:650; padding:2px 9px; border-radius:999px}
309
+ .tag-verificado{background:var(--band-high-bg); color:var(--band-high-fg)}
310
+ .tag-parcial{background:var(--band-mid-bg); color:var(--band-mid-fg)}
311
+ .tag-afirmado{background:var(--band-low-bg); color:var(--band-low-fg)}
312
+ .tag-na{background:var(--chip-bg); color:var(--fg-muted)}
313
+ .area .icn{width:16px; text-align:center}
314
+
315
+ /* anchored quotes (issue 054): one block per quote, under its own area row, with
316
+ a left rail whose colour differs per SOURCE. The .qt-k label is the real
317
+ distinction between the two — the rail is lost in greyscale, the label is not. */
318
+ .area-block{display:flex; flex-direction:column; gap:3px}
319
+ .qt{display:flex; flex-direction:column; gap:1px; margin:0 0 0 12px; padding-left:9px; border-left:2px solid var(--border)}
320
+ .qt .qt-k{font-size:10.5px; font-weight:650; text-transform:uppercase; letter-spacing:.02em; color:var(--fg-muted)}
321
+ .qt .qt-t{font-size:12.5px; line-height:1.5; font-style:italic}
322
+ .qt-transcript{border-left-color:var(--band-high-fg)}
323
+ .qt-definition{border-left-color:var(--fg-muted)}
324
+ .qt-withheld{border-left-color:var(--band-mid-fg)}
325
+ .qt-withheld .qt-k{color:var(--band-mid-fg); text-transform:none; letter-spacing:0; font-size:12px}
326
+
327
+ /* evidence lists */
328
+ .evi{font-size:12.5px; margin:2px 0 0; padding-left:18px}
329
+ .evi li{margin:3px 0}
330
+ .evi.ok li::marker{content:"✓ "; color:var(--band-high-fg)}
331
+ .evi.no li::marker{content:"~ "; color:var(--fg-muted)}
332
+
333
+ /* remediation prompt block */
334
+ .remediation{margin-top:12px; border-top:1px dashed var(--border); padding-top:12px}
335
+ .remediation .rhead{display:flex; align-items:center; justify-content:space-between; gap:12px}
336
+ .copy-btn{appearance:none; border:1px solid var(--border-strong); background:var(--card); color:var(--fg);
337
+ border-radius:8px; padding:4px 10px; font-size:11.5px; cursor:pointer; transition:.2s}
338
+ .copy-btn:hover{border-color:var(--accent); color:var(--accent)}
339
+ .remediation pre{white-space:pre-wrap; word-break:break-word; font-size:12px; line-height:1.5;
340
+ background:var(--card-2); border:1px solid var(--border); border-radius:10px; padding:12px; margin:8px 0 0; color:var(--fg-soft)}
341
+
342
+ footer.foot{text-align:center; padding:30px 20px; color:var(--fg-muted); font-size:12px}
343
+ footer.foot .wm{display:inline-flex; align-items:center; gap:8px; margin-top:8px; opacity:.8}
344
+ footer.foot svg{height:14px}
345
+ footer.foot svg path{fill:var(--wordmark)}
346
+
347
+ .mock-flag{position:fixed; bottom:14px; left:14px; z-index:50; background:var(--lime); color:var(--ink);
348
+ font-size:11px; font-weight:700; padding:6px 12px; border-radius:999px; box-shadow:0 4px 14px rgba(0,0,0,.15)}
349
+
350
+ @media (prefers-reduced-motion:reduce){
351
+ *{animation:none !important; transition:none !important}
352
+ .reveal{opacity:1; transform:none}
353
+ }
354
+ </style>
355
+ </head>
356
+ <body>
357
+
358
+
359
+ <header class="top">
360
+ <div class="brand">
361
+ <span class="bolt" aria-hidden="true"></span>
362
+ <span class="powered">Powered by</span>
363
+ <svg class="wordmark" viewBox="0 0 87 18" role="img" aria-label="Shakers">
364
+ <path d="M19.2845 8.74573C19.2845 8.74573 16.556 10.0876 15.7698 10.5041C15.7236 11.4295 15.6311 12.355 15.5848 13.2804C15.5386 14.3447 15.4924 14.9462 15.4461 16.0105C15.4461 16.2419 15.4461 16.4732 15.3999 16.6583C15.2611 17.445 14.8449 17.9077 14.2437 17.9077C13.55 17.9077 13.0876 17.3987 13.0876 16.5658C13.0876 15.1776 13.1801 14.2059 13.2263 12.8177C13.2263 12.4475 13.2263 11.8923 13.2263 11.4758C13.2263 11.4758 12.8101 11.6609 12.5326 11.7072C12.0701 11.6609 11.7002 11.4295 11.5614 10.9668C11.4227 10.5041 11.5614 10.0876 11.9314 9.76372C12.0239 9.71745 13.5963 8.74573 13.5963 8.74573C13.6425 6.80228 13.735 4.07221 13.8737 2.17503C13.92 1.80485 13.9662 1.43467 14.105 1.11077C14.2437 0.694314 14.6137 0.462951 15.0761 0.509223C15.5386 0.509223 15.8623 0.786858 15.9548 1.20331C16.0473 1.57349 16.0935 1.98994 16.0935 2.4064C16.0473 3.93339 16.0011 4.95138 15.9548 6.47837C15.9548 6.84856 15.9548 7.21873 15.9548 7.774C17.7122 6.9411 17.8971 6.70974 19.562 5.92311C19.6545 4.53493 19.7007 3.65575 19.7932 2.26758C19.8395 1.80485 19.8857 1.29586 19.9782 0.833131C20.117 0.324133 20.4869 0.000226092 21.0419 0.0464986C21.5968 0.0927711 21.9205 0.416678 22.013 0.925676C22.0593 1.34213 22.0593 1.71231 22.0593 2.12876C22.0593 3.05421 21.9668 3.7483 21.9668 4.81257C22.2905 4.72002 22.7067 4.30357 23.0304 4.2573C23.6779 4.07221 24.2791 4.34984 24.5103 4.90511C24.7416 5.41411 24.5566 5.96938 24.0016 6.33956C23.5854 6.52465 21.7356 7.68146 21.7356 7.68146C21.6893 10.6892 21.5968 12.8177 21.5043 15.8717C21.5043 16.1493 21.5043 16.4732 21.4581 16.7509C21.3193 17.445 20.8569 17.8614 20.2557 17.8151C19.6545 17.7689 19.2383 17.3524 19.1458 16.6583C19.0995 16.3807 19.192 12.4013 19.2845 9.76372C19.2845 9.39354 19.2845 9.20845 19.2845 8.74573Z"/>
365
+ <path d="M64.097 4.67388C63.542 4.72016 63.1258 4.76643 62.7558 4.76643C61.9697 4.76643 61.5072 4.48879 61.4147 3.88725C61.3222 3.28571 61.6459 2.82299 62.3859 2.59162C62.9408 2.40653 63.542 2.31399 64.097 2.22144C65.9931 1.99008 67.8429 1.89754 69.6927 2.45281C70.3402 2.6379 70.9414 2.91553 71.4963 3.28571C72.8837 4.21116 73.1612 5.73815 72.2825 7.1726C71.7738 8.0055 70.9876 8.60705 70.1552 9.06977C69.6002 9.39368 69.0453 9.67131 68.3978 9.99522C68.8603 10.4117 73.1149 13.5582 73.4849 14.0209C73.8549 14.4837 73.8086 15.1315 73.4386 15.5479C73.0687 15.9644 72.56 16.0569 72.005 15.8256C71.6813 15.6405 71.4038 15.4091 71.0801 15.1777C69.8315 14.206 67.6579 12.6328 66.3168 11.5685C66.2705 12.0312 65.9931 15.0389 65.9468 16.1032C65.9468 16.3808 65.9468 16.7047 65.8543 16.9824C65.7156 17.5376 65.2069 17.8615 64.6982 17.8153C64.1432 17.769 63.7733 17.4914 63.6345 16.9824C63.542 16.5659 63.542 16.1032 63.542 15.6867C63.727 12.6328 64.097 5.18288 64.097 4.67388ZM66.4555 8.14432C67.7504 7.63532 68.9528 7.1726 69.8777 6.24715C70.2477 5.87697 70.1552 5.27543 69.6927 5.04406C68.6753 4.58134 67.6117 4.53507 66.548 4.44252L66.4555 8.14432Z"/>
366
+ <path d="M32.2335 12.8178C31.9097 13.1879 31.6785 13.4656 31.4473 13.6969C30.6149 14.4836 29.8287 15.3627 28.95 16.0568C28.4413 16.4733 27.7939 16.7047 27.1464 16.8897C25.7128 17.2599 24.4641 16.427 24.2792 14.8538C24.1867 14.0208 24.2329 13.0954 24.4641 12.2625C25.3428 9.02342 27.1002 6.29334 29.6437 4.1648C30.2449 3.65581 30.9386 3.28563 31.6323 3.00799C33.5746 2.22136 35.1007 3.8409 35.1007 5.55298C35.0082 6.5247 34.7308 12.4939 34.6845 14.4373C34.6845 14.7612 34.6845 15.1314 34.592 15.4553C34.4533 16.0568 33.9446 16.4733 33.3896 16.427C32.8809 16.3807 32.4185 16.0106 32.326 15.409C32.2335 14.8538 32.2797 14.2985 32.2797 13.6969C32.2797 13.5118 32.2797 13.2342 32.2335 12.8178ZM26.9614 14.3448C29.6899 13.2805 32.6497 8.51442 32.3722 5.64552C32.3722 5.46043 32.1872 5.36789 32.0485 5.46043C29.6899 6.5247 26.684 11.337 26.684 14.1134C26.684 14.2522 26.8227 14.391 26.9614 14.3448Z"/>
367
+ <path d="M40.4189 7.91296C41.9451 6.84869 43.2399 5.92324 44.5348 5.04406C44.8586 4.8127 45.1823 4.58134 45.5522 4.39625C46.2459 4.02607 46.8934 4.16489 47.2171 4.67388C47.5408 5.18288 47.4483 5.73815 46.8934 6.24715C46.5234 6.57106 46.1534 6.84869 45.7372 7.12633C44.3498 8.09805 42.9625 9.06977 41.4363 10.134C41.8063 10.3191 41.9913 10.5042 42.2688 10.5968C43.8874 11.3834 45.4598 12.1238 47.0784 12.8641C47.4483 13.0492 47.8183 13.2343 48.0958 13.5119C48.512 13.8821 48.5582 14.3448 48.327 14.8538C48.0958 15.3166 47.6333 15.5479 47.1246 15.4554C46.7546 15.4091 46.4309 15.224 46.1072 15.0852C44.5811 14.3448 43.055 13.6045 41.4826 12.9104C41.2051 12.7716 40.8814 12.6328 40.4652 12.4939C40.4189 12.8178 40.3727 13.0492 40.3727 13.2806C40.3727 14.3448 40.3264 14.9464 40.3264 16.0569C40.3264 16.3808 40.3264 16.7047 40.2802 17.0286C40.1415 17.6765 39.7252 18.0466 39.1703 18.0466C38.6153 18.0466 38.1529 17.6765 38.0141 17.0286C37.9679 16.7047 37.9679 16.3808 37.9679 16.0569C38.0141 10.643 38.1066 7.58905 38.1529 2.17517C38.1529 1.75872 38.1991 1.34227 38.2454 0.972085C38.3379 0.463088 38.7078 0.139181 39.2628 0.092908C39.7715 0.000363052 40.234 0.370543 40.4189 0.925813C40.5114 1.29599 40.5114 2.03635 40.5114 2.03635C40.5114 4.16489 40.4189 4.48879 40.3727 6.61733C40.3727 6.94124 40.3727 7.31142 40.4189 7.91296Z"/>
368
+ <path d="M51.5643 9.20847C50.7781 8.88456 50.5468 8.09793 51.0093 7.35757C51.148 7.12621 51.3793 6.89485 51.6105 6.70976C53.8766 4.48868 55.4027 3.19305 57.6687 1.01824C57.9462 0.740604 58.2237 0.509241 58.5012 0.324151C59.0099 0.000243835 59.5648 0.0465163 59.9348 0.416696C60.3048 0.786876 60.351 1.34215 60.0273 1.85114C59.796 2.17505 59.5186 2.49896 59.2411 2.77659C57.6687 4.34986 56.8825 4.9514 55.3102 6.52467C55.079 6.75603 54.8015 7.03366 54.4315 7.45012C55.5877 7.68148 56.5126 7.91284 57.4837 8.1442C57.9462 8.23675 58.4087 8.32929 58.8711 8.46811C59.3798 8.60693 59.796 8.93084 59.8885 9.53238C59.981 10.0876 59.7036 10.5504 59.1024 10.828C57.9462 11.337 56.7901 11.7535 55.5877 12.2162C54.894 12.4938 54.154 12.7715 53.2754 13.1416C53.6453 13.373 56.1889 14.7149 57.345 15.3627C57.6225 15.5478 57.9462 15.6866 58.1774 15.918C58.6399 16.3344 58.7786 16.936 58.5012 17.3987C58.2237 17.8614 57.6687 18.0928 57.0675 17.9077C56.8363 17.8152 56.6051 17.7226 56.3738 17.5838C54.339 16.427 52.4429 15.5478 50.4081 14.3447C50.2694 14.2522 50.0844 14.1596 49.9456 14.0671C49.067 13.373 49.1132 12.355 50.1306 11.846C50.9168 11.4296 55.2177 9.9951 55.2177 9.9951C55.2177 9.9951 51.9342 9.39356 51.5643 9.20847Z"/>
369
+ <path d="M83.0116 9.9949C82.0866 9.90236 81.4392 9.85608 80.7455 9.80981C79.7281 9.71727 78.7107 9.62472 77.6933 9.53218C77.4158 9.4859 77.1383 9.43963 76.8608 9.30081C75.566 8.79182 75.1497 7.49619 76.0284 6.2931C76.4446 5.69156 76.9533 5.18256 77.5083 4.71984C79.4044 3.1003 81.5779 2.12858 83.7977 1.20313C83.9827 1.11058 84.214 1.01804 84.4452 1.06431C84.7227 1.11058 85.0464 1.2494 85.1389 1.48076C85.2314 1.7584 85.2314 2.22112 85.0926 2.45248C84.8152 2.82266 84.4452 3.05403 84.0752 3.28539C83.0116 3.88693 81.9016 4.4422 80.8842 5.04374C79.9593 5.59901 79.0806 6.15428 78.2482 7.21855C78.8494 7.26482 79.3119 7.35737 79.7743 7.40364C81.4392 7.54246 83.1041 7.635 84.7689 7.77382C85.1389 7.82009 85.5088 7.86637 85.8326 8.00518C86.9425 8.37536 87.2199 9.43963 86.4338 10.3651C86.0175 10.8741 85.4626 11.3368 84.9076 11.707C83.7052 12.5399 82.4104 13.3265 81.208 14.2057C80.2368 14.8998 79.2656 15.5939 78.2945 16.288C77.9245 16.5193 77.5545 16.797 77.1383 16.9358C76.6296 17.1209 76.2134 16.8895 75.9359 16.4268C75.6584 15.9641 75.7047 15.5013 76.0284 15.0849C76.2596 14.8072 76.5834 14.5759 76.8608 14.3445C78.7107 13.0489 80.5605 11.7995 82.3641 10.5502C82.5029 10.4576 82.6416 10.3188 83.0116 9.9949Z"/>
370
+ <path d="M3.56097 8.32919C4.53213 8.51428 5.22582 8.65309 5.96576 8.74564C6.79819 8.88446 7.63061 8.93073 8.50929 9.02327C9.20298 9.11582 9.80418 9.39345 10.0354 10.1338C10.2666 10.9204 9.89667 11.4757 9.34172 11.9384C9.06424 12.1698 8.74052 12.4012 8.46304 12.6325C6.38197 14.1132 4.3009 15.594 2.21983 17.0747C2.08109 17.1672 1.94236 17.306 1.75737 17.3986C1.24867 17.6762 0.693714 17.5837 0.323746 17.1672C-0.0462222 16.7508 -0.0924682 16.1955 0.231254 15.6865C0.462484 15.3163 0.786206 15.0387 1.10993 14.8073C2.63605 13.6968 4.16216 12.6325 5.87327 11.3832C5.36456 11.2444 5.04084 11.1981 4.71712 11.1055C3.83844 10.9204 2.95977 10.7816 2.12734 10.504C0.601222 9.94872 0.231254 8.60682 1.29491 7.31119C1.9886 6.47829 2.82103 5.73793 3.65346 5.04384C4.80961 4.16466 6.05825 3.37803 7.30689 2.5914C8.27806 1.94358 8.97175 1.98985 9.38796 2.63767C9.80418 3.28548 9.5267 3.9333 8.60178 4.62739C7.44563 5.46029 6.24323 6.24692 5.08708 7.07983C4.62462 7.40374 4.20841 7.77392 3.56097 8.32919Z"/>
371
+ </svg>
372
+ </div>
373
+ <div class="head-meta">
374
+ <div class="head-title">
375
+ <div class="t">__REPORT_TITLE__</div>
376
+ <div class="s mono">__PROJECT__</div>
377
+ </div>
378
+ <button class="theme-toggle" id="themeBtn" aria-label="__THEME_TOGGLE__">
379
+ <span id="themeIcon">🌙</span><span id="themeLabel">__THEME_DARK__</span>
380
+ </button>
381
+ </div>
382
+ </header>
383
+ <div class="accent-sweep"></div>
384
+
385
+ <div class="wrap">
386
+ <div class="grid">
387
+ __BODY__
388
+ </div>
389
+ </div>
390
+
391
+ <footer class="foot">
392
+ <div>__FOOTER__</div>
393
+ <div class="wm">
394
+ <span>Powered by</span>
395
+ <svg viewBox="0 0 87 18" aria-label="Shakers"><path d="M19.2845 8.74573C19.2845 8.74573 16.556 10.0876 15.7698 10.5041C15.7236 11.4295 15.6311 12.355 15.5848 13.2804C15.5386 14.3447 15.4924 14.9462 15.4461 16.0105C15.4461 16.2419 15.4461 16.4732 15.3999 16.6583C15.2611 17.445 14.8449 17.9077 14.2437 17.9077C13.55 17.9077 13.0876 17.3987 13.0876 16.5658C13.0876 15.1776 13.1801 14.2059 13.2263 12.8177C13.2263 12.4475 13.2263 11.8923 13.2263 11.4758C13.2263 11.4758 12.8101 11.6609 12.5326 11.7072C12.0701 11.6609 11.7002 11.4295 11.5614 10.9668C11.4227 10.5041 11.5614 10.0876 11.9314 9.76372C12.0239 9.71745 13.5963 8.74573 13.5963 8.74573C13.6425 6.80228 13.735 4.07221 13.8737 2.17503C13.92 1.80485 13.9662 1.43467 14.105 1.11077C14.2437 0.694314 14.6137 0.462951 15.0761 0.509223C15.5386 0.509223 15.8623 0.786858 15.9548 1.20331C16.0473 1.57349 16.0935 1.98994 16.0935 2.4064C16.0473 3.93339 16.0011 4.95138 15.9548 6.47837C15.9548 6.84856 15.9548 7.21873 15.9548 7.774C17.7122 6.9411 17.8971 6.70974 19.562 5.92311C19.6545 4.53493 19.7007 3.65575 19.7932 2.26758C19.8395 1.80485 19.8857 1.29586 19.9782 0.833131C20.117 0.324133 20.4869 0.000226092 21.0419 0.0464986C21.5968 0.0927711 21.9205 0.416678 22.013 0.925676C22.0593 1.34213 22.0593 1.71231 22.0593 2.12876C22.0593 3.05421 21.9668 3.7483 21.9668 4.81257C22.2905 4.72002 22.7067 4.30357 23.0304 4.2573C23.6779 4.07221 24.2791 4.34984 24.5103 4.90511C24.7416 5.41411 24.5566 5.96938 24.0016 6.33956C23.5854 6.52465 21.7356 7.68146 21.7356 7.68146C21.6893 10.6892 21.5968 12.8177 21.5043 15.8717C21.5043 16.1493 21.5043 16.4732 21.4581 16.7509C21.3193 17.445 20.8569 17.8614 20.2557 17.8151C19.6545 17.7689 19.2383 17.3524 19.1458 16.6583C19.0995 16.3807 19.192 12.4013 19.2845 9.76372C19.2845 9.39354 19.2845 9.20845 19.2845 8.74573Z"/><path d="M64.097 4.67388C63.542 4.72016 63.1258 4.76643 62.7558 4.76643C61.9697 4.76643 61.5072 4.48879 61.4147 3.88725C61.3222 3.28571 61.6459 2.82299 62.3859 2.59162C62.9408 2.40653 63.542 2.31399 64.097 2.22144C65.9931 1.99008 67.8429 1.89754 69.6927 2.45281C70.3402 2.6379 70.9414 2.91553 71.4963 3.28571C72.8837 4.21116 73.1612 5.73815 72.2825 7.1726C71.7738 8.0055 70.9876 8.60705 70.1552 9.06977C69.6002 9.39368 69.0453 9.67131 68.3978 9.99522C68.8603 10.4117 73.1149 13.5582 73.4849 14.0209C73.8549 14.4837 73.8086 15.1315 73.4386 15.5479C73.0687 15.9644 72.56 16.0569 72.005 15.8256C71.6813 15.6405 71.4038 15.4091 71.0801 15.1777C69.8315 14.206 67.6579 12.6328 66.3168 11.5685C66.2705 12.0312 65.9931 15.0389 65.9468 16.1032C65.9468 16.3808 65.9468 16.7047 65.8543 16.9824C65.7156 17.5376 65.2069 17.8615 64.6982 17.8153C64.1432 17.769 63.7733 17.4914 63.6345 16.9824C63.542 16.5659 63.542 16.1032 63.542 15.6867C63.727 12.6328 64.097 5.18288 64.097 4.67388ZM66.4555 8.14432C67.7504 7.63532 68.9528 7.1726 69.8777 6.24715C70.2477 5.87697 70.1552 5.27543 69.6927 5.04406C68.6753 4.58134 67.6117 4.53507 66.548 4.44252L66.4555 8.14432Z"/><path d="M32.2335 12.8178C31.9097 13.1879 31.6785 13.4656 31.4473 13.6969C30.6149 14.4836 29.8287 15.3627 28.95 16.0568C28.4413 16.4733 27.7939 16.7047 27.1464 16.8897C25.7128 17.2599 24.4641 16.427 24.2792 14.8538C24.1867 14.0208 24.2329 13.0954 24.4641 12.2625C25.3428 9.02342 27.1002 6.29334 29.6437 4.1648C30.2449 3.65581 30.9386 3.28563 31.6323 3.00799C33.5746 2.22136 35.1007 3.8409 35.1007 5.55298C35.0082 6.5247 34.7308 12.4939 34.6845 14.4373C34.6845 14.7612 34.6845 15.1314 34.592 15.4553C34.4533 16.0568 33.9446 16.4733 33.3896 16.427C32.8809 16.3807 32.4185 16.0106 32.326 15.409C32.2335 14.8538 32.2797 14.2985 32.2797 13.6969C32.2797 13.5118 32.2797 13.2342 32.2335 12.8178ZM26.9614 14.3448C29.6899 13.2805 32.6497 8.51442 32.3722 5.64552C32.3722 5.46043 32.1872 5.36789 32.0485 5.46043C29.6899 6.5247 26.684 11.337 26.684 14.1134C26.684 14.2522 26.8227 14.391 26.9614 14.3448Z"/><path d="M40.4189 7.91296C41.9451 6.84869 43.2399 5.92324 44.5348 5.04406C44.8586 4.8127 45.1823 4.58134 45.5522 4.39625C46.2459 4.02607 46.8934 4.16489 47.2171 4.67388C47.5408 5.18288 47.4483 5.73815 46.8934 6.24715C46.5234 6.57106 46.1534 6.84869 45.7372 7.12633C44.3498 8.09805 42.9625 9.06977 41.4363 10.134C41.8063 10.3191 41.9913 10.5042 42.2688 10.5968C43.8874 11.3834 45.4598 12.1238 47.0784 12.8641C47.4483 13.0492 47.8183 13.2343 48.0958 13.5119C48.512 13.8821 48.5582 14.3448 48.327 14.8538C48.0958 15.3166 47.6333 15.5479 47.1246 15.4554C46.7546 15.4091 46.4309 15.224 46.1072 15.0852C44.5811 14.3448 43.055 13.6045 41.4826 12.9104C41.2051 12.7716 40.8814 12.6328 40.4652 12.4939C40.4189 12.8178 40.3727 13.0492 40.3727 13.2806C40.3727 14.3448 40.3264 14.9464 40.3264 16.0569C40.3264 16.3808 40.3264 16.7047 40.2802 17.0286C40.1415 17.6765 39.7252 18.0466 39.1703 18.0466C38.6153 18.0466 38.1529 17.6765 38.0141 17.0286C37.9679 16.7047 37.9679 16.3808 37.9679 16.0569C38.0141 10.643 38.1066 7.58905 38.1529 2.17517C38.1529 1.75872 38.1991 1.34227 38.2454 0.972085C38.3379 0.463088 38.7078 0.139181 39.2628 0.092908C39.7715 0.000363052 40.234 0.370543 40.4189 0.925813C40.5114 1.29599 40.5114 2.03635 40.5114 2.03635C40.5114 4.16489 40.4189 4.48879 40.3727 6.61733C40.3727 6.94124 40.3727 7.31142 40.4189 7.91296Z"/><path d="M51.5643 9.20847C50.7781 8.88456 50.5468 8.09793 51.0093 7.35757C51.148 7.12621 51.3793 6.89485 51.6105 6.70976C53.8766 4.48868 55.4027 3.19305 57.6687 1.01824C57.9462 0.740604 58.2237 0.509241 58.5012 0.324151C59.0099 0.000243835 59.5648 0.0465163 59.9348 0.416696C60.3048 0.786876 60.351 1.34215 60.0273 1.85114C59.796 2.17505 59.5186 2.49896 59.2411 2.77659C57.6687 4.34986 56.8825 4.9514 55.3102 6.52467C55.079 6.75603 54.8015 7.03366 54.4315 7.45012C55.5877 7.68148 56.5126 7.91284 57.4837 8.1442C57.9462 8.23675 58.4087 8.32929 58.8711 8.46811C59.3798 8.60693 59.796 8.93084 59.8885 9.53238C59.981 10.0876 59.7036 10.5504 59.1024 10.828C57.9462 11.337 56.7901 11.7535 55.5877 12.2162C54.894 12.4938 54.154 12.7715 53.2754 13.1416C53.6453 13.373 56.1889 14.7149 57.345 15.3627C57.6225 15.5478 57.9462 15.6866 58.1774 15.918C58.6399 16.3344 58.7786 16.936 58.5012 17.3987C58.2237 17.8614 57.6687 18.0928 57.0675 17.9077C56.8363 17.8152 56.6051 17.7226 56.3738 17.5838C54.339 16.427 52.4429 15.5478 50.4081 14.3447C50.2694 14.2522 50.0844 14.1596 49.9456 14.0671C49.067 13.373 49.1132 12.355 50.1306 11.846C50.9168 11.4296 55.2177 9.9951 55.2177 9.9951C55.2177 9.9951 51.9342 9.39356 51.5643 9.20847Z"/><path d="M83.0116 9.9949C82.0866 9.90236 81.4392 9.85608 80.7455 9.80981C79.7281 9.71727 78.7107 9.62472 77.6933 9.53218C77.4158 9.4859 77.1383 9.43963 76.8608 9.30081C75.566 8.79182 75.1497 7.49619 76.0284 6.2931C76.4446 5.69156 76.9533 5.18256 77.5083 4.71984C79.4044 3.1003 81.5779 2.12858 83.7977 1.20313C83.9827 1.11058 84.214 1.01804 84.4452 1.06431C84.7227 1.11058 85.0464 1.2494 85.1389 1.48076C85.2314 1.7584 85.2314 2.22112 85.0926 2.45248C84.8152 2.82266 84.4452 3.05403 84.0752 3.28539C83.0116 3.88693 81.9016 4.4422 80.8842 5.04374C79.9593 5.59901 79.0806 6.15428 78.2482 7.21855C78.8494 7.26482 79.3119 7.35737 79.7743 7.40364C81.4392 7.54246 83.1041 7.635 84.7689 7.77382C85.1389 7.82009 85.5088 7.86637 85.8326 8.00518C86.9425 8.37536 87.2199 9.43963 86.4338 10.3651C86.0175 10.8741 85.4626 11.3368 84.9076 11.707C83.7052 12.5399 82.4104 13.3265 81.208 14.2057C80.2368 14.8998 79.2656 15.5939 78.2945 16.288C77.9245 16.5193 77.5545 16.797 77.1383 16.9358C76.6296 17.1209 76.2134 16.8895 75.9359 16.4268C75.6584 15.9641 75.7047 15.5013 76.0284 15.0849C76.2596 14.8072 76.5834 14.5759 76.8608 14.3445C78.7107 13.0489 80.5605 11.7995 82.3641 10.5502C82.5029 10.4576 82.6416 10.3188 83.0116 9.9949Z"/><path d="M3.56097 8.32919C4.53213 8.51428 5.22582 8.65309 5.96576 8.74564C6.79819 8.88446 7.63061 8.93073 8.50929 9.02327C9.20298 9.11582 9.80418 9.39345 10.0354 10.1338C10.2666 10.9204 9.89667 11.4757 9.34172 11.9384C9.06424 12.1698 8.74052 12.4012 8.46304 12.6325C6.38197 14.1132 4.3009 15.594 2.21983 17.0747C2.08109 17.1672 1.94236 17.306 1.75737 17.3986C1.24867 17.6762 0.693714 17.5837 0.323746 17.1672C-0.0462222 16.7508 -0.0924682 16.1955 0.231254 15.6865C0.462484 15.3163 0.786206 15.0387 1.10993 14.8073C2.63605 13.6968 4.16216 12.6325 5.87327 11.3832C5.36456 11.2444 5.04084 11.1981 4.71712 11.1055C3.83844 10.9204 2.95977 10.7816 2.12734 10.504C0.601222 9.94872 0.231254 8.60682 1.29491 7.31119C1.9886 6.47829 2.82103 5.73793 3.65346 5.04384C4.80961 4.16466 6.05825 3.37803 7.30689 2.5914C8.27806 1.94358 8.97175 1.98985 9.38796 2.63767C9.80418 3.28548 9.5267 3.9333 8.60178 4.62739C7.44563 5.46029 6.24323 6.24692 5.08708 7.07983C4.62462 7.40374 4.20841 7.77392 3.56097 8.32919Z"/></svg>
396
+ </div>
397
+ </footer>
398
+
399
+ <script>
400
+ (function(){
401
+ "use strict";
402
+
403
+ /* Runs ONE independent init/update step in isolation from its siblings
404
+ (issue 067). `load`, `resize` and the theme toggle each chain several
405
+ UNRELATED effects (paint the ring, count up the score, slide the tab
406
+ indicator, sync a label…) as a plain sequence of statements. Before this,
407
+ one throwing — as `paintRing()` did pre-066, on ITS OWN unguarded lookup —
408
+ killed every sibling after it: the tab indicator stayed unpositioned, the
409
+ expand-all label unsynced, forever, because JS does not resume a function
410
+ after an uncaught throw partway through it.
411
+ Two things this is NOT, on purpose:
412
+ - NOT one try/catch around the whole handler: that swallows the error
413
+ just as quietly and still never reaches the statements after the
414
+ first failing one — same bug, quieter.
415
+ - NOT an `if(!node) return;` guard added to every function that touches
416
+ a DOM node: that only defangs the SPECIFIC lookup someone thought to
417
+ guard (which is exactly the "grep for `if(!fill)`" this issue calls
418
+ out as useless) — it does nothing for a step that fails for any OTHER
419
+ reason. Isolating the SEQUENCE, not the dereference, is what keeps a
420
+ sibling step alive regardless of why one of them broke. */
421
+ function safeStep(fn){
422
+ try { fn(); } catch(e){ if(typeof console!=='undefined' && console.error) console.error(e); }
423
+ }
424
+
425
+ /* ---- Theme toggle (light default; dark = foglamp-style preview) ---- */
426
+ var root = document.documentElement;
427
+ var btn = document.getElementById('themeBtn');
428
+ var icon = document.getElementById('themeIcon');
429
+ var label = document.getElementById('themeLabel');
430
+ btn.addEventListener('click', function(){
431
+ var dark = root.getAttribute('data-theme') === 'dark';
432
+ root.setAttribute('data-theme', dark ? 'light' : 'dark');
433
+ icon.textContent = dark ? '🌙' : '☀️';
434
+ label.textContent = dark ? '__JS_THEME_DARK__' : '__JS_THEME_LIGHT__';
435
+ // re-run the score ring so it re-animates the accent in the new theme —
436
+ // isolated: a broken repaint must not make the toggle itself look broken.
437
+ safeStep(paintRing);
438
+ });
439
+
440
+ /* ---- Score ring animation (stroke-dashoffset) ---- */
441
+ var SCORE = __SCORE__;
442
+ function paintRing(){
443
+ var fill = document.getElementById('ringFill');
444
+ /* The hero ring exists ONLY when this project has footprint data: a report
445
+ built from certifications alone renders the left column as an empty state,
446
+ so #ringFill (and #scoreNum below) are simply not there. Without these two
447
+ guards the `load` handler threw a TypeError on its FIRST statement and never
448
+ reached countUp / moveInd / syncExpandLabel — leaving the tab indicator
449
+ unpositioned and the expand-all label unsynced — and the theme toggle, whose
450
+ handler ends in paintRing(), threw on every single click. A broken script
451
+ fails no test; it silently degrades a report someone is reading. Found in
452
+ issue 066, when a report with only an agent certification became reachable
453
+ for the first time. */
454
+ if(!fill) return;
455
+ var circumference = 2 * Math.PI * 54; // r=54
456
+ fill.style.strokeDasharray = circumference;
457
+ fill.style.strokeDashoffset = circumference;
458
+ // next frame -> animate to target
459
+ requestAnimationFrame(function(){
460
+ requestAnimationFrame(function(){
461
+ fill.style.strokeDashoffset = circumference * (1 - SCORE/100);
462
+ });
463
+ });
464
+ }
465
+ // count-up number
466
+ function countUp(){
467
+ var el = document.getElementById('scoreNum');
468
+ if(!el) return; /* same reason as paintRing(): no footprint, no score node. */
469
+ var start = null, dur = 1400;
470
+ function step(ts){
471
+ if(!start) start = ts;
472
+ var t = Math.min((ts-start)/dur, 1);
473
+ var eased = 1 - Math.pow(1-t, 3);
474
+ el.textContent = Math.round(SCORE * eased);
475
+ if(t < 1) requestAnimationFrame(step);
476
+ }
477
+ requestAnimationFrame(step);
478
+ }
479
+
480
+ /* ---- Reveal on scroll/load (stagger) ---- */
481
+ var reveals = Array.prototype.slice.call(document.querySelectorAll('.reveal'));
482
+ if('IntersectionObserver' in window){
483
+ var io = new IntersectionObserver(function(entries){
484
+ entries.forEach(function(e){
485
+ if(e.isIntersecting){
486
+ var idx = reveals.indexOf(e.target);
487
+ e.target.style.transitionDelay = (Math.max(0, idx) * 80) + 'ms';
488
+ e.target.classList.add('in');
489
+ io.unobserve(e.target);
490
+ }
491
+ });
492
+ }, {threshold:0.12});
493
+ reveals.forEach(function(r){ io.observe(r); });
494
+ } else {
495
+ reveals.forEach(function(r){ r.classList.add('in'); });
496
+ }
497
+
498
+ /* ---- Tabs (with sliding indicator) ---- */
499
+ var tabsBar = document.getElementById('tabs');
500
+ var tabs = Array.prototype.slice.call(tabsBar.querySelectorAll('.tab'));
501
+ var ind = document.getElementById('tabInd');
502
+ var panels = Array.prototype.slice.call(document.querySelectorAll('.tabpanel'));
503
+ function moveInd(tab){
504
+ ind.style.left = tab.offsetLeft + 'px';
505
+ ind.style.width = tab.offsetWidth + 'px';
506
+ }
507
+ function selectTab(tab){
508
+ tabs.forEach(function(t){ t.classList.toggle('active', t===tab); });
509
+ var key = tab.getAttribute('data-tab');
510
+ panels.forEach(function(p){ p.classList.toggle('show', p.getAttribute('data-panel')===key); });
511
+ moveInd(tab);
512
+ syncExpandLabel();
513
+ }
514
+ tabs.forEach(function(t){ t.addEventListener('click', function(){ selectTab(t); }); });
515
+
516
+ /* ---- Accordions ---- */
517
+ function accordions(){ return Array.prototype.slice.call(document.querySelectorAll('.acc')); }
518
+ document.addEventListener('click', function(ev){
519
+ var head = ev.target.closest && ev.target.closest('.acc-head');
520
+ if(head){
521
+ var acc = head.parentElement;
522
+ acc.classList.toggle('open');
523
+ syncExpandLabel();
524
+ }
525
+ var copy = ev.target.closest && ev.target.closest('.copy-btn');
526
+ if(copy){
527
+ ev.stopPropagation();
528
+ var id = copy.getAttribute('data-copy');
529
+ var pre = document.getElementById(id);
530
+ if(pre && navigator.clipboard){
531
+ navigator.clipboard.writeText(pre.textContent).then(function(){
532
+ var old = copy.textContent; copy.textContent = '__JS_COPIED__';
533
+ setTimeout(function(){ copy.textContent = old; }, 1400);
534
+ }).catch(function(){});
535
+ }
536
+ }
537
+ });
538
+
539
+ /* ---- Expand all / collapse all (per visible panel) ---- */
540
+ var expandBtn = document.getElementById('expandAll');
541
+ function visiblePanel(){ return document.querySelector('.tabpanel.show'); }
542
+ function visibleAccs(){
543
+ var p = visiblePanel();
544
+ return p ? Array.prototype.slice.call(p.querySelectorAll('.acc')) : [];
545
+ }
546
+ function syncExpandLabel(){
547
+ var accs = visibleAccs();
548
+ var allOpen = accs.length && accs.every(function(a){ return a.classList.contains('open'); });
549
+ expandBtn.textContent = allOpen ? '__JS_COLLAPSE_ALL__' : '__JS_EXPAND_ALL__';
550
+ }
551
+ expandBtn.addEventListener('click', function(){
552
+ var accs = visibleAccs();
553
+ var allOpen = accs.length && accs.every(function(a){ return a.classList.contains('open'); });
554
+ accs.forEach(function(a){ a.classList.toggle('open', !allOpen); });
555
+ syncExpandLabel();
556
+ });
557
+
558
+ /* ---- init ---- */
559
+ // Four UNRELATED effects (issue 067): each isolated so one failing never
560
+ // takes the other three down with it — see safeStep() above for why this,
561
+ // and not a guard on #ringFill/#scoreNum alone, is the fix at this level.
562
+ window.addEventListener('load', function(){
563
+ safeStep(paintRing);
564
+ safeStep(countUp);
565
+ safeStep(function(){ moveInd(document.querySelector('.tab.active')); });
566
+ safeStep(syncExpandLabel);
567
+ });
568
+ window.addEventListener('resize', function(){ safeStep(function(){ moveInd(document.querySelector('.tab.active')); }); });
569
+ })();
570
+ </script>
571
+ </body>
572
+ </html>