@palate/cli 0.0.0-stage → 0.0.1

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 (1222) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +151 -2
  3. package/bin/palate.mjs +894 -0
  4. package/kb/anatomy/data-table.md +24 -0
  5. package/kb/anatomy/empty-state.md +16 -0
  6. package/kb/anatomy/filter-toolbar.md +19 -0
  7. package/kb/anatomy/meter.md +16 -0
  8. package/kb/anatomy/product-window.md +18 -0
  9. package/kb/anatomy/settings-page.md +18 -0
  10. package/kb/anatomy/status-indicator.md +17 -0
  11. package/kb/anatomy/toast-undo.md +16 -0
  12. package/kb/bench/briefs.json +618 -0
  13. package/kb/craft/SOURCES.md +48 -0
  14. package/kb/craft/creativity.md +55 -0
  15. package/kb/craft/details.md +75 -0
  16. package/kb/craft/landing-demos.md +46 -0
  17. package/kb/craft/mistakes.md +73 -0
  18. package/kb/craft/motion.md +53 -0
  19. package/kb/craft/principles.md +68 -0
  20. package/kb/index/discovery.json +1 -0
  21. package/kb/inspiration/index.json +79 -0
  22. package/kb/moves/animated-comparison-table.md +58 -0
  23. package/kb/moves/before-after-slider.md +58 -0
  24. package/kb/moves/bento-live-tiles.md +57 -0
  25. package/kb/moves/calm-motion-system.md +63 -0
  26. package/kb/moves/canvas-grain.md +58 -0
  27. package/kb/moves/changelog-as-magazine.md +56 -0
  28. package/kb/moves/code-drawn-diagram.md +56 -0
  29. package/kb/moves/colophon.md +54 -0
  30. package/kb/moves/command-palette-site-nav.md +58 -0
  31. package/kb/moves/confetti-free-celebration.md +58 -0
  32. package/kb/moves/console-easter-egg.md +57 -0
  33. package/kb/moves/copy-confirmation.md +57 -0
  34. package/kb/moves/cursor-reactive-light.md +57 -0
  35. package/kb/moves/dark-band-contrast-section.md +55 -0
  36. package/kb/moves/designed-dark-mode.md +58 -0
  37. package/kb/moves/duotone-photography.md +55 -0
  38. package/kb/moves/editorial-drop-cap.md +58 -0
  39. package/kb/moves/editorial-grid-pull-quotes.md +55 -0
  40. package/kb/moves/engraving-colour-lens.md +64 -0
  41. package/kb/moves/explorable-control.md +57 -0
  42. package/kb/moves/feature-films-on-painted-stages.md +58 -0
  43. package/kb/moves/first-win-onboarding.md +59 -0
  44. package/kb/moves/footer-live-status.md +56 -0
  45. package/kb/moves/footer-rewards-the-end.md +58 -0
  46. package/kb/moves/generative-brand-pattern.md +55 -0
  47. package/kb/moves/gentle-error-nudge.md +59 -0
  48. package/kb/moves/halftone-dot-imagery.md +55 -0
  49. package/kb/moves/hand-drawn-annotations.md +55 -0
  50. package/kb/moves/hero-paints-itself-in.md +65 -0
  51. package/kb/moves/honest-progress-narration.md +58 -0
  52. package/kb/moves/honest-testimonial-wall.md +57 -0
  53. package/kb/moves/inline-sparklines-in-copy.md +57 -0
  54. package/kb/moves/inspect-our-taste.md +58 -0
  55. package/kb/moves/interactive-pricing-calculator.md +59 -0
  56. package/kb/moves/isometric-illustration-system.md +56 -0
  57. package/kb/moves/kinetic-variable-type.md +57 -0
  58. package/kb/moves/konami-secret.md +59 -0
  59. package/kb/moves/layered-paper-depth.md +56 -0
  60. package/kb/moves/lightweight-3d-object-hero.md +59 -0
  61. package/kb/moves/line-engraving-from-image.md +63 -0
  62. package/kb/moves/link-preview-peek.md +57 -0
  63. package/kb/moves/live-data-ticker.md +61 -0
  64. package/kb/moves/made-with-proof.md +51 -0
  65. package/kb/moves/magnetic-buttons.md +55 -0
  66. package/kb/moves/marquee-done-right.md +58 -0
  67. package/kb/moves/moves.yaml +450 -0
  68. package/kb/moves/museum-painting-backdrop.md +59 -0
  69. package/kb/moves/one-colour-brand-world.md +56 -0
  70. package/kb/moves/playful-404.md +58 -0
  71. package/kb/moves/pointer-highlight-word.md +56 -0
  72. package/kb/moves/product-in-context.md +56 -0
  73. package/kb/moves/real-world-data.md +58 -0
  74. package/kb/moves/referral-reward-reveal.md +59 -0
  75. package/kb/moves/riso-print-texture.md +57 -0
  76. package/kb/moves/screenshot-detail-crops.md +56 -0
  77. package/kb/moves/scroll-progress-chapters.md +57 -0
  78. package/kb/moves/scroll-scrubbed-product-film.md +62 -0
  79. package/kb/moves/self-playing-product-demo.md +61 -0
  80. package/kb/moves/shape-continuity-morph.md +58 -0
  81. package/kb/moves/sliding-nav-indicator.md +58 -0
  82. package/kb/moves/sticky-scroll-story.md +56 -0
  83. package/kb/moves/story-led-long-scroll.md +56 -0
  84. package/kb/moves/tactile-button.md +63 -0
  85. package/kb/moves/technical-drawing-lines.md +57 -0
  86. package/kb/moves/time-of-day-theming.md +56 -0
  87. package/kb/moves/tiny-game-easter-egg.md +60 -0
  88. package/kb/moves/type-decode-reveal.md +57 -0
  89. package/kb/moves/typographic-numeral-hero.md +58 -0
  90. package/kb/moves/view-transitions-between-pages.md +57 -0
  91. package/kb/moves/waitlist-position-live.md +60 -0
  92. package/kb/patterns/app-team-members.md +27 -0
  93. package/kb/patterns/chat-agent-approvals.md +31 -0
  94. package/kb/patterns/commerce-product-page.md +34 -0
  95. package/kb/patterns/dashboard-entity-table.md +37 -0
  96. package/kb/patterns/dashboard-home-today.md +24 -0
  97. package/kb/patterns/docs-api-reference.md +31 -0
  98. package/kb/patterns/marketing-product-led-landing.md +30 -0
  99. package/kb/patterns/mobile-daily-checklist.md +26 -0
  100. package/kb/references/index.json +1 -0
  101. package/kb/references/thumbs/004c5e9072.jpg +0 -0
  102. package/kb/references/thumbs/008915eb21.jpg +0 -0
  103. package/kb/references/thumbs/0098cf66c2.jpg +0 -0
  104. package/kb/references/thumbs/012e61b31f.jpg +0 -0
  105. package/kb/references/thumbs/014d4e46fc.jpg +0 -0
  106. package/kb/references/thumbs/01e54f394a.jpg +0 -0
  107. package/kb/references/thumbs/01e99a579c.jpg +0 -0
  108. package/kb/references/thumbs/01fd014575.jpg +0 -0
  109. package/kb/references/thumbs/022778ca72.jpg +0 -0
  110. package/kb/references/thumbs/02717bf1e1.jpg +0 -0
  111. package/kb/references/thumbs/02a5cfdbe9.jpg +0 -0
  112. package/kb/references/thumbs/02c9823f8d.jpg +0 -0
  113. package/kb/references/thumbs/02f3974613.jpg +0 -0
  114. package/kb/references/thumbs/032427f1ce.jpg +0 -0
  115. package/kb/references/thumbs/03ba677d07.jpg +0 -0
  116. package/kb/references/thumbs/04f2476cfe.jpg +0 -0
  117. package/kb/references/thumbs/0563e7c52e.jpg +0 -0
  118. package/kb/references/thumbs/05fa6b8eef.jpg +0 -0
  119. package/kb/references/thumbs/065bce1517.jpg +0 -0
  120. package/kb/references/thumbs/06c017f113.jpg +0 -0
  121. package/kb/references/thumbs/0770e2d8d3.jpg +0 -0
  122. package/kb/references/thumbs/078a26c4cb.jpg +0 -0
  123. package/kb/references/thumbs/08679b6e84.jpg +0 -0
  124. package/kb/references/thumbs/088c45a34c.jpg +0 -0
  125. package/kb/references/thumbs/089e9fd4e4.jpg +0 -0
  126. package/kb/references/thumbs/08f773231e.jpg +0 -0
  127. package/kb/references/thumbs/093ca3e2e3.jpg +0 -0
  128. package/kb/references/thumbs/0963320f74.jpg +0 -0
  129. package/kb/references/thumbs/09fc1ded85.jpg +0 -0
  130. package/kb/references/thumbs/0a368f3963.jpg +0 -0
  131. package/kb/references/thumbs/0a9e0e5201.jpg +0 -0
  132. package/kb/references/thumbs/0aace67ee7.jpg +0 -0
  133. package/kb/references/thumbs/0b61ecf793.jpg +0 -0
  134. package/kb/references/thumbs/0bde18ae09.jpg +0 -0
  135. package/kb/references/thumbs/0bf7ee8143.jpg +0 -0
  136. package/kb/references/thumbs/0ca20a13e0.jpg +0 -0
  137. package/kb/references/thumbs/0cdc0198c4.jpg +0 -0
  138. package/kb/references/thumbs/0cf9790b28.jpg +0 -0
  139. package/kb/references/thumbs/0d0202904b.jpg +0 -0
  140. package/kb/references/thumbs/0d053aba18.jpg +0 -0
  141. package/kb/references/thumbs/0d0c2c13d2.jpg +0 -0
  142. package/kb/references/thumbs/0d30be99b0.jpg +0 -0
  143. package/kb/references/thumbs/0d503da1b7.jpg +0 -0
  144. package/kb/references/thumbs/0ddad7b740.jpg +0 -0
  145. package/kb/references/thumbs/0e0a148101.jpg +0 -0
  146. package/kb/references/thumbs/0e401b6b75.jpg +0 -0
  147. package/kb/references/thumbs/0e8f32ba0f.jpg +0 -0
  148. package/kb/references/thumbs/0ef1966a7d.jpg +0 -0
  149. package/kb/references/thumbs/0efd416d11.jpg +0 -0
  150. package/kb/references/thumbs/0f3af263ae.jpg +0 -0
  151. package/kb/references/thumbs/0f9644c3a5.jpg +0 -0
  152. package/kb/references/thumbs/1020552737.jpg +0 -0
  153. package/kb/references/thumbs/1037626587.jpg +0 -0
  154. package/kb/references/thumbs/10512b7e94.jpg +0 -0
  155. package/kb/references/thumbs/106dc4a7b5.jpg +0 -0
  156. package/kb/references/thumbs/108943f6f7.jpg +0 -0
  157. package/kb/references/thumbs/10a5551d51.jpg +0 -0
  158. package/kb/references/thumbs/10dd01076c.jpg +0 -0
  159. package/kb/references/thumbs/11b06e39da.jpg +0 -0
  160. package/kb/references/thumbs/11baa1ec83.jpg +0 -0
  161. package/kb/references/thumbs/11bbe12167.jpg +0 -0
  162. package/kb/references/thumbs/11ca37feb8.jpg +0 -0
  163. package/kb/references/thumbs/11f14d3f12.jpg +0 -0
  164. package/kb/references/thumbs/124530ceb5.jpg +0 -0
  165. package/kb/references/thumbs/1276402336.jpg +0 -0
  166. package/kb/references/thumbs/1280e5c9d6.jpg +0 -0
  167. package/kb/references/thumbs/12cac0d172.jpg +0 -0
  168. package/kb/references/thumbs/138be6623a.jpg +0 -0
  169. package/kb/references/thumbs/13a0c544d1.jpg +0 -0
  170. package/kb/references/thumbs/13a32cb94c.jpg +0 -0
  171. package/kb/references/thumbs/144035d641.jpg +0 -0
  172. package/kb/references/thumbs/1457e41924.jpg +0 -0
  173. package/kb/references/thumbs/14972e3a86.jpg +0 -0
  174. package/kb/references/thumbs/15a47f2e4c.jpg +0 -0
  175. package/kb/references/thumbs/15aa15d22e.jpg +0 -0
  176. package/kb/references/thumbs/160dcd24b7.jpg +0 -0
  177. package/kb/references/thumbs/1610978529.jpg +0 -0
  178. package/kb/references/thumbs/16db30ba37.jpg +0 -0
  179. package/kb/references/thumbs/16f19ae4f3.jpg +0 -0
  180. package/kb/references/thumbs/16f758b03f.jpg +0 -0
  181. package/kb/references/thumbs/1748e5b751.jpg +0 -0
  182. package/kb/references/thumbs/175d0b0e69.jpg +0 -0
  183. package/kb/references/thumbs/178f571a36.jpg +0 -0
  184. package/kb/references/thumbs/17c43dc025.jpg +0 -0
  185. package/kb/references/thumbs/17c7f05b50.jpg +0 -0
  186. package/kb/references/thumbs/17f97e61dd.jpg +0 -0
  187. package/kb/references/thumbs/1804e17365.jpg +0 -0
  188. package/kb/references/thumbs/195c162ed1.jpg +0 -0
  189. package/kb/references/thumbs/19e3699c29.jpg +0 -0
  190. package/kb/references/thumbs/1a48e0a4d5.jpg +0 -0
  191. package/kb/references/thumbs/1a6cda265e.jpg +0 -0
  192. package/kb/references/thumbs/1a7e012091.jpg +0 -0
  193. package/kb/references/thumbs/1a9173a172.jpg +0 -0
  194. package/kb/references/thumbs/1b34a6b4d2.jpg +0 -0
  195. package/kb/references/thumbs/1b5284f3b2.jpg +0 -0
  196. package/kb/references/thumbs/1b93fbd6d3.jpg +0 -0
  197. package/kb/references/thumbs/1c6728fef6.jpg +0 -0
  198. package/kb/references/thumbs/1c6f101618.jpg +0 -0
  199. package/kb/references/thumbs/1cf7011a94.jpg +0 -0
  200. package/kb/references/thumbs/1d1769e539.jpg +0 -0
  201. package/kb/references/thumbs/1d1fd23199.jpg +0 -0
  202. package/kb/references/thumbs/1d7b6602ac.jpg +0 -0
  203. package/kb/references/thumbs/1dbbc61b9e.jpg +0 -0
  204. package/kb/references/thumbs/1dbdd6d21d.jpg +0 -0
  205. package/kb/references/thumbs/1dd873744b.jpg +0 -0
  206. package/kb/references/thumbs/1df4dd7cf9.jpg +0 -0
  207. package/kb/references/thumbs/1ece8c35b8.jpg +0 -0
  208. package/kb/references/thumbs/1f0e5b9aa4.jpg +0 -0
  209. package/kb/references/thumbs/1f1419651b.jpg +0 -0
  210. package/kb/references/thumbs/1f2a6da92d.jpg +0 -0
  211. package/kb/references/thumbs/1f651f33c0.jpg +0 -0
  212. package/kb/references/thumbs/1f8b697d18.jpg +0 -0
  213. package/kb/references/thumbs/1fee3824cd.jpg +0 -0
  214. package/kb/references/thumbs/1ff7757853.jpg +0 -0
  215. package/kb/references/thumbs/2043182f01.jpg +0 -0
  216. package/kb/references/thumbs/2059dabcd5.jpg +0 -0
  217. package/kb/references/thumbs/209d9dbb95.jpg +0 -0
  218. package/kb/references/thumbs/20d8923554.jpg +0 -0
  219. package/kb/references/thumbs/210dd3b671.jpg +0 -0
  220. package/kb/references/thumbs/2120719116.jpg +0 -0
  221. package/kb/references/thumbs/21a7384429.jpg +0 -0
  222. package/kb/references/thumbs/225f187391.jpg +0 -0
  223. package/kb/references/thumbs/230caf7dd2.jpg +0 -0
  224. package/kb/references/thumbs/235afad355.jpg +0 -0
  225. package/kb/references/thumbs/2378066687.jpg +0 -0
  226. package/kb/references/thumbs/241faf68a1.jpg +0 -0
  227. package/kb/references/thumbs/24d9f1a9b9.jpg +0 -0
  228. package/kb/references/thumbs/2505c8b9a0.jpg +0 -0
  229. package/kb/references/thumbs/25593638ca.jpg +0 -0
  230. package/kb/references/thumbs/25dd454f18.jpg +0 -0
  231. package/kb/references/thumbs/2602e123e2.jpg +0 -0
  232. package/kb/references/thumbs/26095c0b11.jpg +0 -0
  233. package/kb/references/thumbs/26260d6238.jpg +0 -0
  234. package/kb/references/thumbs/26bba21836.jpg +0 -0
  235. package/kb/references/thumbs/26bde3520b.jpg +0 -0
  236. package/kb/references/thumbs/272e22067c.jpg +0 -0
  237. package/kb/references/thumbs/27421b3226.jpg +0 -0
  238. package/kb/references/thumbs/2769ebca41.jpg +0 -0
  239. package/kb/references/thumbs/27e2f6e8b0.jpg +0 -0
  240. package/kb/references/thumbs/28432ba373.jpg +0 -0
  241. package/kb/references/thumbs/2846626f19.jpg +0 -0
  242. package/kb/references/thumbs/28750e821e.jpg +0 -0
  243. package/kb/references/thumbs/289481230f.jpg +0 -0
  244. package/kb/references/thumbs/2897967408.jpg +0 -0
  245. package/kb/references/thumbs/28c0a5fa17.jpg +0 -0
  246. package/kb/references/thumbs/28c181fba5.jpg +0 -0
  247. package/kb/references/thumbs/29806f539f.jpg +0 -0
  248. package/kb/references/thumbs/2997f56ee7.jpg +0 -0
  249. package/kb/references/thumbs/29b34a10fb.jpg +0 -0
  250. package/kb/references/thumbs/29bd78cdb4.jpg +0 -0
  251. package/kb/references/thumbs/29c8c6c22a.jpg +0 -0
  252. package/kb/references/thumbs/2ac23f7fae.jpg +0 -0
  253. package/kb/references/thumbs/2b76e13f79.jpg +0 -0
  254. package/kb/references/thumbs/2b84226479.jpg +0 -0
  255. package/kb/references/thumbs/2bf2dbf737.jpg +0 -0
  256. package/kb/references/thumbs/2bf89abbf6.jpg +0 -0
  257. package/kb/references/thumbs/2c562cd534.jpg +0 -0
  258. package/kb/references/thumbs/2cfe9d48c4.jpg +0 -0
  259. package/kb/references/thumbs/2d49f19b27.jpg +0 -0
  260. package/kb/references/thumbs/2dc8670da7.jpg +0 -0
  261. package/kb/references/thumbs/2de947c469.jpg +0 -0
  262. package/kb/references/thumbs/2e075ca79f.jpg +0 -0
  263. package/kb/references/thumbs/2eb0820e6b.jpg +0 -0
  264. package/kb/references/thumbs/2ec56a9dc0.jpg +0 -0
  265. package/kb/references/thumbs/30bb4ad607.jpg +0 -0
  266. package/kb/references/thumbs/30d19b8444.jpg +0 -0
  267. package/kb/references/thumbs/3173c71d73.jpg +0 -0
  268. package/kb/references/thumbs/31e59e4518.jpg +0 -0
  269. package/kb/references/thumbs/322b8960e8.jpg +0 -0
  270. package/kb/references/thumbs/325c9be6f4.jpg +0 -0
  271. package/kb/references/thumbs/3273338982.jpg +0 -0
  272. package/kb/references/thumbs/32f8387f38.jpg +0 -0
  273. package/kb/references/thumbs/332f6b74c3.jpg +0 -0
  274. package/kb/references/thumbs/334b146b6a.jpg +0 -0
  275. package/kb/references/thumbs/339abd60e4.jpg +0 -0
  276. package/kb/references/thumbs/33e88ec295.jpg +0 -0
  277. package/kb/references/thumbs/340931b5d4.jpg +0 -0
  278. package/kb/references/thumbs/34165a93bd.jpg +0 -0
  279. package/kb/references/thumbs/34d0575dd7.jpg +0 -0
  280. package/kb/references/thumbs/3549e75f0a.jpg +0 -0
  281. package/kb/references/thumbs/35827f1561.jpg +0 -0
  282. package/kb/references/thumbs/37c50280ec.jpg +0 -0
  283. package/kb/references/thumbs/37e66ecf11.jpg +0 -0
  284. package/kb/references/thumbs/3841a04fdc.jpg +0 -0
  285. package/kb/references/thumbs/38490c088f.jpg +0 -0
  286. package/kb/references/thumbs/3850319e9f.jpg +0 -0
  287. package/kb/references/thumbs/3856a64166.jpg +0 -0
  288. package/kb/references/thumbs/3881f211c2.jpg +0 -0
  289. package/kb/references/thumbs/38c7dde2f3.jpg +0 -0
  290. package/kb/references/thumbs/38ce9e486c.jpg +0 -0
  291. package/kb/references/thumbs/390ea8e01b.jpg +0 -0
  292. package/kb/references/thumbs/391b7fc27c.jpg +0 -0
  293. package/kb/references/thumbs/395f7214dc.jpg +0 -0
  294. package/kb/references/thumbs/39e81a5db2.jpg +0 -0
  295. package/kb/references/thumbs/3a67c8d655.jpg +0 -0
  296. package/kb/references/thumbs/3a89c25e3b.jpg +0 -0
  297. package/kb/references/thumbs/3aa0837542.jpg +0 -0
  298. package/kb/references/thumbs/3aae18aa8a.jpg +0 -0
  299. package/kb/references/thumbs/3bdbe767db.jpg +0 -0
  300. package/kb/references/thumbs/3c0616fc7f.jpg +0 -0
  301. package/kb/references/thumbs/3c18e9db1d.jpg +0 -0
  302. package/kb/references/thumbs/3c5a14076c.jpg +0 -0
  303. package/kb/references/thumbs/3ca9ec15c6.jpg +0 -0
  304. package/kb/references/thumbs/3d7756fd35.jpg +0 -0
  305. package/kb/references/thumbs/3dd45bd33d.jpg +0 -0
  306. package/kb/references/thumbs/3de72d5414.jpg +0 -0
  307. package/kb/references/thumbs/3dede597af.jpg +0 -0
  308. package/kb/references/thumbs/3ea948619e.jpg +0 -0
  309. package/kb/references/thumbs/402c12054c.jpg +0 -0
  310. package/kb/references/thumbs/403ab4af90.jpg +0 -0
  311. package/kb/references/thumbs/40773a8c63.jpg +0 -0
  312. package/kb/references/thumbs/407d9583db.jpg +0 -0
  313. package/kb/references/thumbs/415dc1d84c.jpg +0 -0
  314. package/kb/references/thumbs/420b676a4f.jpg +0 -0
  315. package/kb/references/thumbs/42a1ee85eb.jpg +0 -0
  316. package/kb/references/thumbs/43059221aa.jpg +0 -0
  317. package/kb/references/thumbs/432fa0d22c.jpg +0 -0
  318. package/kb/references/thumbs/43826f20e3.jpg +0 -0
  319. package/kb/references/thumbs/43ac55c98a.jpg +0 -0
  320. package/kb/references/thumbs/43d098f3c3.jpg +0 -0
  321. package/kb/references/thumbs/4449d80c2c.jpg +0 -0
  322. package/kb/references/thumbs/44625727dc.jpg +0 -0
  323. package/kb/references/thumbs/4482bb87a1.jpg +0 -0
  324. package/kb/references/thumbs/44b078acdc.jpg +0 -0
  325. package/kb/references/thumbs/44d0305850.jpg +0 -0
  326. package/kb/references/thumbs/456bb6b59e.jpg +0 -0
  327. package/kb/references/thumbs/45df73ce48.jpg +0 -0
  328. package/kb/references/thumbs/45edb45d87.jpg +0 -0
  329. package/kb/references/thumbs/4671d57cd5.jpg +0 -0
  330. package/kb/references/thumbs/46cc108518.jpg +0 -0
  331. package/kb/references/thumbs/470022e614.jpg +0 -0
  332. package/kb/references/thumbs/4719a669ce.jpg +0 -0
  333. package/kb/references/thumbs/47404f5ad4.jpg +0 -0
  334. package/kb/references/thumbs/479d3eb977.jpg +0 -0
  335. package/kb/references/thumbs/479ed92ceb.jpg +0 -0
  336. package/kb/references/thumbs/47baed50c7.jpg +0 -0
  337. package/kb/references/thumbs/47fdcfd7b0.jpg +0 -0
  338. package/kb/references/thumbs/48fd5e94c5.jpg +0 -0
  339. package/kb/references/thumbs/493807917b.jpg +0 -0
  340. package/kb/references/thumbs/49a1c67293.jpg +0 -0
  341. package/kb/references/thumbs/49e2d7fafe.jpg +0 -0
  342. package/kb/references/thumbs/49f540fcb6.jpg +0 -0
  343. package/kb/references/thumbs/4a67ec4028.jpg +0 -0
  344. package/kb/references/thumbs/4a93275b04.jpg +0 -0
  345. package/kb/references/thumbs/4aae6f52da.jpg +0 -0
  346. package/kb/references/thumbs/4b1bae66bd.jpg +0 -0
  347. package/kb/references/thumbs/4b75da15a9.jpg +0 -0
  348. package/kb/references/thumbs/4b77789b88.jpg +0 -0
  349. package/kb/references/thumbs/4bc2f3f3a4.jpg +0 -0
  350. package/kb/references/thumbs/4c48f27b88.jpg +0 -0
  351. package/kb/references/thumbs/4c52bd8bfb.jpg +0 -0
  352. package/kb/references/thumbs/4c9b4acc60.jpg +0 -0
  353. package/kb/references/thumbs/4cbf0752d4.jpg +0 -0
  354. package/kb/references/thumbs/4cd2672517.jpg +0 -0
  355. package/kb/references/thumbs/4dd22a8181.jpg +0 -0
  356. package/kb/references/thumbs/4ee3447314.jpg +0 -0
  357. package/kb/references/thumbs/4ef29c8144.jpg +0 -0
  358. package/kb/references/thumbs/4f82fadfd6.jpg +0 -0
  359. package/kb/references/thumbs/4fc9b643d8.jpg +0 -0
  360. package/kb/references/thumbs/50549524b0.jpg +0 -0
  361. package/kb/references/thumbs/509a615883.jpg +0 -0
  362. package/kb/references/thumbs/50c03ef855.jpg +0 -0
  363. package/kb/references/thumbs/50cf9acad6.jpg +0 -0
  364. package/kb/references/thumbs/51131b025c.jpg +0 -0
  365. package/kb/references/thumbs/51da1124f0.jpg +0 -0
  366. package/kb/references/thumbs/524d5e7288.jpg +0 -0
  367. package/kb/references/thumbs/5251247c81.jpg +0 -0
  368. package/kb/references/thumbs/526b91b7ec.jpg +0 -0
  369. package/kb/references/thumbs/52f02f6c2e.jpg +0 -0
  370. package/kb/references/thumbs/530ab530dc.jpg +0 -0
  371. package/kb/references/thumbs/5335ee4404.jpg +0 -0
  372. package/kb/references/thumbs/5385b90939.jpg +0 -0
  373. package/kb/references/thumbs/53c54654cc.jpg +0 -0
  374. package/kb/references/thumbs/5420794c6c.jpg +0 -0
  375. package/kb/references/thumbs/542a9dce8b.jpg +0 -0
  376. package/kb/references/thumbs/5571530a5f.jpg +0 -0
  377. package/kb/references/thumbs/56cc68d804.jpg +0 -0
  378. package/kb/references/thumbs/577b7f7c44.jpg +0 -0
  379. package/kb/references/thumbs/57adf8f1bb.jpg +0 -0
  380. package/kb/references/thumbs/57c5f5e3a6.jpg +0 -0
  381. package/kb/references/thumbs/57ffe4e839.jpg +0 -0
  382. package/kb/references/thumbs/5817dfc7d6.jpg +0 -0
  383. package/kb/references/thumbs/581a182a7a.jpg +0 -0
  384. package/kb/references/thumbs/58807679c8.jpg +0 -0
  385. package/kb/references/thumbs/589a1d9111.jpg +0 -0
  386. package/kb/references/thumbs/59167391ab.jpg +0 -0
  387. package/kb/references/thumbs/5a70420166.jpg +0 -0
  388. package/kb/references/thumbs/5b10df1b8d.jpg +0 -0
  389. package/kb/references/thumbs/5b7a712fdb.jpg +0 -0
  390. package/kb/references/thumbs/5bc46b92ad.jpg +0 -0
  391. package/kb/references/thumbs/5c0546a120.jpg +0 -0
  392. package/kb/references/thumbs/5c2b6125a8.jpg +0 -0
  393. package/kb/references/thumbs/5c536b9e42.jpg +0 -0
  394. package/kb/references/thumbs/5c8e441d30.jpg +0 -0
  395. package/kb/references/thumbs/5cb6f1baa5.jpg +0 -0
  396. package/kb/references/thumbs/5ce6c1f21e.jpg +0 -0
  397. package/kb/references/thumbs/5cef052c0a.jpg +0 -0
  398. package/kb/references/thumbs/5d08438209.jpg +0 -0
  399. package/kb/references/thumbs/5d0f9af9b0.jpg +0 -0
  400. package/kb/references/thumbs/5d300ed332.jpg +0 -0
  401. package/kb/references/thumbs/5d708a4794.jpg +0 -0
  402. package/kb/references/thumbs/5de8826a98.jpg +0 -0
  403. package/kb/references/thumbs/5df7bff22c.jpg +0 -0
  404. package/kb/references/thumbs/5e087d0f00.jpg +0 -0
  405. package/kb/references/thumbs/5e44e70ca0.jpg +0 -0
  406. package/kb/references/thumbs/5eff5d80ce.jpg +0 -0
  407. package/kb/references/thumbs/5f015c96b1.jpg +0 -0
  408. package/kb/references/thumbs/5f3289b7c4.jpg +0 -0
  409. package/kb/references/thumbs/5f830c40eb.jpg +0 -0
  410. package/kb/references/thumbs/60c4c3527b.jpg +0 -0
  411. package/kb/references/thumbs/60e9e594dd.jpg +0 -0
  412. package/kb/references/thumbs/615421d7be.jpg +0 -0
  413. package/kb/references/thumbs/6176e4004f.jpg +0 -0
  414. package/kb/references/thumbs/61f0fe5e06.jpg +0 -0
  415. package/kb/references/thumbs/6213785db4.jpg +0 -0
  416. package/kb/references/thumbs/62a670ae09.jpg +0 -0
  417. package/kb/references/thumbs/63cc12981b.jpg +0 -0
  418. package/kb/references/thumbs/641f15cc71.jpg +0 -0
  419. package/kb/references/thumbs/642ab2081d.jpg +0 -0
  420. package/kb/references/thumbs/644a59bf6e.jpg +0 -0
  421. package/kb/references/thumbs/644da8a8e3.jpg +0 -0
  422. package/kb/references/thumbs/6458986897.jpg +0 -0
  423. package/kb/references/thumbs/64cfbd7b93.jpg +0 -0
  424. package/kb/references/thumbs/661a57686a.jpg +0 -0
  425. package/kb/references/thumbs/66355a813a.jpg +0 -0
  426. package/kb/references/thumbs/664396dd44.jpg +0 -0
  427. package/kb/references/thumbs/66d832d65f.jpg +0 -0
  428. package/kb/references/thumbs/66de9e9833.jpg +0 -0
  429. package/kb/references/thumbs/6750964fd9.jpg +0 -0
  430. package/kb/references/thumbs/67c7164832.jpg +0 -0
  431. package/kb/references/thumbs/681600b6cd.jpg +0 -0
  432. package/kb/references/thumbs/682e75afe4.jpg +0 -0
  433. package/kb/references/thumbs/69274bc952.jpg +0 -0
  434. package/kb/references/thumbs/696ec08c52.jpg +0 -0
  435. package/kb/references/thumbs/69760111cc.jpg +0 -0
  436. package/kb/references/thumbs/697e32664f.jpg +0 -0
  437. package/kb/references/thumbs/69df92544b.jpg +0 -0
  438. package/kb/references/thumbs/69e48c5229.jpg +0 -0
  439. package/kb/references/thumbs/6a5fb75de1.jpg +0 -0
  440. package/kb/references/thumbs/6a7389e472.jpg +0 -0
  441. package/kb/references/thumbs/6a9b041e02.jpg +0 -0
  442. package/kb/references/thumbs/6ab2fa93ee.jpg +0 -0
  443. package/kb/references/thumbs/6ae3400319.jpg +0 -0
  444. package/kb/references/thumbs/6b21344bf3.jpg +0 -0
  445. package/kb/references/thumbs/6bd1b67a8a.jpg +0 -0
  446. package/kb/references/thumbs/6c99156c4f.jpg +0 -0
  447. package/kb/references/thumbs/6cc9b65384.jpg +0 -0
  448. package/kb/references/thumbs/6d15830a73.jpg +0 -0
  449. package/kb/references/thumbs/6d4c33d180.jpg +0 -0
  450. package/kb/references/thumbs/6e36815799.jpg +0 -0
  451. package/kb/references/thumbs/6eddb9cbfc.jpg +0 -0
  452. package/kb/references/thumbs/6f97968a7c.jpg +0 -0
  453. package/kb/references/thumbs/6fa0716a7d.jpg +0 -0
  454. package/kb/references/thumbs/6facd57184.jpg +0 -0
  455. package/kb/references/thumbs/6fd57b785a.jpg +0 -0
  456. package/kb/references/thumbs/700b3cb185.jpg +0 -0
  457. package/kb/references/thumbs/71197443fa.jpg +0 -0
  458. package/kb/references/thumbs/72082bc9ee.jpg +0 -0
  459. package/kb/references/thumbs/72453db479.jpg +0 -0
  460. package/kb/references/thumbs/72cd64d500.jpg +0 -0
  461. package/kb/references/thumbs/72d1e75283.jpg +0 -0
  462. package/kb/references/thumbs/72e13f7f5b.jpg +0 -0
  463. package/kb/references/thumbs/7459f8e644.jpg +0 -0
  464. package/kb/references/thumbs/7470d55c79.jpg +0 -0
  465. package/kb/references/thumbs/74c468f02a.jpg +0 -0
  466. package/kb/references/thumbs/74fed6eb77.jpg +0 -0
  467. package/kb/references/thumbs/756e05080d.jpg +0 -0
  468. package/kb/references/thumbs/75df7e5f49.jpg +0 -0
  469. package/kb/references/thumbs/76226da6ef.jpg +0 -0
  470. package/kb/references/thumbs/76a34aa056.jpg +0 -0
  471. package/kb/references/thumbs/76c7076589.jpg +0 -0
  472. package/kb/references/thumbs/76cccc396c.jpg +0 -0
  473. package/kb/references/thumbs/774654789f.jpg +0 -0
  474. package/kb/references/thumbs/7746cc494d.jpg +0 -0
  475. package/kb/references/thumbs/7766e36aa3.jpg +0 -0
  476. package/kb/references/thumbs/78049331ef.jpg +0 -0
  477. package/kb/references/thumbs/78808a0ca5.jpg +0 -0
  478. package/kb/references/thumbs/7890b0a310.jpg +0 -0
  479. package/kb/references/thumbs/78911975e6.jpg +0 -0
  480. package/kb/references/thumbs/78d289d5a0.jpg +0 -0
  481. package/kb/references/thumbs/794126267e.jpg +0 -0
  482. package/kb/references/thumbs/7976eb9025.jpg +0 -0
  483. package/kb/references/thumbs/798519ec7c.jpg +0 -0
  484. package/kb/references/thumbs/7a6cc5eecc.jpg +0 -0
  485. package/kb/references/thumbs/7ac8a7cd4a.jpg +0 -0
  486. package/kb/references/thumbs/7b1341d61d.jpg +0 -0
  487. package/kb/references/thumbs/7b9c4031e3.jpg +0 -0
  488. package/kb/references/thumbs/7ba5ad3779.jpg +0 -0
  489. package/kb/references/thumbs/7be6eb2bd0.jpg +0 -0
  490. package/kb/references/thumbs/7c30864f5d.jpg +0 -0
  491. package/kb/references/thumbs/7c379b5e17.jpg +0 -0
  492. package/kb/references/thumbs/7c4050eb02.jpg +0 -0
  493. package/kb/references/thumbs/7ca47b3221.jpg +0 -0
  494. package/kb/references/thumbs/7d43516bd7.jpg +0 -0
  495. package/kb/references/thumbs/7da9dbb98a.jpg +0 -0
  496. package/kb/references/thumbs/7ddda8f57c.jpg +0 -0
  497. package/kb/references/thumbs/7ece6d215c.jpg +0 -0
  498. package/kb/references/thumbs/7eebf029de.jpg +0 -0
  499. package/kb/references/thumbs/7f45483c15.jpg +0 -0
  500. package/kb/references/thumbs/7fde06eaa5.jpg +0 -0
  501. package/kb/references/thumbs/80407b97e9.jpg +0 -0
  502. package/kb/references/thumbs/80f0a6cf4b.jpg +0 -0
  503. package/kb/references/thumbs/8118b451b8.jpg +0 -0
  504. package/kb/references/thumbs/816baed4a8.jpg +0 -0
  505. package/kb/references/thumbs/817d2d15d9.jpg +0 -0
  506. package/kb/references/thumbs/8260cb430d.jpg +0 -0
  507. package/kb/references/thumbs/827b562899.jpg +0 -0
  508. package/kb/references/thumbs/8293cf1e92.jpg +0 -0
  509. package/kb/references/thumbs/8294653169.jpg +0 -0
  510. package/kb/references/thumbs/83061f871f.jpg +0 -0
  511. package/kb/references/thumbs/833df778ca.jpg +0 -0
  512. package/kb/references/thumbs/83d3acaafc.jpg +0 -0
  513. package/kb/references/thumbs/844c6efeff.jpg +0 -0
  514. package/kb/references/thumbs/84563d482a.jpg +0 -0
  515. package/kb/references/thumbs/848082dd94.jpg +0 -0
  516. package/kb/references/thumbs/8494b1da94.jpg +0 -0
  517. package/kb/references/thumbs/84b71014be.jpg +0 -0
  518. package/kb/references/thumbs/84be4a5f3c.jpg +0 -0
  519. package/kb/references/thumbs/84fd0fd25a.jpg +0 -0
  520. package/kb/references/thumbs/853e488e22.jpg +0 -0
  521. package/kb/references/thumbs/858c91585d.jpg +0 -0
  522. package/kb/references/thumbs/85903e8fe9.jpg +0 -0
  523. package/kb/references/thumbs/86597d43fd.jpg +0 -0
  524. package/kb/references/thumbs/866eb99500.jpg +0 -0
  525. package/kb/references/thumbs/86ac050340.jpg +0 -0
  526. package/kb/references/thumbs/86afec7b64.jpg +0 -0
  527. package/kb/references/thumbs/87238127f5.jpg +0 -0
  528. package/kb/references/thumbs/87321df792.jpg +0 -0
  529. package/kb/references/thumbs/87460f70cd.jpg +0 -0
  530. package/kb/references/thumbs/8770dfeaec.jpg +0 -0
  531. package/kb/references/thumbs/8798451a8c.jpg +0 -0
  532. package/kb/references/thumbs/87da1f34a3.jpg +0 -0
  533. package/kb/references/thumbs/882cdf8c05.jpg +0 -0
  534. package/kb/references/thumbs/8839f4106f.jpg +0 -0
  535. package/kb/references/thumbs/8844455212.jpg +0 -0
  536. package/kb/references/thumbs/88abba0497.jpg +0 -0
  537. package/kb/references/thumbs/88b372435d.jpg +0 -0
  538. package/kb/references/thumbs/88bd4f35fa.jpg +0 -0
  539. package/kb/references/thumbs/88c71a075e.jpg +0 -0
  540. package/kb/references/thumbs/892e5f048e.jpg +0 -0
  541. package/kb/references/thumbs/893cc8e920.jpg +0 -0
  542. package/kb/references/thumbs/896c198814.jpg +0 -0
  543. package/kb/references/thumbs/8a8e22f756.jpg +0 -0
  544. package/kb/references/thumbs/8a9d8a35cb.jpg +0 -0
  545. package/kb/references/thumbs/8ab1d67fec.jpg +0 -0
  546. package/kb/references/thumbs/8ab201fe12.jpg +0 -0
  547. package/kb/references/thumbs/8ac372c102.jpg +0 -0
  548. package/kb/references/thumbs/8b15736d62.jpg +0 -0
  549. package/kb/references/thumbs/8b1d8d6a05.jpg +0 -0
  550. package/kb/references/thumbs/8bb6050e20.jpg +0 -0
  551. package/kb/references/thumbs/8c6c9d1577.jpg +0 -0
  552. package/kb/references/thumbs/8d337851e0.jpg +0 -0
  553. package/kb/references/thumbs/8d44b33cc5.jpg +0 -0
  554. package/kb/references/thumbs/8d4536ff3b.jpg +0 -0
  555. package/kb/references/thumbs/8d9b99454a.jpg +0 -0
  556. package/kb/references/thumbs/8dda2303ff.jpg +0 -0
  557. package/kb/references/thumbs/8dec722d62.jpg +0 -0
  558. package/kb/references/thumbs/8e0ea4ca74.jpg +0 -0
  559. package/kb/references/thumbs/8ebba84d69.jpg +0 -0
  560. package/kb/references/thumbs/8ef09140c0.jpg +0 -0
  561. package/kb/references/thumbs/8f2928a1b0.jpg +0 -0
  562. package/kb/references/thumbs/8f88f9ddba.jpg +0 -0
  563. package/kb/references/thumbs/8fa4451009.jpg +0 -0
  564. package/kb/references/thumbs/90109b0e02.jpg +0 -0
  565. package/kb/references/thumbs/90e854e5c9.jpg +0 -0
  566. package/kb/references/thumbs/914e79f893.jpg +0 -0
  567. package/kb/references/thumbs/9161369197.jpg +0 -0
  568. package/kb/references/thumbs/9165c87d6f.jpg +0 -0
  569. package/kb/references/thumbs/9193f6558a.jpg +0 -0
  570. package/kb/references/thumbs/91ac7df877.jpg +0 -0
  571. package/kb/references/thumbs/9216bd4931.jpg +0 -0
  572. package/kb/references/thumbs/922ae8a26a.jpg +0 -0
  573. package/kb/references/thumbs/92a70e6867.jpg +0 -0
  574. package/kb/references/thumbs/92c77ece22.jpg +0 -0
  575. package/kb/references/thumbs/92e5914fbf.jpg +0 -0
  576. package/kb/references/thumbs/92ee36419a.jpg +0 -0
  577. package/kb/references/thumbs/94153a60b0.jpg +0 -0
  578. package/kb/references/thumbs/9449666640.jpg +0 -0
  579. package/kb/references/thumbs/944be6a646.jpg +0 -0
  580. package/kb/references/thumbs/945831476e.jpg +0 -0
  581. package/kb/references/thumbs/94c1af82f4.jpg +0 -0
  582. package/kb/references/thumbs/9599d6d589.jpg +0 -0
  583. package/kb/references/thumbs/960710f4cf.jpg +0 -0
  584. package/kb/references/thumbs/9608f12d64.jpg +0 -0
  585. package/kb/references/thumbs/962dabfaa0.jpg +0 -0
  586. package/kb/references/thumbs/965a3a95c6.jpg +0 -0
  587. package/kb/references/thumbs/9672ed5ae4.jpg +0 -0
  588. package/kb/references/thumbs/96891ae9c1.jpg +0 -0
  589. package/kb/references/thumbs/96e1907c99.jpg +0 -0
  590. package/kb/references/thumbs/96f25b6578.jpg +0 -0
  591. package/kb/references/thumbs/97211df9ae.jpg +0 -0
  592. package/kb/references/thumbs/9769d763a5.jpg +0 -0
  593. package/kb/references/thumbs/9792bd6d05.jpg +0 -0
  594. package/kb/references/thumbs/97d2837e3e.jpg +0 -0
  595. package/kb/references/thumbs/97d90d1d85.jpg +0 -0
  596. package/kb/references/thumbs/97fcd5774a.jpg +0 -0
  597. package/kb/references/thumbs/980f68564e.jpg +0 -0
  598. package/kb/references/thumbs/98511fb393.jpg +0 -0
  599. package/kb/references/thumbs/98f8fa694d.jpg +0 -0
  600. package/kb/references/thumbs/9a42c6bb97.jpg +0 -0
  601. package/kb/references/thumbs/9ab3600a06.jpg +0 -0
  602. package/kb/references/thumbs/9ab48d9130.jpg +0 -0
  603. package/kb/references/thumbs/9ad519197d.jpg +0 -0
  604. package/kb/references/thumbs/9b1382c657.jpg +0 -0
  605. package/kb/references/thumbs/9b6cc1e077.jpg +0 -0
  606. package/kb/references/thumbs/9beede8480.jpg +0 -0
  607. package/kb/references/thumbs/9bf8976952.jpg +0 -0
  608. package/kb/references/thumbs/9cace7b75a.jpg +0 -0
  609. package/kb/references/thumbs/9cdcc5745e.jpg +0 -0
  610. package/kb/references/thumbs/9d0b269074.jpg +0 -0
  611. package/kb/references/thumbs/9d41788b48.jpg +0 -0
  612. package/kb/references/thumbs/9d475bd6af.jpg +0 -0
  613. package/kb/references/thumbs/9e6bc543d0.jpg +0 -0
  614. package/kb/references/thumbs/9ed0e0a206.jpg +0 -0
  615. package/kb/references/thumbs/9ef538de13.jpg +0 -0
  616. package/kb/references/thumbs/9efc373ab4.jpg +0 -0
  617. package/kb/references/thumbs/9f4a939fa5.jpg +0 -0
  618. package/kb/references/thumbs/9f8d12d7b9.jpg +0 -0
  619. package/kb/references/thumbs/9fd5e3efa7.jpg +0 -0
  620. package/kb/references/thumbs/a0a9b9c572.jpg +0 -0
  621. package/kb/references/thumbs/a29ca9b185.jpg +0 -0
  622. package/kb/references/thumbs/a2a42884af.jpg +0 -0
  623. package/kb/references/thumbs/a2f5064e34.jpg +0 -0
  624. package/kb/references/thumbs/a3112ce672.jpg +0 -0
  625. package/kb/references/thumbs/a3437736d1.jpg +0 -0
  626. package/kb/references/thumbs/a372667e76.jpg +0 -0
  627. package/kb/references/thumbs/a39bae94e9.jpg +0 -0
  628. package/kb/references/thumbs/a3fc75d94c.jpg +0 -0
  629. package/kb/references/thumbs/a440487631.jpg +0 -0
  630. package/kb/references/thumbs/a4993d5fb5.jpg +0 -0
  631. package/kb/references/thumbs/a500476a86.jpg +0 -0
  632. package/kb/references/thumbs/a55506bf46.jpg +0 -0
  633. package/kb/references/thumbs/a59a5f9843.jpg +0 -0
  634. package/kb/references/thumbs/a5bce0eede.jpg +0 -0
  635. package/kb/references/thumbs/a5d921e1f0.jpg +0 -0
  636. package/kb/references/thumbs/a5e19d4637.jpg +0 -0
  637. package/kb/references/thumbs/a5f92b97d5.jpg +0 -0
  638. package/kb/references/thumbs/a6562c210a.jpg +0 -0
  639. package/kb/references/thumbs/a6f61a8d09.jpg +0 -0
  640. package/kb/references/thumbs/a719fdf663.jpg +0 -0
  641. package/kb/references/thumbs/a724e5442f.jpg +0 -0
  642. package/kb/references/thumbs/a852b4319f.jpg +0 -0
  643. package/kb/references/thumbs/a896c08448.jpg +0 -0
  644. package/kb/references/thumbs/a8d190a4b8.jpg +0 -0
  645. package/kb/references/thumbs/a8dcdd57d7.jpg +0 -0
  646. package/kb/references/thumbs/a951347e18.jpg +0 -0
  647. package/kb/references/thumbs/a9a1a0bf7d.jpg +0 -0
  648. package/kb/references/thumbs/a9b72986a8.jpg +0 -0
  649. package/kb/references/thumbs/aa11555ba8.jpg +0 -0
  650. package/kb/references/thumbs/aa4aab9093.jpg +0 -0
  651. package/kb/references/thumbs/aa89b96658.jpg +0 -0
  652. package/kb/references/thumbs/aaeb3739b6.jpg +0 -0
  653. package/kb/references/thumbs/ab25a608ec.jpg +0 -0
  654. package/kb/references/thumbs/ab34b62268.jpg +0 -0
  655. package/kb/references/thumbs/ab3b0217bb.jpg +0 -0
  656. package/kb/references/thumbs/ac70c04afb.jpg +0 -0
  657. package/kb/references/thumbs/ace0879117.jpg +0 -0
  658. package/kb/references/thumbs/ad0515ae55.jpg +0 -0
  659. package/kb/references/thumbs/ad66b25b47.jpg +0 -0
  660. package/kb/references/thumbs/ad68229d2f.jpg +0 -0
  661. package/kb/references/thumbs/add6480693.jpg +0 -0
  662. package/kb/references/thumbs/ae5b8c3b8c.jpg +0 -0
  663. package/kb/references/thumbs/ae81247b47.jpg +0 -0
  664. package/kb/references/thumbs/af8aa54379.jpg +0 -0
  665. package/kb/references/thumbs/afa80dedd4.jpg +0 -0
  666. package/kb/references/thumbs/afe04d730f.jpg +0 -0
  667. package/kb/references/thumbs/b09a7dc315.jpg +0 -0
  668. package/kb/references/thumbs/b0c02629f6.jpg +0 -0
  669. package/kb/references/thumbs/b1cb4f91de.jpg +0 -0
  670. package/kb/references/thumbs/b1d4c13c92.jpg +0 -0
  671. package/kb/references/thumbs/b20eef86db.jpg +0 -0
  672. package/kb/references/thumbs/b3136ccdde.jpg +0 -0
  673. package/kb/references/thumbs/b3f5e8d7dd.jpg +0 -0
  674. package/kb/references/thumbs/b44adb81c9.jpg +0 -0
  675. package/kb/references/thumbs/b457a5894b.jpg +0 -0
  676. package/kb/references/thumbs/b461281e0f.jpg +0 -0
  677. package/kb/references/thumbs/b4942644f0.jpg +0 -0
  678. package/kb/references/thumbs/b49f8eccad.jpg +0 -0
  679. package/kb/references/thumbs/b56c69be23.jpg +0 -0
  680. package/kb/references/thumbs/b59786b33c.jpg +0 -0
  681. package/kb/references/thumbs/b5d368ce5a.jpg +0 -0
  682. package/kb/references/thumbs/b5eb442406.jpg +0 -0
  683. package/kb/references/thumbs/b7fdabf4ab.jpg +0 -0
  684. package/kb/references/thumbs/b88ee8d235.jpg +0 -0
  685. package/kb/references/thumbs/b8a4e5be8a.jpg +0 -0
  686. package/kb/references/thumbs/b8c85b661c.jpg +0 -0
  687. package/kb/references/thumbs/ba1b8e7435.jpg +0 -0
  688. package/kb/references/thumbs/ba9a593eed.jpg +0 -0
  689. package/kb/references/thumbs/baa7afc201.jpg +0 -0
  690. package/kb/references/thumbs/baf74398f7.jpg +0 -0
  691. package/kb/references/thumbs/bb3e947c10.jpg +0 -0
  692. package/kb/references/thumbs/bb4c1bb7c1.jpg +0 -0
  693. package/kb/references/thumbs/bbaa25cc8a.jpg +0 -0
  694. package/kb/references/thumbs/bc7e7f88cc.jpg +0 -0
  695. package/kb/references/thumbs/bc9d5b58f5.jpg +0 -0
  696. package/kb/references/thumbs/bcc99a51f7.jpg +0 -0
  697. package/kb/references/thumbs/bce13dd1c1.jpg +0 -0
  698. package/kb/references/thumbs/bd079a6130.jpg +0 -0
  699. package/kb/references/thumbs/bd2d70dda7.jpg +0 -0
  700. package/kb/references/thumbs/bd5895c5f2.jpg +0 -0
  701. package/kb/references/thumbs/bde8e31d62.jpg +0 -0
  702. package/kb/references/thumbs/be084e78f3.jpg +0 -0
  703. package/kb/references/thumbs/be94f170c8.jpg +0 -0
  704. package/kb/references/thumbs/bed33654f7.jpg +0 -0
  705. package/kb/references/thumbs/beeb9a0c18.jpg +0 -0
  706. package/kb/references/thumbs/bef42094dc.jpg +0 -0
  707. package/kb/references/thumbs/bf625496fa.jpg +0 -0
  708. package/kb/references/thumbs/bf771f1398.jpg +0 -0
  709. package/kb/references/thumbs/c0ddefa3d9.jpg +0 -0
  710. package/kb/references/thumbs/c0f01e536e.jpg +0 -0
  711. package/kb/references/thumbs/c1d592b678.jpg +0 -0
  712. package/kb/references/thumbs/c1e83e1b3e.jpg +0 -0
  713. package/kb/references/thumbs/c2168f8096.jpg +0 -0
  714. package/kb/references/thumbs/c249cd6b17.jpg +0 -0
  715. package/kb/references/thumbs/c2e3d4feed.jpg +0 -0
  716. package/kb/references/thumbs/c3cec01df1.jpg +0 -0
  717. package/kb/references/thumbs/c47bedb42d.jpg +0 -0
  718. package/kb/references/thumbs/c4b03b4680.jpg +0 -0
  719. package/kb/references/thumbs/c4e6d18ec3.jpg +0 -0
  720. package/kb/references/thumbs/c5477e2877.jpg +0 -0
  721. package/kb/references/thumbs/c566794674.jpg +0 -0
  722. package/kb/references/thumbs/c5b945afbb.jpg +0 -0
  723. package/kb/references/thumbs/c5e4659497.jpg +0 -0
  724. package/kb/references/thumbs/c622786702.jpg +0 -0
  725. package/kb/references/thumbs/c6250649ca.jpg +0 -0
  726. package/kb/references/thumbs/c6261a9526.jpg +0 -0
  727. package/kb/references/thumbs/c62e609076.jpg +0 -0
  728. package/kb/references/thumbs/c6602d3e85.jpg +0 -0
  729. package/kb/references/thumbs/c6c1a16e40.jpg +0 -0
  730. package/kb/references/thumbs/c77f932157.jpg +0 -0
  731. package/kb/references/thumbs/c79788f01e.jpg +0 -0
  732. package/kb/references/thumbs/c7a511ad1e.jpg +0 -0
  733. package/kb/references/thumbs/c7acfbc3c8.jpg +0 -0
  734. package/kb/references/thumbs/c7bb6d1bb8.jpg +0 -0
  735. package/kb/references/thumbs/c829eadcb9.jpg +0 -0
  736. package/kb/references/thumbs/c891fd9d85.jpg +0 -0
  737. package/kb/references/thumbs/c89f812ee3.jpg +0 -0
  738. package/kb/references/thumbs/c8a85b9f61.jpg +0 -0
  739. package/kb/references/thumbs/c8fdf85c9a.jpg +0 -0
  740. package/kb/references/thumbs/c961ac1031.jpg +0 -0
  741. package/kb/references/thumbs/c9fd3f88d8.jpg +0 -0
  742. package/kb/references/thumbs/ca0d6399c3.jpg +0 -0
  743. package/kb/references/thumbs/ca316c83e4.jpg +0 -0
  744. package/kb/references/thumbs/ca3b93aa0d.jpg +0 -0
  745. package/kb/references/thumbs/cb0596a85b.jpg +0 -0
  746. package/kb/references/thumbs/cb95a5c3ef.jpg +0 -0
  747. package/kb/references/thumbs/cbe238b656.jpg +0 -0
  748. package/kb/references/thumbs/cbf530e608.jpg +0 -0
  749. package/kb/references/thumbs/cc173494ec.jpg +0 -0
  750. package/kb/references/thumbs/ccb664bddf.jpg +0 -0
  751. package/kb/references/thumbs/ccf69d36cd.jpg +0 -0
  752. package/kb/references/thumbs/ccf759db42.jpg +0 -0
  753. package/kb/references/thumbs/cd2a285807.jpg +0 -0
  754. package/kb/references/thumbs/cd4a24a8ca.jpg +0 -0
  755. package/kb/references/thumbs/cdb3fe8938.jpg +0 -0
  756. package/kb/references/thumbs/cdce1b9bc6.jpg +0 -0
  757. package/kb/references/thumbs/cde3aa761a.jpg +0 -0
  758. package/kb/references/thumbs/ce9d5cd9c2.jpg +0 -0
  759. package/kb/references/thumbs/ceb01f8a34.jpg +0 -0
  760. package/kb/references/thumbs/cee15c98d0.jpg +0 -0
  761. package/kb/references/thumbs/cf6944ddae.jpg +0 -0
  762. package/kb/references/thumbs/d15dd21cd8.jpg +0 -0
  763. package/kb/references/thumbs/d1633bf2ed.jpg +0 -0
  764. package/kb/references/thumbs/d16a838000.jpg +0 -0
  765. package/kb/references/thumbs/d1cf87e9f0.jpg +0 -0
  766. package/kb/references/thumbs/d32511d913.jpg +0 -0
  767. package/kb/references/thumbs/d327e9dedd.jpg +0 -0
  768. package/kb/references/thumbs/d35bf6c85e.jpg +0 -0
  769. package/kb/references/thumbs/d37afc9d61.jpg +0 -0
  770. package/kb/references/thumbs/d3b66655c7.jpg +0 -0
  771. package/kb/references/thumbs/d40b4172b6.jpg +0 -0
  772. package/kb/references/thumbs/d4c40d1824.jpg +0 -0
  773. package/kb/references/thumbs/d4cb55a4b6.jpg +0 -0
  774. package/kb/references/thumbs/d4f0c30f7e.jpg +0 -0
  775. package/kb/references/thumbs/d51cce3ae5.jpg +0 -0
  776. package/kb/references/thumbs/d52948f9ba.jpg +0 -0
  777. package/kb/references/thumbs/d62bd3cba4.jpg +0 -0
  778. package/kb/references/thumbs/d668a21c92.jpg +0 -0
  779. package/kb/references/thumbs/d685072c52.jpg +0 -0
  780. package/kb/references/thumbs/d6d7b96093.jpg +0 -0
  781. package/kb/references/thumbs/d7cd874b21.jpg +0 -0
  782. package/kb/references/thumbs/d7e111736a.jpg +0 -0
  783. package/kb/references/thumbs/d88325f832.jpg +0 -0
  784. package/kb/references/thumbs/d9272ded76.jpg +0 -0
  785. package/kb/references/thumbs/d962e7c654.jpg +0 -0
  786. package/kb/references/thumbs/d97f000ee2.jpg +0 -0
  787. package/kb/references/thumbs/d9c1ae2543.jpg +0 -0
  788. package/kb/references/thumbs/d9ccb3cac8.jpg +0 -0
  789. package/kb/references/thumbs/da99019680.jpg +0 -0
  790. package/kb/references/thumbs/da9b8bc615.jpg +0 -0
  791. package/kb/references/thumbs/daafbee2c1.jpg +0 -0
  792. package/kb/references/thumbs/db0f11f143.jpg +0 -0
  793. package/kb/references/thumbs/db5dd367f5.jpg +0 -0
  794. package/kb/references/thumbs/db69677866.jpg +0 -0
  795. package/kb/references/thumbs/db87c1956e.jpg +0 -0
  796. package/kb/references/thumbs/dbb86d8bda.jpg +0 -0
  797. package/kb/references/thumbs/dbd3a8ff2a.jpg +0 -0
  798. package/kb/references/thumbs/dbdf4b2794.jpg +0 -0
  799. package/kb/references/thumbs/dd4c19b4b1.jpg +0 -0
  800. package/kb/references/thumbs/dd98d9f6fa.jpg +0 -0
  801. package/kb/references/thumbs/ddb00e3b6a.jpg +0 -0
  802. package/kb/references/thumbs/dde3541a5c.jpg +0 -0
  803. package/kb/references/thumbs/de2a4da502.jpg +0 -0
  804. package/kb/references/thumbs/de4c387a66.jpg +0 -0
  805. package/kb/references/thumbs/de5618fa82.jpg +0 -0
  806. package/kb/references/thumbs/de6bec2538.jpg +0 -0
  807. package/kb/references/thumbs/dea13cbb1f.jpg +0 -0
  808. package/kb/references/thumbs/deff9202aa.jpg +0 -0
  809. package/kb/references/thumbs/df12835f8d.jpg +0 -0
  810. package/kb/references/thumbs/df57acbb3d.jpg +0 -0
  811. package/kb/references/thumbs/df8265009e.jpg +0 -0
  812. package/kb/references/thumbs/dfc99a1e8e.jpg +0 -0
  813. package/kb/references/thumbs/dfc9fde460.jpg +0 -0
  814. package/kb/references/thumbs/e038348eee.jpg +0 -0
  815. package/kb/references/thumbs/e04c2ae1d5.jpg +0 -0
  816. package/kb/references/thumbs/e070345c43.jpg +0 -0
  817. package/kb/references/thumbs/e0a0c84d47.jpg +0 -0
  818. package/kb/references/thumbs/e0af1e6b9e.jpg +0 -0
  819. package/kb/references/thumbs/e1025942c4.jpg +0 -0
  820. package/kb/references/thumbs/e11b6f5b82.jpg +0 -0
  821. package/kb/references/thumbs/e1553de3fc.jpg +0 -0
  822. package/kb/references/thumbs/e162ebe7cd.jpg +0 -0
  823. package/kb/references/thumbs/e1f1e531d8.jpg +0 -0
  824. package/kb/references/thumbs/e20842e5c9.jpg +0 -0
  825. package/kb/references/thumbs/e24e7d949b.jpg +0 -0
  826. package/kb/references/thumbs/e2a3e5d10a.jpg +0 -0
  827. package/kb/references/thumbs/e2de02615a.jpg +0 -0
  828. package/kb/references/thumbs/e305dad7dd.jpg +0 -0
  829. package/kb/references/thumbs/e52e3ef97f.jpg +0 -0
  830. package/kb/references/thumbs/e570e92d06.jpg +0 -0
  831. package/kb/references/thumbs/e583932fb9.jpg +0 -0
  832. package/kb/references/thumbs/e5da3c038f.jpg +0 -0
  833. package/kb/references/thumbs/e657634af3.jpg +0 -0
  834. package/kb/references/thumbs/e67946e3e1.jpg +0 -0
  835. package/kb/references/thumbs/e69e0f3639.jpg +0 -0
  836. package/kb/references/thumbs/e6ebe66031.jpg +0 -0
  837. package/kb/references/thumbs/e7db9563c9.jpg +0 -0
  838. package/kb/references/thumbs/e84a4c3b7b.jpg +0 -0
  839. package/kb/references/thumbs/e84cf52c27.jpg +0 -0
  840. package/kb/references/thumbs/e8707cf6e8.jpg +0 -0
  841. package/kb/references/thumbs/e888890b40.jpg +0 -0
  842. package/kb/references/thumbs/e8a502b418.jpg +0 -0
  843. package/kb/references/thumbs/e8ba68371d.jpg +0 -0
  844. package/kb/references/thumbs/e9098f54cc.jpg +0 -0
  845. package/kb/references/thumbs/e9144deae9.jpg +0 -0
  846. package/kb/references/thumbs/e93e98d757.jpg +0 -0
  847. package/kb/references/thumbs/e9762dd435.jpg +0 -0
  848. package/kb/references/thumbs/e9db4544c8.jpg +0 -0
  849. package/kb/references/thumbs/e9ddffeacd.jpg +0 -0
  850. package/kb/references/thumbs/e9e2cf1d7e.jpg +0 -0
  851. package/kb/references/thumbs/ea1e23e623.jpg +0 -0
  852. package/kb/references/thumbs/ea41200f5e.jpg +0 -0
  853. package/kb/references/thumbs/eaf4916e1a.jpg +0 -0
  854. package/kb/references/thumbs/eb2c505fc8.jpg +0 -0
  855. package/kb/references/thumbs/eb47943693.jpg +0 -0
  856. package/kb/references/thumbs/eb4ffd6625.jpg +0 -0
  857. package/kb/references/thumbs/eb82ac0a4d.jpg +0 -0
  858. package/kb/references/thumbs/ebeb345ade.jpg +0 -0
  859. package/kb/references/thumbs/ec4e2aba0a.jpg +0 -0
  860. package/kb/references/thumbs/ed683bc9f7.jpg +0 -0
  861. package/kb/references/thumbs/ed9e236a84.jpg +0 -0
  862. package/kb/references/thumbs/ee45e1caea.jpg +0 -0
  863. package/kb/references/thumbs/ee50bbf2e7.jpg +0 -0
  864. package/kb/references/thumbs/eed6ad3b7b.jpg +0 -0
  865. package/kb/references/thumbs/eee247fd70.jpg +0 -0
  866. package/kb/references/thumbs/ef87b6cb99.jpg +0 -0
  867. package/kb/references/thumbs/ef87dccf04.jpg +0 -0
  868. package/kb/references/thumbs/ef941bba1e.jpg +0 -0
  869. package/kb/references/thumbs/effc14fb2c.jpg +0 -0
  870. package/kb/references/thumbs/f04aa5a191.jpg +0 -0
  871. package/kb/references/thumbs/f13f12485f.jpg +0 -0
  872. package/kb/references/thumbs/f16a5c0fd4.jpg +0 -0
  873. package/kb/references/thumbs/f1ba7a7a8f.jpg +0 -0
  874. package/kb/references/thumbs/f1bc4c9c19.jpg +0 -0
  875. package/kb/references/thumbs/f1ee827878.jpg +0 -0
  876. package/kb/references/thumbs/f21736aeff.jpg +0 -0
  877. package/kb/references/thumbs/f22bc41213.jpg +0 -0
  878. package/kb/references/thumbs/f266aaffcc.jpg +0 -0
  879. package/kb/references/thumbs/f2c0ed0d65.jpg +0 -0
  880. package/kb/references/thumbs/f32ac710fd.jpg +0 -0
  881. package/kb/references/thumbs/f32b697ff6.jpg +0 -0
  882. package/kb/references/thumbs/f39c883904.jpg +0 -0
  883. package/kb/references/thumbs/f3b9d55e6e.jpg +0 -0
  884. package/kb/references/thumbs/f3c1763e14.jpg +0 -0
  885. package/kb/references/thumbs/f40853fd43.jpg +0 -0
  886. package/kb/references/thumbs/f4fd07dc4d.jpg +0 -0
  887. package/kb/references/thumbs/f50b756ee5.jpg +0 -0
  888. package/kb/references/thumbs/f51272e9b3.jpg +0 -0
  889. package/kb/references/thumbs/f538895eee.jpg +0 -0
  890. package/kb/references/thumbs/f5d28081b5.jpg +0 -0
  891. package/kb/references/thumbs/f6046c9501.jpg +0 -0
  892. package/kb/references/thumbs/f62e4af316.jpg +0 -0
  893. package/kb/references/thumbs/f6f6bffeb8.jpg +0 -0
  894. package/kb/references/thumbs/f736e00af6.jpg +0 -0
  895. package/kb/references/thumbs/f7932da47d.jpg +0 -0
  896. package/kb/references/thumbs/f879e118ca.jpg +0 -0
  897. package/kb/references/thumbs/f88257215d.jpg +0 -0
  898. package/kb/references/thumbs/f88c45a6d7.jpg +0 -0
  899. package/kb/references/thumbs/f89a7b646e.jpg +0 -0
  900. package/kb/references/thumbs/f8a1e7a274.jpg +0 -0
  901. package/kb/references/thumbs/f9183cf2b6.jpg +0 -0
  902. package/kb/references/thumbs/f995de5ce9.jpg +0 -0
  903. package/kb/references/thumbs/f9ada2c3e1.jpg +0 -0
  904. package/kb/references/thumbs/f9d4616f89.jpg +0 -0
  905. package/kb/references/thumbs/f9f4307bb0.jpg +0 -0
  906. package/kb/references/thumbs/fa070122c7.jpg +0 -0
  907. package/kb/references/thumbs/fa29cc1369.jpg +0 -0
  908. package/kb/references/thumbs/fa791a4dab.jpg +0 -0
  909. package/kb/references/thumbs/fa8060509e.jpg +0 -0
  910. package/kb/references/thumbs/fafeed4003.jpg +0 -0
  911. package/kb/references/thumbs/fb1c024f38.jpg +0 -0
  912. package/kb/references/thumbs/fb77aa28b3.jpg +0 -0
  913. package/kb/references/thumbs/fb9f7f1966.jpg +0 -0
  914. package/kb/references/thumbs/fba8a8c2da.jpg +0 -0
  915. package/kb/references/thumbs/fce2754c36.jpg +0 -0
  916. package/kb/references/thumbs/fce2fb83df.jpg +0 -0
  917. package/kb/references/thumbs/fd4c51bb47.jpg +0 -0
  918. package/kb/references/thumbs/fd8c269817.jpg +0 -0
  919. package/kb/references/thumbs/fdb7b250ca.jpg +0 -0
  920. package/kb/references/thumbs/fe18088de8.jpg +0 -0
  921. package/kb/references/thumbs/fe4f742ba7.jpg +0 -0
  922. package/kb/references/thumbs/fe64742ce7.jpg +0 -0
  923. package/kb/references/thumbs/feb5d62161.jpg +0 -0
  924. package/kb/references/thumbs/fed21b3e98.jpg +0 -0
  925. package/kb/references/thumbs/fed2205951.jpg +0 -0
  926. package/kb/references/thumbs/ffdacf1715.jpg +0 -0
  927. package/kb/sections/_buttons.md +93 -0
  928. package/kb/sections/_finish.md +325 -0
  929. package/kb/sections/about-founder-note.md +85 -0
  930. package/kb/sections/about-team-roster.md +86 -0
  931. package/kb/sections/alternating-rows-live-tiles.md +110 -0
  932. package/kb/sections/annotated-product-shot.md +105 -0
  933. package/kb/sections/asymmetric-bento-dominant.md +114 -0
  934. package/kb/sections/before-after-features.md +104 -0
  935. package/kb/sections/before-after-repair.md +107 -0
  936. package/kb/sections/bento-lead-tile.md +112 -0
  937. package/kb/sections/book-a-slot-honest.md +116 -0
  938. package/kb/sections/capability-prompt-ledger.md +68 -0
  939. package/kb/sections/case-study-band.md +113 -0
  940. package/kb/sections/centred-stack-hero.md +107 -0
  941. package/kb/sections/closing-demo-final-state.md +81 -0
  942. package/kb/sections/closing-full-bleed-line-and-form.md +86 -0
  943. package/kb/sections/closing-result-previewed.md +88 -0
  944. package/kb/sections/closing-three-real-steps.md +87 -0
  945. package/kb/sections/command-reference-table.md +70 -0
  946. package/kb/sections/comparison-old-way-bars.md +88 -0
  947. package/kb/sections/comparison-same-task-two-ways.md +88 -0
  948. package/kb/sections/day-of-the-place.md +102 -0
  949. package/kb/sections/docs-reading-rail.md +88 -0
  950. package/kb/sections/editorial-columns-hero.md +107 -0
  951. package/kb/sections/editorial-essay-pull-quote.md +112 -0
  952. package/kb/sections/events-calendar-list.md +107 -0
  953. package/kb/sections/explorable-control-demo.md +102 -0
  954. package/kb/sections/faq-open-answers-editorial.md +85 -0
  955. package/kb/sections/faq-ruled-accordion-aside.md +87 -0
  956. package/kb/sections/footer-closing-scene.md +84 -0
  957. package/kb/sections/footer-editorial-sitemap.md +85 -0
  958. package/kb/sections/footer-minimal-credits.md +80 -0
  959. package/kb/sections/full-bleed-image-band.md +103 -0
  960. package/kb/sections/full-bleed-image-hero.md +105 -0
  961. package/kb/sections/gallery-first-hero.md +109 -0
  962. package/kb/sections/horizontal-scroll-strip.md +101 -0
  963. package/kb/sections/how-i-work-own-voice.md +111 -0
  964. package/kb/sections/how-it-works-in-place.md +109 -0
  965. package/kb/sections/impact-numbers-sourced.md +109 -0
  966. package/kb/sections/install-steps-per-agent.md +77 -0
  967. package/kb/sections/masonry-real-work.md +109 -0
  968. package/kb/sections/numbered-editorial-columns.md +105 -0
  969. package/kb/sections/object-first-hero.md +106 -0
  970. package/kb/sections/object-timeline.md +105 -0
  971. package/kb/sections/one-huge-number.md +100 -0
  972. package/kb/sections/one-person-story.md +103 -0
  973. package/kb/sections/order-ahead-ticket.md +119 -0
  974. package/kb/sections/press-strip-quoted.md +106 -0
  975. package/kb/sections/pricing-comparison-table-sticky.md +94 -0
  976. package/kb/sections/pricing-free-pro-upgrade-moment.md +85 -0
  977. package/kb/sections/pricing-plan-beside-product.md +88 -0
  978. package/kb/sections/pricing-plans-ledger.md +94 -0
  979. package/kb/sections/pricing-usage-calculator.md +93 -0
  980. package/kb/sections/printed-menu-card.md +110 -0
  981. package/kb/sections/process-happening.md +102 -0
  982. package/kb/sections/project-index-ruled.md +106 -0
  983. package/kb/sections/proof-logo-cloud-with-reasons.md +82 -0
  984. package/kb/sections/proof-measured-numbers.md +88 -0
  985. package/kb/sections/proof-output-wall.md +84 -0
  986. package/kb/sections/proof-single-honest-quote.md +81 -0
  987. package/kb/sections/resources-searchable-list.md +110 -0
  988. package/kb/sections/rooms-and-rates.md +107 -0
  989. package/kb/sections/ruled-facts-table.md +108 -0
  990. package/kb/sections/ruled-list-pinned-visual.md +107 -0
  991. package/kb/sections/scroll-scrubbed-product-demo.md +103 -0
  992. package/kb/sections/self-playing-run-demo.md +104 -0
  993. package/kb/sections/services-price-ledger.md +106 -0
  994. package/kb/sections/signup-form-with-next-steps.md +86 -0
  995. package/kb/sections/signup-waitlist-place-in-line.md +90 -0
  996. package/kb/sections/spec-sheet-features.md +108 -0
  997. package/kb/sections/split-hero-live-object.md +108 -0
  998. package/kb/sections/sticky-feature-narrative.md +104 -0
  999. package/kb/sections/todays-board-hero.md +112 -0
  1000. package/kb/sections/touchable-tool-hero.md +111 -0
  1001. package/kb/sections/typographic-statement-hero.md +99 -0
  1002. package/kb/sections/visit-hours-map.md +110 -0
  1003. package/kb/sections/ways-to-help-honest.md +115 -0
  1004. package/kb/sources/design-reference.md +991 -0
  1005. package/kb/sources/product-and-docs.md +37 -0
  1006. package/kb/systems/1win/DESIGN.md +463 -0
  1007. package/kb/systems/LICENSE-awesome-design-md +21 -0
  1008. package/kb/systems/README.md +7 -0
  1009. package/kb/systems/airbnb/DESIGN.md +545 -0
  1010. package/kb/systems/airtable/DESIGN.md +554 -0
  1011. package/kb/systems/aljazeera/DESIGN.md +594 -0
  1012. package/kb/systems/apple/DESIGN.md +562 -0
  1013. package/kb/systems/binance/DESIGN.md +634 -0
  1014. package/kb/systems/bmw/DESIGN.md +544 -0
  1015. package/kb/systems/bmw-m/DESIGN.md +503 -0
  1016. package/kb/systems/bugatti/DESIGN.md +454 -0
  1017. package/kb/systems/cal/DESIGN.md +542 -0
  1018. package/kb/systems/categories.json +87 -0
  1019. package/kb/systems/claude/DESIGN.md +589 -0
  1020. package/kb/systems/clay/DESIGN.md +541 -0
  1021. package/kb/systems/clickhouse/DESIGN.md +544 -0
  1022. package/kb/systems/cohere/DESIGN.md +451 -0
  1023. package/kb/systems/coinbase/DESIGN.md +570 -0
  1024. package/kb/systems/composio/DESIGN.md +506 -0
  1025. package/kb/systems/cursor/DESIGN.md +537 -0
  1026. package/kb/systems/dell-1996/DESIGN.md +632 -0
  1027. package/kb/systems/dji/DESIGN.md +882 -0
  1028. package/kb/systems/elevenlabs/DESIGN.md +504 -0
  1029. package/kb/systems/expo/DESIGN.md +526 -0
  1030. package/kb/systems/ferrari/DESIGN.md +531 -0
  1031. package/kb/systems/figma/DESIGN.md +578 -0
  1032. package/kb/systems/framer/DESIGN.md +544 -0
  1033. package/kb/systems/hashicorp/DESIGN.md +575 -0
  1034. package/kb/systems/hp/DESIGN.md +670 -0
  1035. package/kb/systems/ibm/DESIGN.md +550 -0
  1036. package/kb/systems/intercom/DESIGN.md +546 -0
  1037. package/kb/systems/jobright/DESIGN.md +525 -0
  1038. package/kb/systems/kraken/DESIGN.md +125 -0
  1039. package/kb/systems/lamborghini/DESIGN.md +288 -0
  1040. package/kb/systems/linear.app/DESIGN.md +548 -0
  1041. package/kb/systems/lovable/DESIGN.md +298 -0
  1042. package/kb/systems/mastercard/DESIGN.md +365 -0
  1043. package/kb/systems/mercury/DESIGN.md +568 -0
  1044. package/kb/systems/meta/DESIGN.md +683 -0
  1045. package/kb/systems/minimax/DESIGN.md +746 -0
  1046. package/kb/systems/mintlify/DESIGN.md +852 -0
  1047. package/kb/systems/miro/DESIGN.md +825 -0
  1048. package/kb/systems/mistral.ai/DESIGN.md +773 -0
  1049. package/kb/systems/mongodb/DESIGN.md +767 -0
  1050. package/kb/systems/mydealz/DESIGN.md +551 -0
  1051. package/kb/systems/nerdwallet/DESIGN.md +722 -0
  1052. package/kb/systems/nike/DESIGN.md +575 -0
  1053. package/kb/systems/nintendo-2001/DESIGN.md +649 -0
  1054. package/kb/systems/notion/DESIGN.md +821 -0
  1055. package/kb/systems/nvidia/DESIGN.md +640 -0
  1056. package/kb/systems/ollama/DESIGN.md +539 -0
  1057. package/kb/systems/opencode.ai/DESIGN.md +521 -0
  1058. package/kb/systems/oracle/DESIGN.md +778 -0
  1059. package/kb/systems/oura/DESIGN.md +616 -0
  1060. package/kb/systems/pinterest/DESIGN.md +597 -0
  1061. package/kb/systems/playstation/DESIGN.md +661 -0
  1062. package/kb/systems/posthog/DESIGN.md +690 -0
  1063. package/kb/systems/raycast/DESIGN.md +669 -0
  1064. package/kb/systems/renault/DESIGN.md +589 -0
  1065. package/kb/systems/replicate/DESIGN.md +616 -0
  1066. package/kb/systems/resend/DESIGN.md +585 -0
  1067. package/kb/systems/revolut/DESIGN.md +636 -0
  1068. package/kb/systems/runwayml/DESIGN.md +244 -0
  1069. package/kb/systems/sanity/DESIGN.md +357 -0
  1070. package/kb/systems/sap/DESIGN.md +639 -0
  1071. package/kb/systems/sentry/DESIGN.md +551 -0
  1072. package/kb/systems/shopify/DESIGN.md +516 -0
  1073. package/kb/systems/slack/DESIGN.md +482 -0
  1074. package/kb/systems/spacex/DESIGN.md +363 -0
  1075. package/kb/systems/spotify/DESIGN.md +246 -0
  1076. package/kb/systems/starbucks/DESIGN.md +580 -0
  1077. package/kb/systems/stripe/DESIGN.md +487 -0
  1078. package/kb/systems/supabase/DESIGN.md +462 -0
  1079. package/kb/systems/superhuman/DESIGN.md +448 -0
  1080. package/kb/systems/tesla/DESIGN.md +286 -0
  1081. package/kb/systems/theverge/DESIGN.md +339 -0
  1082. package/kb/systems/together.ai/DESIGN.md +633 -0
  1083. package/kb/systems/uber/DESIGN.md +636 -0
  1084. package/kb/systems/vercel/DESIGN.md +736 -0
  1085. package/kb/systems/vodafone/DESIGN.md +538 -0
  1086. package/kb/systems/voltagent/DESIGN.md +521 -0
  1087. package/kb/systems/warp/DESIGN.md +526 -0
  1088. package/kb/systems/webflow/DESIGN.md +588 -0
  1089. package/kb/systems/wired/DESIGN.md +497 -0
  1090. package/kb/systems/wise/DESIGN.md +544 -0
  1091. package/kb/systems/x.ai/DESIGN.md +465 -0
  1092. package/kb/systems/zapier/DESIGN.md +537 -0
  1093. package/kb/taste/garvit-by-job.md +46 -0
  1094. package/kb/taste/garvit-portfolio.md +94 -0
  1095. package/kb/universal/color-roles.md +26 -0
  1096. package/kb/universal/copy.md +16 -0
  1097. package/kb/universal/details.md +31 -0
  1098. package/kb/universal/first-screen.md +30 -0
  1099. package/kb/universal/fit-the-codebase.md +29 -0
  1100. package/kb/universal/layout-width.md +28 -0
  1101. package/kb/universal/motion-feedback.md +23 -0
  1102. package/kb/universal/signature.md +29 -0
  1103. package/kb/universal/states.md +17 -0
  1104. package/kb/universal/type-app.md +33 -0
  1105. package/kb/universal/type-marketing.md +28 -0
  1106. package/package.json +36 -4
  1107. package/scripts/pack-assets.mjs +48 -0
  1108. package/skill/palate/SKILL.md +261 -0
  1109. package/src/auth-recheck.mjs +4 -0
  1110. package/src/auth.mjs +416 -0
  1111. package/src/capture.mjs +182 -0
  1112. package/src/catalog/classify.mjs +96 -0
  1113. package/src/catalog/curated.mjs +128 -0
  1114. package/src/catalog/index-build.mjs +67 -0
  1115. package/src/catalog/literal.mjs +146 -0
  1116. package/src/catalog/store.mjs +40 -0
  1117. package/src/catalog/vibrant.mjs +126 -0
  1118. package/src/check.mjs +93 -0
  1119. package/src/concept.mjs +157 -0
  1120. package/src/context.mjs +226 -0
  1121. package/src/copy.mjs +188 -0
  1122. package/src/devserver.mjs +135 -0
  1123. package/src/fontgenre.mjs +44 -0
  1124. package/src/guard-agents.mjs +213 -0
  1125. package/src/guard.mjs +377 -0
  1126. package/src/kb.mjs +91 -0
  1127. package/src/look.mjs +312 -0
  1128. package/src/loop/ambition.mjs +620 -0
  1129. package/src/loop/animate.mjs +70 -0
  1130. package/src/loop/artdirection.mjs +264 -0
  1131. package/src/loop/bench.mjs +184 -0
  1132. package/src/loop/benchpick.mjs +155 -0
  1133. package/src/loop/blueprint.mjs +620 -0
  1134. package/src/loop/board.html +180 -0
  1135. package/src/loop/build.mjs +393 -0
  1136. package/src/loop/buttons.mjs +413 -0
  1137. package/src/loop/common.mjs +206 -0
  1138. package/src/loop/cost.mjs +63 -0
  1139. package/src/loop/craft.mjs +152 -0
  1140. package/src/loop/craftchecks.mjs +148 -0
  1141. package/src/loop/critique.mjs +393 -0
  1142. package/src/loop/details.mjs +225 -0
  1143. package/src/loop/direction.html +168 -0
  1144. package/src/loop/direction.mjs +256 -0
  1145. package/src/loop/directions.json +8 -0
  1146. package/src/loop/explore.mjs +219 -0
  1147. package/src/loop/fail.mjs +110 -0
  1148. package/src/loop/fix.mjs +194 -0
  1149. package/src/loop/floor.mjs +677 -0
  1150. package/src/loop/forget.mjs +52 -0
  1151. package/src/loop/gate.mjs +1053 -0
  1152. package/src/loop/grid.html +123 -0
  1153. package/src/loop/growth.mjs +118 -0
  1154. package/src/loop/icons.mjs +109 -0
  1155. package/src/loop/imagery.mjs +1280 -0
  1156. package/src/loop/judge-api.mjs +245 -0
  1157. package/src/loop/judge.mjs +167 -0
  1158. package/src/loop/layouts.mjs +492 -0
  1159. package/src/loop/library.mjs +82 -0
  1160. package/src/loop/lookplan.mjs +182 -0
  1161. package/src/loop/lookvec.mjs +645 -0
  1162. package/src/loop/match.mjs +923 -0
  1163. package/src/loop/matched.mjs +26 -0
  1164. package/src/loop/myrefs.mjs +151 -0
  1165. package/src/loop/pick.mjs +213 -0
  1166. package/src/loop/picker.html +118 -0
  1167. package/src/loop/plan.mjs +289 -0
  1168. package/src/loop/polish.mjs +39 -0
  1169. package/src/loop/research-learn.mjs +41 -0
  1170. package/src/loop/research.mjs +1130 -0
  1171. package/src/loop/rulereview.mjs +146 -0
  1172. package/src/loop/rules-board.html +199 -0
  1173. package/src/loop/rules.mjs +939 -0
  1174. package/src/loop/similar.mjs +276 -0
  1175. package/src/loop/states-source.mjs +258 -0
  1176. package/src/loop/states.mjs +1547 -0
  1177. package/src/loop/stats.mjs +78 -0
  1178. package/src/loop/taste.mjs +81 -0
  1179. package/src/loop/timeline.mjs +149 -0
  1180. package/src/loop/variants.mjs +228 -0
  1181. package/src/loop/vary.mjs +249 -0
  1182. package/src/mcp/server.mjs +1246 -0
  1183. package/src/motion.mjs +207 -0
  1184. package/src/moves.mjs +242 -0
  1185. package/src/novelty.mjs +116 -0
  1186. package/src/page-ambition.js +181 -0
  1187. package/src/page-craft.js +724 -0
  1188. package/src/page-details.js +415 -0
  1189. package/src/page-devices.js +365 -0
  1190. package/src/page-fingerprint.js +281 -0
  1191. package/src/page-gate.js +1307 -0
  1192. package/src/page-look.js +698 -0
  1193. package/src/page-metrics.js +78 -0
  1194. package/src/page-principles.js +1167 -0
  1195. package/src/page-rules.js +308 -0
  1196. package/src/page-sections.js +211 -0
  1197. package/src/page-states.js +703 -0
  1198. package/src/paths.mjs +13 -0
  1199. package/src/png.mjs +107 -0
  1200. package/src/point/auto.mjs +224 -0
  1201. package/src/point/locate.mjs +114 -0
  1202. package/src/point/overlay.js +1516 -0
  1203. package/src/point/server.mjs +1122 -0
  1204. package/src/point/trail.mjs +219 -0
  1205. package/src/pool.mjs +80 -0
  1206. package/src/principles.mjs +279 -0
  1207. package/src/refs.mjs +299 -0
  1208. package/src/robots.mjs +145 -0
  1209. package/src/sections.mjs +210 -0
  1210. package/src/shoot.mjs +235 -0
  1211. package/src/systems/apply.mjs +355 -0
  1212. package/src/systems/load.mjs +251 -0
  1213. package/src/systems/pick.mjs +213 -0
  1214. package/templates/vite-react-tw/index.html +12 -0
  1215. package/templates/vite-react-tw/package.json +25 -0
  1216. package/templates/vite-react-tw/src/App.tsx +3 -0
  1217. package/templates/vite-react-tw/src/index.css +1 -0
  1218. package/templates/vite-react-tw/src/main.tsx +10 -0
  1219. package/templates/vite-react-tw/tsconfig.app.json +26 -0
  1220. package/templates/vite-react-tw/tsconfig.json +7 -0
  1221. package/templates/vite-react-tw/tsconfig.node.json +23 -0
  1222. package/templates/vite-react-tw/vite.config.ts +8 -0
@@ -0,0 +1,219 @@
1
+ // Pins on states that need a click first (an open modal, a dropdown, a tab, a hover menu, an expanded row).
2
+ // The overlay records the user's steps since the last navigation (clicks, a few keys, typing, a hover) and,
3
+ // when they pin, keeps the few steps that lead to the pinned element's state: its "trail". Every crop is
4
+ // shot on a fresh page load, so the server replays the trail with real input before each one.
5
+ //
6
+ // trailCore() is shared with the overlay: the server pastes its source into overlay.js, so it must not use
7
+ // anything outside itself.
8
+ export function trailCore() {
9
+ // Fields whose values are never recorded: passwords, card numbers, one-time codes, tokens and the like.
10
+ const SECRET = /pass(word|code|phrase)?|pwd|cvc|cvv|csc|card|ccnum|\bcc-|ssn|social.?security|token|secret|\botp\b|one.?time|\bpin\b|iban|routing|account.?num/i;
11
+ const SECRET_AUTOCOMPLETE = /(^|\s)(cc-|one-time-code|current-password|new-password)/i;
12
+ const MAX = 10;
13
+ // How long after a step something that appeared counts as opened by it.
14
+ const CAUSE_MS = 2500;
15
+
16
+ const lastIndex = (a, f) => {
17
+ for (let i = a.length - 1; i >= 0; i--) if (f(a[i], i)) return i;
18
+ return -1;
19
+ };
20
+
21
+ // The minimal trail for a pin. steps: oldest first, each with
22
+ // rest: the page was at rest just before it (no dialog, menu, popover or expanded part open, beyond what
23
+ // was open when the user first did anything on this page),
24
+ // keep: a tab or expander clicked earlier whose state still holds over the pinned element.
25
+ // inOpen: the pinned element sits inside an open dialog, menu, popover, listbox or expanded part.
26
+ // revealedAt: times the element or one of its parents appeared (added to the page, or unhidden).
27
+ // The heuristic:
28
+ // 1. In an open layer: everything from the last step taken while the page was at rest. That step opened
29
+ // the first layer; the steps after it open the rest (a dropdown inside the modal, a tab in it).
30
+ // 2. Something it sits in appeared within 2.5 s after a step (a custom modal without roles, a panel
31
+ // rendered on click): everything from the earliest such step.
32
+ // 3. Tabs and expanders clicked before that, still selected or open over the element, are kept too.
33
+ // Nothing else: a page at rest with the element in plain view needs no steps. At most 10, the latest.
34
+ function trimTrail(steps, { inOpen = false, revealedAt = [], max = MAX } = {}) {
35
+ if (!Array.isArray(steps) || !steps.length) return [];
36
+ let from = steps.length;
37
+ if (inOpen) from = Math.max(0, lastIndex(steps, (s) => s.rest));
38
+ for (const t of revealedAt) {
39
+ const i = lastIndex(steps, (s) => s.at <= t);
40
+ if (i >= 0 && t - steps[i].at <= CAUSE_MS) from = Math.min(from, i);
41
+ }
42
+ return steps.filter((s, i) => i >= from || s.keep).slice(-max);
43
+ }
44
+
45
+ const quote = (t, n = 32) => {
46
+ const s = String(t ?? "").replace(/\s+/g, " ").trim();
47
+ return s ? `"${s.length > n ? `${s.slice(0, n - 1).trimEnd()}…` : s}"` : "";
48
+ };
49
+ const KEY = { " ": "Space", Escape: "Esc", ArrowDown: "↓", ArrowUp: "↑", ArrowLeft: "←", ArrowRight: "→" };
50
+ // One step in plain words: click "Invite members", press Enter on "Role", type "ada" in "Search".
51
+ function stepWords(s) {
52
+ const what = quote(s.text || s.name);
53
+ if (s.type === "click") return `click ${what || "it"}`;
54
+ if (s.type === "hover") return `hover ${what || "it"}`;
55
+ if (s.type === "select") return `choose ${quote(s.label || s.value) || "an option"}${what ? ` in ${what}` : ""}`;
56
+ if (s.type === "type") return s.redacted ? `type in ${what || "a private field"} (not recorded)` : `type ${quote(s.value, 24) || "nothing"}${what ? ` in ${what}` : ""}`;
57
+ if (s.type === "key") return `press ${(s.shift ? "Shift+" : "") + (KEY[s.key] ?? s.key)}${what && s.key !== "Escape" && s.key !== "Tab" ? ` on ${what}` : ""}`;
58
+ return s.type;
59
+ }
60
+ // Short, for the overlay: Invite members → Role → the menu.
61
+ function stepShort(s) {
62
+ const t = String(s.text || s.name || "").replace(/\s+/g, " ").trim();
63
+ const short = t.length > 24 ? `${t.slice(0, 23).trimEnd()}…` : t;
64
+ if (s.type === "key") return (s.shift ? "Shift+" : "") + (KEY[s.key] ?? s.key);
65
+ if (s.type === "type") return s.redacted ? `typed in ${short || "a private field"}` : `typed ${quote(s.value, 16)}`;
66
+ if (s.type === "hover") return `hover ${short}`;
67
+ if (s.type === "select") return `chose ${quote(s.label || s.value, 16)}`;
68
+ return short || s.type;
69
+ }
70
+ return { SECRET, SECRET_AUTOCOMPLETE, MAX, trimTrail, stepWords, stepShort };
71
+ }
72
+
73
+ export const { SECRET, MAX, trimTrail, stepWords, stepShort } = trailCore();
74
+
75
+ const TYPES = new Set(["click", "key", "type", "select", "hover"]);
76
+ const KEYS = new Set(["Enter", " ", "Escape", "Tab", "ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Home", "End"]);
77
+ const LAYERS = new Set(["modal", "dialog", "menu", "popover", "listbox", "tooltip", "tab panel", "expanded part", "hover menu"]);
78
+ const str = (v, n) => (typeof v === "string" && v.trim() ? v.replace(/\s+/g, " ").trim().slice(0, n) : undefined);
79
+
80
+ // A trail from the overlay, made safe to store: known step types only, short strings, at most 10 steps, and
81
+ // no value from anything that looks like a private field (checked again here, whatever the overlay said).
82
+ export function cleanTrail(raw) {
83
+ if (!Array.isArray(raw)) return [];
84
+ const out = [];
85
+ for (const s of raw.slice(-MAX)) {
86
+ if (!s || typeof s !== "object" || !TYPES.has(s.type)) continue;
87
+ const step = {
88
+ type: s.type,
89
+ selector: str(s.selector, 400),
90
+ testid: str(s.testid, 120),
91
+ role: str(s.role, 40),
92
+ name: str(s.name, 120),
93
+ tag: /^[a-z][a-z0-9-]{0,30}$/.test(s.tag ?? "") ? s.tag : undefined,
94
+ text: str(s.text, 120),
95
+ at: Number.isFinite(s.at) ? Math.round(s.at) : undefined,
96
+ };
97
+ if (s.type === "key") {
98
+ if (!KEYS.has(s.key)) continue;
99
+ step.key = s.key;
100
+ if (s.shift) step.shift = true;
101
+ }
102
+ if (s.type === "type" || s.type === "select") {
103
+ const hint = [s.selector, s.testid, s.name, s.text, s.field].filter(Boolean).join(" ");
104
+ if (s.redacted || SECRET.test(hint)) step.redacted = true;
105
+ else if (typeof s.value === "string") step.value = s.value.slice(0, 200);
106
+ if (s.type === "select" && !step.redacted) step.label = str(s.label, 120);
107
+ }
108
+ for (const k of Object.keys(step)) if (step[k] === undefined) delete step[k];
109
+ if (!step.selector && !step.testid && !step.name && !step.text && step.type !== "key") continue;
110
+ out.push(step);
111
+ }
112
+ return out;
113
+ }
114
+ export const cleanLayer = (v) => (LAYERS.has(v) ? v : null);
115
+
116
+ // The trail in the agent's words: click "Invite members" → the modal.
117
+ export function trailWords(trail, layer) {
118
+ if (!trail?.length) return "";
119
+ return [...trail.map(stepWords), ...(layer ? [`the ${layer}`] : [])].join(" → ");
120
+ }
121
+
122
+ // What the server does for each step, in order: where to find its target (most exact first) and what to do.
123
+ // loose (the other width): one more try by its words alone, since a menu may sit elsewhere on a phone.
124
+ export function replayPlan(trail, { loose = false } = {}) {
125
+ return (trail ?? []).map((s, i) => {
126
+ const find = [];
127
+ if (s.selector) find.push({ by: "css", css: s.selector });
128
+ if (s.testid) find.push({ by: "css", css: `[data-testid="${s.testid.replace(/["\\]/g, "\\$&")}"]` });
129
+ if (s.role && s.name) find.push({ by: "role", role: s.role, name: s.name });
130
+ const words = s.text || s.name;
131
+ if (s.tag && words) find.push({ by: "text", tag: s.tag, text: words, exact: true });
132
+ if (loose && words) find.push({ by: "text", text: words, exact: false });
133
+ const step = { n: i + 1, words: stepWords(s), find };
134
+ if (s.type === "click") return { ...step, do: "click" };
135
+ if (s.type === "hover") return { ...step, do: "hover" };
136
+ if (s.type === "select") return s.redacted ? { ...step, do: "skip", why: "a private field: palate never records what's chosen there" } : { ...step, do: "select", value: s.value, label: s.label };
137
+ if (s.type === "type") return s.redacted || typeof s.value !== "string" ? { ...step, do: "skip", why: "a private field: palate never records what's typed there" } : { ...step, do: "fill", value: s.value };
138
+ const key = `${s.shift ? "Shift+" : ""}${s.key === " " ? "Space" : s.key}`;
139
+ // Esc and Tab act on whatever has focus; the rest act on their control, focused first.
140
+ return { ...step, do: "press", key, find: s.key === "Escape" || s.key === "Tab" ? [] : find };
141
+ });
142
+ }
143
+
144
+ // Runs the plan in a Playwright page with real input: waits for each target (up to 3 s), acts, settles.
145
+ // Returns { state: "reopened" | "couldn't reopen", ran, steps, failed?, why?, skipped?, loose? }.
146
+ export async function replay(page, trail, { loose = false, wait = 3000, settle = 350 } = {}) {
147
+ const plan = replayPlan(trail, { loose });
148
+ const skipped = [];
149
+ let hamburger = false;
150
+ let used = null;
151
+ const visibleOf = async (f) => {
152
+ const l =
153
+ f.by === "css"
154
+ ? page.locator(f.css)
155
+ : f.by === "role"
156
+ ? page.getByRole(f.role, { name: f.name, exact: true })
157
+ : f.tag
158
+ ? page.locator(f.tag).filter({ hasText: new RegExp(`^\\s*${f.text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+")}\\s*$`, "i") })
159
+ : page.getByText(f.text, { exact: f.exact });
160
+ try {
161
+ const v = l.filter({ visible: true });
162
+ return (await v.count()) ? v.first() : null;
163
+ } catch {
164
+ return null;
165
+ }
166
+ };
167
+ const findTarget = async (step, ms) => {
168
+ const until = Date.now() + ms;
169
+ for (;;) {
170
+ for (const f of step.find) {
171
+ const l = await visibleOf(f);
172
+ if (l) return { l, f };
173
+ }
174
+ if (Date.now() > until) return null;
175
+ await page.waitForTimeout(150);
176
+ }
177
+ };
178
+ // On a phone the desktop menu often hides behind a menu button: press it once, then look again.
179
+ const openMenu = async () => {
180
+ if (hamburger) return false;
181
+ hamburger = true;
182
+ const b = page.locator('button[aria-label*="menu" i], [role="button"][aria-label*="menu" i], button[aria-label*="navigation" i], button[aria-controls][aria-expanded="false"]').filter({ visible: true }).first();
183
+ if (!(await b.count().catch(() => 0))) return false;
184
+ await b.click({ timeout: 2000 }).catch(() => {});
185
+ await page.waitForTimeout(settle);
186
+ return true;
187
+ };
188
+ for (const step of plan) {
189
+ if (step.do === "skip") {
190
+ skipped.push(`step ${step.n} (${step.words}): ${step.why}`);
191
+ continue;
192
+ }
193
+ let hit = null;
194
+ if (step.find.length) {
195
+ hit = await findTarget(step, wait);
196
+ if (!hit && loose && (await openMenu())) {
197
+ hit = await findTarget(step, 1000);
198
+ if (hit) used = "opened the menu button first";
199
+ }
200
+ if (!hit) return { state: "couldn't reopen", ran: false, steps: plan.length, failed: step.n, step: step.words, why: `step ${step.n} (${step.words}): nothing on the page matched it`, skipped };
201
+ if (loose && hit.f.by === "text" && !hit.f.tag) used ??= `found ${step.words.replace(/^\w+ /, "")} by its words`;
202
+ }
203
+ try {
204
+ const l = hit?.l;
205
+ if (step.do === "click") await l.click({ timeout: wait }).catch(() => l.click({ timeout: 1000, force: true }));
206
+ else if (step.do === "hover") await l.hover({ timeout: wait }).catch(() => l.hover({ timeout: 1000, force: true }));
207
+ else if (step.do === "fill") await l.fill(step.value, { timeout: wait });
208
+ else if (step.do === "select") await l.selectOption(step.value ?? { label: step.label }, { timeout: wait }).catch(() => l.selectOption({ label: step.label }, { timeout: 1000 }));
209
+ else if (step.do === "press") {
210
+ if (l) await l.focus({ timeout: wait });
211
+ await page.keyboard.press(step.key);
212
+ }
213
+ } catch (e) {
214
+ return { state: "couldn't reopen", ran: false, steps: plan.length, failed: step.n, step: step.words, why: `step ${step.n} (${step.words}) didn't work: ${String(e.message).split("\n")[0].slice(0, 160)}`, skipped };
215
+ }
216
+ await page.waitForTimeout(settle);
217
+ }
218
+ return { state: "reopened", ran: true, steps: plan.length, ...(skipped.length ? { skipped } : {}), ...(used ? { loose: used } : {}) };
219
+ }
package/src/pool.mjs ADDED
@@ -0,0 +1,80 @@
1
+ // One browser and one dev server per process, shared by every render in it. A CLI command (judge, pick,
2
+ // critique …) renders many pages; the MCP server runs a whole loop in one process. Launching Chrome and
3
+ // finding or starting the dev server each time cost seconds per step, so in keep-alive mode they stay up
4
+ // until closePool() (the CLI calls it when the command ends) or until they sit idle (the MCP server).
5
+ import { chromium } from "playwright-core";
6
+ import { startDevServer } from "./devserver.mjs";
7
+
8
+ let keep = false;
9
+ let idleMs = 0;
10
+ let browserP = null;
11
+ const servers = new Map();
12
+ const live = new Set();
13
+ let timer = null;
14
+
15
+ export async function launch() {
16
+ if (process.env.PALATE_BROWSER) return chromium.launch({ executablePath: process.env.PALATE_BROWSER });
17
+ for (const channel of ["chrome", "msedge", "chromium"]) {
18
+ try {
19
+ return await chromium.launch({ channel });
20
+ } catch {}
21
+ }
22
+ throw new Error("No Chrome or Edge found. Install one, or set PALATE_BROWSER to a Chromium executable.");
23
+ }
24
+
25
+ // Turns sharing on. idle: close everything after this long without use (0: only on closePool).
26
+ export function keepAlive({ idle = 0 } = {}) {
27
+ keep = true;
28
+ idleMs = idle;
29
+ }
30
+
31
+ function touch() {
32
+ if (!idleMs) return;
33
+ clearTimeout(timer);
34
+ timer = setTimeout(() => closePool().catch(() => {}), idleMs);
35
+ timer.unref?.();
36
+ }
37
+
38
+ // { browser, release }. Callers open and close their own pages or contexts; release() never closes a shared browser.
39
+ export async function acquireBrowser() {
40
+ if (!keep) {
41
+ const browser = await launch();
42
+ return { browser, release: () => browser.close().catch(() => {}) };
43
+ }
44
+ touch();
45
+ if (browserP) {
46
+ const b = await browserP.catch(() => null);
47
+ if (b?.isConnected()) return { browser: b, release: touch };
48
+ }
49
+ browserP = launch();
50
+ return { browser: await browserP, release: touch };
51
+ }
52
+
53
+ // { base, stop, name }, like startDevServer; shared per project folder in keep-alive mode.
54
+ export async function acquireServer(dir, { url } = {}) {
55
+ if (url || !keep) return startDevServer(dir, { url });
56
+ touch();
57
+ const key = dir.toLowerCase();
58
+ if (!servers.has(key)) servers.set(key, startDevServer(dir).catch((e) => (servers.delete(key), Promise.reject(e))));
59
+ const s = await servers.get(key);
60
+ live.add(s);
61
+ return { ...s, stop: touch };
62
+ }
63
+
64
+ export async function closePool() {
65
+ clearTimeout(timer);
66
+ const b = browserP;
67
+ browserP = null;
68
+ if (b) await (await b.catch(() => null))?.close().catch(() => {});
69
+ for (const [k, p] of servers) {
70
+ servers.delete(k);
71
+ const s = await p.catch(() => null);
72
+ live.delete(s);
73
+ s?.stop();
74
+ }
75
+ }
76
+
77
+ // A dev server palate started must not outlive the process (stop is synchronous).
78
+ process.on("exit", () => {
79
+ for (const s of live) s.stop();
80
+ });
@@ -0,0 +1,279 @@
1
+ // Design principles, measured: alignment, hierarchy, spacing and proximity, sizing and type, consistency
2
+ // and density. page-principles.js runs inside the page and returns raw measurements with offenders; this
3
+ // module turns them into short findings (what, where, the fix), each tagged with the kb/craft/principles.md
4
+ // item it comes from ([P01]…), so critique findings can cite it.
5
+ //
6
+ // import { measurePrinciples, principleFindings, principlesReport } from "./principles.mjs";
7
+ // const p = await measurePrinciples(page); // a Playwright page, already loaded
8
+ // report.push(...principlesReport(p)); // markdown lines
9
+ //
10
+ // Run alone for calibration: node src/principles.mjs <url> [--width 1440] [--json]
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
14
+
15
+ const here = path.dirname(fileURLToPath(import.meta.url));
16
+ const load = (f) => fs.readFileSync(path.join(here, f), "utf8").trim().replace(/;\s*$/, "");
17
+ const sectionsScript = load("page-sections.js");
18
+ const principlesScript = load("page-principles.js");
19
+
20
+ // Thresholds, tuned on palate-site, cal.com, plane.so, linear.app, vercel.com and stripe.com (Oct 2026).
21
+ export const LIMITS = {
22
+ nearMissMax: 6, // px: an edge this close to a shared line, but not on it, is a near miss
23
+ flatStep: 1.2, // headline / next text below it: under this (without a weight gap) the step is too flat
24
+ jumpStep: 4.5, // ... over this next to text of 16px or less is a jump
25
+ focalShare: 0.85, // elements within 15% of the top weight compete for attention
26
+ primaryLead: 1.12, // the primary button should outweigh the next one by this much
27
+ offScaleShare: 15, // % of spacing values off the 4px scale before it is worth fixing
28
+ measure: [35, 85], // characters per line for body text on desktop
29
+ bodyLeading: [1.35, 1.8],
30
+ displayLeading: [0.9, 1.3],
31
+ weights: 4,
32
+ sizes: 10,
33
+ headingWords: 12,
34
+ radii: 4,
35
+ shadows: 4,
36
+ buttonVariants: 8,
37
+ hole: 280, // px of nothing inside a section
38
+ };
39
+
40
+ /** Measures the page at its current width. Scrolls to the top first; returns the raw measurements. */
41
+ export async function measurePrinciples(page, { sections } = {}) {
42
+ await page.evaluate(() => window.scrollTo({ top: 0, behavior: "instant" }));
43
+ const secs = sections ?? (await page.evaluate(`(${sectionsScript})()`).catch(() => null));
44
+ return page.evaluate(`(${principlesScript})(${JSON.stringify(secs ?? null)})`);
45
+ }
46
+
47
+ const q = (s, n = 40) => `"${String(s ?? "").slice(0, n)}${String(s ?? "").length > n ? "…" : ""}"`;
48
+ const at = (o) => (o ? `${o.label} ${q(o.text)}` : "");
49
+ const where = (o) => (o ? { selector: o.selector, label: o.label, text: o.text, box: o.box } : null);
50
+ const secName = (p, i) => (i ? `section ${i} (${p.sections.find((s) => s.index === i)?.name.slice(0, 30) ?? "?"})` : "the page");
51
+
52
+ /** Turns raw measurements into findings: { id, area, severity, what, where[], fix }. */
53
+ export function principleFindings(p, limits = LIMITS) {
54
+ const L = { ...LIMITS, ...limits };
55
+ const out = [];
56
+ const add = (id, area, severity, what, offenders, fix) => out.push({ id, area, severity, what, where: (offenders ?? []).filter(Boolean).map(where), fix });
57
+ const desktop = p.viewport.width >= 1000;
58
+
59
+ // ---- alignment ----
60
+ const a = p.alignment;
61
+ const misses = a.nearMisses.filter((m) => Math.abs(m.offBy) <= L.nearMissMax);
62
+ const byLine = new Map();
63
+ for (const m of misses) {
64
+ const k = `${m.side}:${m.line}`;
65
+ byLine.set(k, [...(byLine.get(k) ?? []), m]);
66
+ }
67
+ for (const ms of [...byLine.values()].slice(0, 6)) {
68
+ const m = ms[0];
69
+ add("P01", "alignment", m.lineCount >= 4 ? "medium" : "low",
70
+ `${ms.length} item(s) sit ${[...new Set(ms.map((x) => `${Math.abs(x.offBy)}px`))].join("/")} ${m.offBy > 0 ? "right" : "left"} of the ${m.side} edge at x=${m.line} that ${m.lineCount} items share (${secName(p, m.section)}): ${ms.slice(0, 3).map((x) => at(x.offender)).join(", ")}. On the line: ${at(m.onLine)}.`,
71
+ ms.map((x) => x.offender),
72
+ `Put them on the shared edge: match the container padding or max-width of ${m.onLine.label} instead of nudging.`);
73
+ }
74
+ // [P02] The count of distinct edges is reported as a fact, not a finding: grid columns legitimately add edges.
75
+ for (const m of a.mixed) {
76
+ const pr = m.pairs[0];
77
+ add("P03", "alignment", "medium", `Centred and left-aligned text are stacked in one column in ${secName(p, m.section)}: centred ${at(pr.centred)} over left-aligned ${at(pr.left)}.`, [pr.centred, pr.left], "Pick one alignment for the stack. Left-align when the text runs past two lines; centre only short, single-purpose blocks.");
78
+ }
79
+ for (const r of a.ragged) {
80
+ for (const i of r.issues) {
81
+ const sev = i.kind === "first lines" || i.kind === "item tops" || i.kind === "pair edges" ? "medium" : "low";
82
+ const what = {
83
+ "item tops": `items in a row start ${i.spread}px apart`,
84
+ "first lines": `the first lines of ${r.items} items in a row start ${i.spread}px apart${i.why ? ` (${i.why})` : ""}`,
85
+ "second lines": `the text under the titles starts ${i.spread}px apart${i.why ? ` because ${i.why}` : ""}`,
86
+ "card heights": `cards in one row differ in height by ${i.spread}px`,
87
+ "pair edges": `two blocks side by side almost line up: their ${i.why} edges are ${i.spread}px apart`,
88
+ }[i.kind];
89
+ const fix = {
90
+ "item tops": "Align the row to the top (align-items: start or stretch), not centre.",
91
+ "first lines": "Give every item the same lead-in (same icon slot or none) so the titles share a baseline.",
92
+ "second lines": "Shorten the titles to one line, or give the title a fixed line count (min-height in lines) so the body text lines up.",
93
+ "card heights": "Stretch the cards to the row height (align-items: stretch, height: 100%).",
94
+ "pair edges": "Line them up exactly: stretch both to the row's height (align-items: stretch, height: 100%), or align both tops, bottoms or centres. A near miss reads as a mistake.",
95
+ }[i.kind];
96
+ add("P04", "alignment", sev, `Ragged row in ${secName(p, r.section)}: ${what}. Items: ${r.offenders.slice(0, 3).map(at).join(", ")}.`, r.offenders, fix);
97
+ }
98
+ }
99
+
100
+ // ---- hierarchy ----
101
+ const h = p.hierarchy;
102
+ if (!h.h1Count) add("P06", "hierarchy", "medium", "The page has no <h1>. Screen readers and search engines lose the page's title, and there is no declared top of the hierarchy.", [], "Mark the main headline as the single <h1>.");
103
+ else if (h.h1Count > 1) add("P06", "hierarchy", "low", `${h.h1Count} <h1> elements: ${h.h1.map(at).join(", ")}.`, h.h1, "Keep one <h1>; make the others <h2>.");
104
+ for (const s of h.scopes) {
105
+ const scope = s.section ? secName(p, s.section) : "the first screen";
106
+ if (s.heading && s.headingRank > 1 && s.outranking.length) {
107
+ const o = s.outranking[0];
108
+ const by = o.score / s.heading.score;
109
+ if (by >= 1.1) add("P06", "hierarchy", !s.section && by >= 1.2 ? "high" : by >= 1.2 ? "medium" : "low", `In ${scope}, ${o.kind} ${at(o)} outweighs the heading ${at(s.heading)} (${Math.round((by - 1) * 100)}% more visual weight from size, weight and contrast).`, [o, s.heading], `Make the heading the heaviest thing: raise its size or weight, or quiet ${o.label} (smaller, lighter, lower contrast).`);
110
+ }
111
+ // Peers that are headings of the same style are parallel columns (no section heading above them), not rivals.
112
+ const rivals = (s.peers ?? []).filter((x) => x.kind !== s.heading?.kind || Math.abs(x.score - s.heading.score) > 0.5);
113
+ if (s.heading && rivals.length && s.headingRank <= 1 + s.peers.length) add("P08", "hierarchy", "low", `In ${scope}, ${rivals.length} element(s) carry as much weight as the heading ${at(s.heading)}: ${rivals.slice(0, 3).map(at).join(", ")}.`, [s.heading, ...rivals], "Let the heading lead: set the others a step smaller or lighter, or in a quieter colour.");
114
+ else if (s.focalPoints >= 3 && s.competing.length >= 2 && s.top[0].score >= 20) add("P08", "hierarchy", "medium", `${s.focalPoints} things compete for first look in ${scope} (within 15% of the top weight): ${[s.top[0], ...s.competing].slice(0, 4).map(at).join(", ")}.`, [s.top[0], ...s.competing], "Choose one focal point and step the others down a size or a weight.");
115
+ const st = s.steps;
116
+ const realLead = s.heading || (s.top[0] && s.top[0].size >= 20);
117
+ if (st?.next && st.headlineToNext && realLead) {
118
+ const lead = s.heading ?? s.top[0];
119
+ // Hierarchy carried by colour (a dimmer line under the heading) counts: only flat when size, weight and contrast all match.
120
+ if (st.headlineToNext < L.flatStep && (st.weightGap ?? 0) < 200 && (st.scoreRatio ?? 1) < 1.25 && st.headline >= st.next.size) add("P07", "hierarchy", "medium", `Flat step in ${scope}: the heading ${at(lead)} is ${st.headline}px/${st.headlineWeight} and the text under it ${at(st.next)} is ${st.next.size}px/${st.next.weight} (×${st.headlineToNext}).`, [lead, st.next], `Separate them by at least ×1.25 in size (e.g. ${Math.round(st.next.size * 1.25)}px+), two weight steps, or a clearly quieter colour for the second line.`);
121
+ if (st.headlineToNext > L.jumpStep && st.next.size <= 16) add("P07", "hierarchy", "low", `Big jump in ${scope}: ${st.headline}px heading straight onto ${st.next.size}px text (×${st.headlineToNext}); the small text looks like a caption.`, [lead, st.next], `Add a step between them: a ${Math.round(st.next.size * 1.25)}–${Math.round(st.next.size * 1.5)}px lead paragraph, or bring the heading down.`);
122
+ }
123
+ for (const g of s.buttonGroups ?? []) {
124
+ if (g.choiceSet) continue;
125
+ const [b1, b2] = g.buttons;
126
+ if (!g.clearPrimary) add("P09", "hierarchy", "medium", `No clear primary action in ${scope}: ${at(b1)} and ${at(b2)} carry about the same weight (${b1.prominence} vs ${b2.prominence}${g.equalStyle ? ", same style" : ""}).`, [b1, b2], "Fill the primary with the accent and make the others outline or text buttons.");
127
+ else if (!g.primaryFirst && !s.section) add("P09", "hierarchy", "low", `The strongest button in ${scope}, ${at(b1)}, is not first in reading order.`, [b1], "Put the primary action first (left), the secondary after it.");
128
+ }
129
+ if (!s.section && s.controls?.length >= 2) {
130
+ const heroTop = s.buttonGroups?.find((g) => !g.choiceSet)?.buttons?.[0] ?? s.controls.find((c) => !c.inNav && c.filled);
131
+ const navTop = s.controls.find((c) => c.inNav);
132
+ if (heroTop && navTop && navTop.prominence > heroTop.prominence * 1.1) add("P09", "hierarchy", "low", `The header button ${at(navTop)} outweighs the hero's main action ${at(heroTop)}.`, [navTop, heroTop], "Make the hero's primary the strongest control on the first screen; keep the header button smaller or quieter.");
133
+ }
134
+ }
135
+
136
+ // ---- spacing ----
137
+ const sp = p.spacing;
138
+ if (sp.offScaleShare > L.offScaleShare) add("P12", "spacing", "medium", `${sp.offScaleShare}% of spacing values are off a 4px scale (fractional em/fluid values excluded): ${sp.offScale.slice(0, 5).map((v) => `${v.value}px×${v.count} (${v.examples[0]?.prop} on ${v.examples[0]?.label})`).join(", ")}.`, sp.offScale.flatMap((v) => v.examples.slice(0, 1)), `Use the scale${sp.unit ? ` (${sp.unit}px unit)` : ""}: round each to the nearest step (e.g. 18→16 or 20, 30→32).`);
139
+ else if (sp.offScale.length && sp.offScale[0].count >= 3) add("P12", "spacing", "low", `Spacing mostly sits on a ${sp.unit ?? 4}px scale, except ${sp.offScale.slice(0, 3).map((v) => `${v.value}px×${v.count}`).join(", ")}.`, sp.offScale.flatMap((v) => v.examples.slice(0, 1)), "Round these to the nearest step on the scale.");
140
+ for (const x of sp.proximity.slice(0, 6)) add("P13", "spacing", x.gapAbove < x.gapBelow * 0.9 && x.gapBelow - x.gapAbove >= 4 ? "medium" : "low", `${at(x.offender)} sits ${x.gapAbove}px below ${at(x.above)} but ${x.gapBelow}px above ${at(x.below)}, the content it introduces: it reads as part of the wrong group.`, [x.offender, x.above, x.below], `Give it more space above than below (e.g. ${Math.max(x.gapBelow * 2, 24)}px above, ${Math.min(x.gapAbove, Math.round(x.gapBelow / 2)) || 8}px below).`);
141
+ const pads = sp.sectionPadding.sections.filter((s) => !s.ownBg || true);
142
+ if (pads.length >= 3) {
143
+ const gaps = pads.slice(1).map((s) => s.gapFromPrevious).filter((g) => g > 0);
144
+ const med = median(gaps);
145
+ const odd = pads.slice(1).filter((s) => s.gapFromPrevious > 0 && med && (s.gapFromPrevious < med * 0.6 || s.gapFromPrevious > med * 1.7) && Math.abs(s.gapFromPrevious - med) > 32);
146
+ if (odd.length) add("P14", "spacing", "low", `Section rhythm: the space between sections is mostly ~${Math.round(med)}px, but ${odd.slice(0, 3).map((s) => `${s.gapFromPrevious}px before ${secName(p, s.section)}`).join(", ")}.`, [], "Use one or two section spacings (e.g. 96 and 128px) and keep each one consistent.");
147
+ }
148
+
149
+ // ---- type ----
150
+ const t = p.type;
151
+ if (desktop) {
152
+ const wide = t.measure.filter((m) => m.cpl > L.measure[1]);
153
+ const narrow = t.measure.filter((m) => m.cpl < L.measure[0]);
154
+ if (wide.length) add("P17", "type", "medium", `${wide.length} paragraph(s) run ${wide.map((m) => m.cpl).slice(0, 3).join("/")} characters per line (comfortable: 45–75): ${wide.slice(0, 2).map(at).join(", ")}.`, wide, "Cap the measure with max-width: 65ch on the paragraph.");
155
+ if (narrow.length) add("P17", "type", "low", `${narrow.length} paragraph(s) are only ${narrow.map((m) => m.cpl).slice(0, 3).join("/")} characters wide: ${narrow.slice(0, 2).map(at).join(", ")}.`, narrow, "Widen the column or reduce the size so lines carry 45+ characters.");
156
+ }
157
+ const tight = t.lineHeight.filter((l) => l.kind === "body" && l.ratio < L.bodyLeading[0]);
158
+ const loose = t.lineHeight.filter((l) => l.kind === "body" && l.ratio > L.bodyLeading[1]);
159
+ const dispLoose = t.lineHeight.filter((l) => l.kind === "display" && l.ratio > L.displayLeading[1]);
160
+ const dispTight = t.lineHeight.filter((l) => l.kind === "display" && l.ratio < L.displayLeading[0]);
161
+ const headLoose = t.lineHeight.filter((l) => l.kind === "heading" && (l.ratio > 1.6 || l.ratio < 1.0));
162
+ if (tight.length) add("P18", "type", "medium", `Body text set tight (line-height ${[...new Set(tight.map((l) => l.ratio))].join("/")}): ${tight.slice(0, 2).map(at).join(", ")}.`, tight, "Use line-height 1.5 for body text (1.4–1.6).");
163
+ if (loose.length) add("P18", "type", "low", `Body text set loose (line-height ${[...new Set(loose.map((l) => l.ratio))].join("/")}): ${loose.slice(0, 2).map(at).join(", ")}.`, loose, "Bring body line-height to 1.5–1.6.");
164
+ if (dispLoose.length) add("P18", "type", "medium", `Display text wraps with body-text leading (${[...new Set(dispLoose.map((l) => l.ratio))].join("/")}): ${dispLoose.slice(0, 2).map(at).join(", ")}. The lines drift apart.`, dispLoose, "Set headings at line-height 1.0–1.15.");
165
+ if (dispTight.length) add("P18", "type", "low", `Display text so tight lines may collide (${dispTight.map((l) => l.ratio).join("/")}): ${dispTight.slice(0, 2).map(at).join(", ")}.`, dispTight, "Use at least 0.95 so descenders clear the next line.");
166
+ if (headLoose.length) add("P18", "type", "low", `Headings with off leading (${[...new Set(headLoose.map((l) => l.ratio))].join("/")}): ${headLoose.slice(0, 2).map(at).join(", ")}.`, headLoose, "Set small headings at line-height 1.2–1.35.");
167
+ const usedWeights = t.weights.filter((w) => w.chars >= 20);
168
+ if (usedWeights.length > L.weights) add("P19", "type", "low", `${usedWeights.length} font weights in use (${usedWeights.map((w) => `${w.weight}×${w.blocks}`).join(", ")}).`, [], "Use two or three weights (e.g. 400, 500, 600) and build hierarchy with size and colour.");
169
+ if (t.looseDisplay.length) add("P20", "type", "medium", `Large text set loose (positive tracking on ${t.looseDisplay.map((x) => `${x.size}px`).join("/")}): ${t.looseDisplay.slice(0, 2).map(at).join(", ")}.`, t.looseDisplay, "Display sizes want zero or slightly negative tracking (-0.01 to -0.03em).");
170
+ if (t.capsNoTracking.length) add("P21", "type", "low", `${t.capsNoTracking.length} all-caps label(s) without letter-spacing: ${t.capsNoTracking.slice(0, 3).map(at).join(", ")}.`, t.capsNoTracking, "Track caps out by 0.05–0.1em (letter-spacing: 0.06em).");
171
+ if (t.sizes.length > L.sizes) add("P22", "type", "low", `${t.sizes.length} font sizes: ${t.sizes.map((s) => `${s.size}×${s.blocks}`).join(", ")}.`, [], "Reduce to a scale of 5–7 sizes and map every text style onto it.");
172
+ if (t.scale && t.scale.of >= 5 && t.scale.fit / t.scale.of < 0.6) add("P22", "type", "low", `No consistent type scale: the best ratio (×${t.scale.ratio} from ${t.bodySize}px) fits ${t.scale.fit} of ${t.scale.of} sizes; off-scale: ${t.scale.outside.join(", ")}px.`, [], `Pick a ratio (e.g. ×1.25 from ${t.bodySize}px) and snap sizes to it.`);
173
+ for (const c of t.closeSizes.slice(0, 3)) add("P26", "type", "low", `${c.sizes[0]}px and ${c.sizes[1]}px are both used and too close to tell apart: ${c.examples.map(at).join(" vs ")}.`, c.examples, `Merge them into one size, or separate them by at least ×1.15.`);
174
+ for (const x of t.longHeadings.filter((x, i, a) => a.findIndex((y) => y.text === x.text) === i).slice(0, 3)) add("P23", "type", "low", `Heading of ${x.words} words: ${at(x, 70)}.`, [x], "Cut the heading to under 10 words; move the rest into the paragraph below.");
175
+ if (t.widows.length) add("P24", "type", "low", `${t.widows.length} heading(s) end with one word alone on the last line: ${t.widows.slice(0, 3).map((w) => `${q(w.text, 40)} → "${w.word}"`).join(", ")}.`, t.widows, "Add text-wrap: balance to headings (or rewrite to fit).");
176
+
177
+ // ---- consistency ----
178
+ const c = p.consistency;
179
+ const realRadii = c.radii.filter((r) => r.radius !== "0" && r.radius !== "pill/circle" && r.count >= 1);
180
+ if (realRadii.length > L.radii) add("P27", "consistency", "low", `${realRadii.length} corner radii in use: ${realRadii.map((r) => `${r.radius}px×${r.count}`).join(", ")}.`, realRadii.slice(-3).map((r) => r.example), "Use a radius scale of 2–3 values (e.g. 6, 12, pill) and nest them concentrically.");
181
+ const nestGroups = new Map();
182
+ for (const n of c.nestedRadii) nestGroups.set(`${n.inner}/${n.outer}/${n.inset}`, [...(nestGroups.get(`${n.inner}/${n.outer}/${n.inset}`) ?? []), n]);
183
+ for (const ns of [...nestGroups.values()].slice(0, 3)) {
184
+ const n = ns[0];
185
+ add("P27", "consistency", "medium", `Nested corners are not concentric${ns.length > 1 ? ` (${ns.length} times)` : ""}: ${n.inner_.label} has radius ${n.inner}px inside ${n.outer_.label} (${n.outer}px) with ${n.inset}px between them; the inner corner should be ~${n.ideal}px.`, ns.flatMap((x) => [x.inner_, x.outer_]).slice(0, 6), n.ideal > 0 ? `Set the inner radius to outer − padding (${n.outer} − ${n.inset} = ${n.ideal}px).` : `The padding is larger than the outer radius, so the inner corner should be nearly square (0–4px), or raise the outer radius to ~${n.inner + n.inset}px.`);
186
+ }
187
+ if (c.borders.length > 4) add("P28", "consistency", "low", `${c.borders.length} different box borders: ${c.borders.slice(0, 6).map((b) => `${b.border}×${b.count}`).join(", ")}.`, c.borders.slice(0, 3).map((b) => b.example), "Use one border colour token (and maybe one stronger one for inputs).");
188
+ if (c.shadows.length > L.shadows) add("P29", "consistency", "low", `${c.shadows.length} different shadows.`, c.shadows.slice(0, 3).map((s) => s.example), "Define 2–3 elevation levels and use only those.");
189
+ const col = c.colors;
190
+ if (col.nearDuplicates.length) add("P30", "consistency", "low", `Near-duplicate colours (≤5 apart per channel): ${col.nearDuplicates.slice(0, 4).map((d) => d.join("≈")).join(", ")}.`, [], "Collapse each pair into one token.");
191
+ const hues = col.hueFamilies.filter((x) => x.share >= 0.5);
192
+ if (hues.length >= 3) add("P31", "consistency", "low", `${hues.length} colour families each cover 0.5%+ of the page: ${hues.map((x) => `${x.colors[0]} (${x.share}%)`).join(", ")}.`, [], "Keep one accent hue for actions and emphasis; turn the others neutral or into a single supporting tint.");
193
+ if (col.accent && col.accent.share > 30) add("P31", "consistency", "low", `The accent (${col.accent.colors[0]}) covers ${col.accent.share}% of the page; it stops signalling what to click.`, [], "Reserve the accent for actions and key highlights (~10% of the surface).");
194
+ const v = c.variants;
195
+ if (v.buttons.length > L.buttonVariants) add("P32", "consistency", "low", `${v.buttons.length} button styles: ${v.buttons.slice(0, 6).map((b) => `[${b.style}]×${b.count}`).join(", ")}.`, v.buttons.slice(0, 4).map((b) => b.example), "Reduce to primary, secondary and ghost at one or two sizes.");
196
+ for (const [kind, list] of Object.entries(v.near)) for (const n of list.slice(0, 2)) add("P32", "consistency", "medium", `Two ${kind} that are almost the same but differ in ${n.differs}: ${n.examples.map(at).join(" and ")}.`, n.examples, `Make them one component with one value for ${n.differs.split(" vs ")[0].replace(/[\d.]+.*$/, "") || "that property"}.`);
197
+ if (v.cards.length > 3) add("P32", "consistency", "low", `${v.cards.length} card styles: ${v.cards.slice(0, 5).map((x) => `[${x.style}]×${x.count}`).join(", ")}.`, v.cards.slice(0, 3).map((x) => x.example), "Use one card surface (fill, border, radius, padding) and vary content, not chrome.");
198
+ if (v.inputs.length > 2) add("P32", "consistency", "medium", `${v.inputs.length} input styles: ${v.inputs.map((x) => `[${x.style}]×${x.count}`).join(", ")}.`, v.inputs.slice(0, 3).map((x) => x.example), "Inputs should share one height, radius, border and fill.");
199
+
200
+ // ---- density ----
201
+ const content = p.density.filter((d) => !d.chrome && d.height >= 200);
202
+ const medInk = median(content.map((d) => d.ink));
203
+ for (let i = 0; i < content.length; i++) {
204
+ const d = content[i];
205
+ if (d.items === 0 && d.height > p.viewport.height * 0.5) add("P38", "density", "low", `${secName(p, d.section)} has no measurable content in its ${d.height}px (nothing palate can see: it may be drawn in WebGL or a CSS background, revealed by scrolling, or failed to render).`, [], "Check the screenshot: if it is empty there too, remove the section or fix what failed to load.");
206
+ else if (medInk >= 6 && d.ink < medInk * 0.35 && d.ink < 8 && d.height > p.viewport.height * 0.6) add("P37", "density", "low", `${secName(p, d.section)} is much emptier than the rest (${d.ink}% ink vs ~${medInk}% typical, ${d.height}px tall).`, [], "Shorten it (less padding, fewer empty rows) or give it content that earns the space.");
207
+ const busy = (x) => x.textInk + (x.ctlInk ?? 0);
208
+ const medBusy = median(content.map(busy));
209
+ if (medBusy >= 2 && busy(d) > Math.max(medBusy * 3, 45)) add("P37", "density", "low", `${secName(p, d.section)} is much denser than its neighbours (${busy(d)}% of it is text and controls vs ~${medBusy}% typical).`, [], "Give it room: more padding, fewer items per row, or split it.");
210
+ const pinned = d.height > p.viewport.height * 2.5;
211
+ if (d.emptiestRun > Math.max(L.hole, p.viewport.height * 0.35)) add("P38", "density", pinned ? "low" : "medium", `${secName(p, d.section)} has ${d.emptiestRun}px of nothing at y=${d.emptiestAt}${pinned ? ` (the section is ${d.height}px tall: probably a scroll-pinned sequence)` : ""}.`, [], "Close the gap, or check that something there did not fail to render (a lazy image, an animation that never ran).");
212
+ }
213
+
214
+ const rank = { high: 0, medium: 1, low: 2 };
215
+ return out.sort((a, b) => rank[a.severity] - rank[b.severity]);
216
+ }
217
+
218
+ function median(a) {
219
+ if (!a.length) return 0;
220
+ const s = [...a].sort((x, y) => x - y);
221
+ const m = s.length >> 1;
222
+ return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
223
+ }
224
+
225
+ /** A short summary of the measurements (facts, not judgements), as markdown lines. */
226
+ export function principleFacts(p) {
227
+ const t = p.type;
228
+ const c = p.consistency;
229
+ const sp = p.spacing;
230
+ const fs = p.hierarchy.scopes.find((s) => !s.section);
231
+ const lines = [];
232
+ if (fs) lines.push(`- Hierarchy (first screen): ${fs.top.slice(0, 3).map((x) => `${x.kind} ${x.size}px/${x.weight} ${q(x.text, 30)} (${x.score})`).join(" > ")}${fs.steps ? `; steps ${fs.steps.headline}:${fs.steps.subhead ?? "–"}:${fs.steps.body}px` : ""}.`);
233
+ lines.push(`- Alignment: page edges ${p.alignment.pageLines.map((l) => `x=${l.x} (${l.sections} sections)`).join(", ") || "none shared by 3+ sections"}; ${p.alignment.nearMisses.length} near miss(es).`);
234
+ lines.push(`- Spacing: ${sp.unit ? `${sp.unit}px unit` : "no clear unit"} (${sp.on8}% on 8, ${sp.on4}% on 4); ${sp.offScaleShare}% off-scale; most used ${sp.common.slice(0, 8).join(", ")}${sp.fluid.length ? `; fluid ${sp.fluid.slice(0, 4).join(", ")}` : ""}.`);
235
+ lines.push(`- Type: ${t.sizes.length} sizes (${t.sizes.map((s) => s.size).join(", ")}), body ${t.bodySize}px${t.scale ? `, best scale ×${t.scale.ratio} fits ${t.scale.fit}/${t.scale.of}` : ""}; weights ${t.weights.map((w) => w.weight).join("/")}; ${t.families.map((f) => f.family).join(", ")}.`);
236
+ lines.push(`- Surfaces: radii ${c.radii.map((r) => `${r.radius}×${r.count}`).join(", ")}; ${c.borders.length} border style(s); ${c.shadows.length} shadow(s); ${c.variants.buttons.length} button, ${c.variants.cards.length} card, ${c.variants.inputs.length} input style(s).`);
237
+ lines.push(`- Colour: ${c.colors.distinct} colours, ${c.colors.text} text colours${c.colors.accent ? `; accent ${c.colors.accent.colors[0]} covers ${c.colors.accent.share < 0.1 ? "<0.1" : c.colors.accent.share}%` : "; no accent hue"}.`);
238
+ lines.push(`- Density (ink % per section): ${p.density.map((d) => `${d.section}:${d.ink}%`).join(" ")}.`);
239
+ return lines;
240
+ }
241
+
242
+ /** Facts plus findings, as markdown lines for a report (shoot, critique). */
243
+ export function principlesReport(p, { max = 14 } = {}) {
244
+ const f = principleFindings(p);
245
+ const lines = [`### Principles (${p.viewport.width}px)`, ...principleFacts(p)];
246
+ if (!f.length) lines.push("- No principle checks fired.");
247
+ for (const x of f.slice(0, max)) lines.push(`- ${x.severity === "high" ? "⚠⚠" : x.severity === "medium" ? "⚠" : "·"} [${x.id}] ${x.what} Fix: ${x.fix}${x.where[0]?.selector ? ` (\`${x.where[0].selector}\`)` : ""}`);
248
+ if (f.length > max) lines.push(`- …and ${f.length - max} more (low).`);
249
+ return lines;
250
+ }
251
+
252
+ // CLI for calibration: node src/principles.mjs <url> [--width 1440] [--json] [--raw]
253
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
254
+ const args = process.argv.slice(2);
255
+ const url = args.find((a) => /^(https?|file):/.test(a));
256
+ const width = Number(args[args.indexOf("--width") + 1]) || 1440;
257
+ if (!url) {
258
+ console.error("usage: node src/principles.mjs <url> [--width 1440] [--json] [--raw]");
259
+ process.exit(1);
260
+ }
261
+ const { launch } = await import("./shoot.mjs");
262
+ const { scrollThrough } = await import("./look.mjs");
263
+ const browser = await launch();
264
+ try {
265
+ const ctx = await browser.newContext({ viewport: { width, height: width < 600 ? 844 : 900 } });
266
+ const page = await ctx.newPage();
267
+ await page.goto(url, { waitUntil: "networkidle", timeout: 60_000 }).catch(() => {});
268
+ await page.waitForTimeout(800);
269
+ await scrollThrough(page);
270
+ const t0 = Date.now();
271
+ const p = await measurePrinciples(page);
272
+ const ms = Date.now() - t0;
273
+ if (args.includes("--raw")) console.log(JSON.stringify(p, null, 1));
274
+ else if (args.includes("--json")) console.log(JSON.stringify(principleFindings(p), null, 1));
275
+ else console.log([`# ${url} @${width} (${ms}ms)`, ...principlesReport(p, { max: 60 })].join("\n"));
276
+ } finally {
277
+ await browser.close();
278
+ }
279
+ }